/*
 * Copyright (C) 2026 Fluxer Contributors
 *
 * This file is part of Fluxer.
 *
 * Fluxer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fluxer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
 */

import styles from '@app/components/modals/MobileVideoViewer.module.css';
import AccessibilityStore from '@app/stores/AccessibilityStore';
import VideoVolumeStore from '@app/stores/VideoVolumeStore';
import {useLingui} from '@lingui/react/macro';
import {DotsThreeIcon, PauseIcon, PlayIcon, SpeakerHighIcon, SpeakerXIcon, XIcon} from '@phosphor-icons/react';
import {AnimatePresence, motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useCallback, useEffect, useRef, useState} from 'react';
import {type ReactZoomPanPinchRef, TransformComponent, TransformWrapper} from 'react-zoom-pan-pinch';

interface MobileVideoViewerProps {
	src: string;
	initialTime?: number;
	loop?: boolean;
	onClose: () => void;
	onMenuOpen?: () => void;
}

function formatTime(time: number): string {
	if (!Number.isFinite(time)) return '0:00';
	const minutes = Math.floor(time / 60);
	const seconds = Math.floor(time % 60);
	return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}

export const MobileVideoViewer = observer(function MobileVideoViewer({
	src,
	initialTime,
	loop = true,
	onClose,
	onMenuOpen,
}: MobileVideoViewerProps) {
	const {t} = useLingui();
	const videoRef = useRef<HTMLVideoElement>(null);
	const transformRef = useRef<ReactZoomPanPinchRef>(null);
	const [isPlaying, setIsPlaying] = useState(false);
	const [zoomScale, setZoomScale] = useState(1);
	const [currentTime, setCurrentTime] = useState(0);
	const [duration, setDuration] = useState(0);
	const [hudVisible, setHudVisible] = useState(true);
	const hudTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
	const [hasInitialized, setHasInitialized] = useState(false);

	const progress = duration > 0 ? currentTime / duration : 0;

	const scheduleHudHide = useCallback(() => {
		if (hudTimerRef.current) {
			clearTimeout(hudTimerRef.current);
		}
		hudTimerRef.current = setTimeout(() => {
			if (isPlaying) {
				setHudVisible(false);
			}
			hudTimerRef.current = null;
		}, 3000);
	}, [isPlaying]);

	const handleTapSurface = useCallback(() => {
		setHudVisible((prev) => !prev);
		if (!hudVisible) {
			scheduleHudHide();
		}
	}, [hudVisible, scheduleHudHide]);

	const handleZoomChange = useCallback((_ref: ReactZoomPanPinchRef, state: {scale: number}) => {
		setZoomScale(state.scale);
	}, []);

	const handlePlayPause = useCallback((e: React.MouseEvent) => {
		e.stopPropagation();
		const video = videoRef.current;
		if (!video) return;

		if (video.paused) {
			video.play();
			transformRef.current?.resetTransform();
		} else {
			video.pause();
		}
	}, []);

	const handleToggleMute = useCallback((e: React.MouseEvent) => {
		e.stopPropagation();
		VideoVolumeStore.toggleMute();
	}, []);

	const handleProgressClick = useCallback(
		(e: React.MouseEvent<HTMLDivElement>) => {
			e.stopPropagation();
			const video = videoRef.current;
			if (!video || !duration) return;
			const rect = e.currentTarget.getBoundingClientRect();
			const x = e.clientX - rect.left;
			const percentage = Math.max(0, Math.min(1, x / rect.width));
			video.currentTime = percentage * duration;
		},
		[duration],
	);

	useEffect(() => {
		const video = videoRef.current;
		if (!video) return;

		const handlePlay = () => {
			setIsPlaying(true);
			scheduleHudHide();
		};
		const handlePause = () => {
			setIsPlaying(false);
			setHudVisible(true);
			if (hudTimerRef.current) {
				clearTimeout(hudTimerRef.current);
				hudTimerRef.current = null;
			}
		};
		const handleTimeUpdate = () => setCurrentTime(video.currentTime);
		const handleLoadedMetadata = () => {
			setDuration(video.duration);
			if (initialTime && !hasInitialized) {
				video.currentTime = initialTime;
				setHasInitialized(true);
				video.play();
			}
		};
		const handleDurationChange = () => setDuration(video.duration);

		video.addEventListener('play', handlePlay);
		video.addEventListener('pause', handlePause);
		video.addEventListener('timeupdate', handleTimeUpdate);
		video.addEventListener('loadedmetadata', handleLoadedMetadata);
		video.addEventListener('durationchange', handleDurationChange);

		return () => {
			video.removeEventListener('play', handlePlay);
			video.removeEventListener('pause', handlePause);
			video.removeEventListener('timeupdate', handleTimeUpdate);
			video.removeEventListener('loadedmetadata', handleLoadedMetadata);
			video.removeEventListener('durationchange', handleDurationChange);
		};
	}, [initialTime, hasInitialized, scheduleHudHide]);

	useEffect(() => {
		const video = videoRef.current;
		if (!video) return;
		video.muted = VideoVolumeStore.isMuted;
	}, []);

	useEffect(() => {
		return () => {
			if (hudTimerRef.current) {
				clearTimeout(hudTimerRef.current);
			}
		};
	}, []);

	const handleClose = useCallback(
		(e: React.MouseEvent) => {
			e.stopPropagation();
			onClose();
		},
		[onClose],
	);

	const handleMenuOpen = useCallback(
		(e: React.MouseEvent) => {
			e.stopPropagation();
			onMenuOpen?.();
		},
		[onMenuOpen],
	);

	return (
		<div className={styles.container}>
			<TransformWrapper
				ref={transformRef}
				initialScale={1}
				minScale={1}
				maxScale={5}
				pinch={{step: 5}}
				doubleClick={{disabled: true}}
				panning={{disabled: isPlaying}}
				disabled={isPlaying}
				onTransformed={handleZoomChange}
				centerOnInit
				centerZoomedOut
			>
				<TransformComponent
					wrapperStyle={{width: '100%', height: '100%'}}
					contentStyle={{
						width: '100%',
						height: '100%',
						display: 'flex',
						alignItems: 'center',
						justifyContent: 'center',
					}}
				>
					<video
						ref={videoRef}
						className={styles.video}
						src={src}
						autoPlay={!initialTime}
						playsInline
						loop={loop}
						muted={VideoVolumeStore.isMuted}
					/>
				</TransformComponent>
			</TransformWrapper>

			<div
				className={styles.tapSurface}
				onClick={handleTapSurface}
				onKeyDown={(e) => {
					if (e.key === 'Enter' || e.key === ' ') {
						handleTapSurface();
					}
				}}
				role="button"
				tabIndex={0}
				aria-label={t`Toggle controls`}
			/>

			<AnimatePresence>
				{hudVisible && zoomScale <= 1 && (
					<motion.div
						className={styles.hudOverlay}
						initial={{opacity: 0}}
						animate={{opacity: 1}}
						exit={{opacity: 0}}
						transition={{duration: AccessibilityStore.useReducedMotion ? 0 : 0.15}}
					>
						<div className={styles.topBar}>
							<button type="button" className={styles.topBarButton} onClick={handleClose} aria-label={t`Close`}>
								<XIcon size={20} weight="bold" />
							</button>
							{onMenuOpen && (
								<button
									type="button"
									className={styles.topBarButton}
									onClick={handleMenuOpen}
									aria-label={t`More options`}
								>
									<DotsThreeIcon size={20} weight="bold" />
								</button>
							)}
						</div>

						<div className={styles.bottomArea}>
							<button
								type="button"
								className={styles.muteButton}
								onClick={handleToggleMute}
								aria-label={VideoVolumeStore.isMuted ? t`Unmute` : t`Mute`}
							>
								{VideoVolumeStore.isMuted ? (
									<SpeakerXIcon size={18} weight="fill" />
								) : (
									<SpeakerHighIcon size={18} weight="fill" />
								)}
							</button>

							<div className={styles.controlsBar}>
								<button
									type="button"
									className={styles.playPauseButton}
									onClick={handlePlayPause}
									aria-label={isPlaying ? t`Pause` : t`Play`}
								>
									{isPlaying ? <PauseIcon size={20} weight="fill" /> : <PlayIcon size={20} weight="fill" />}
								</button>

								<div
									className={styles.progressBarWrapper}
									onClick={handleProgressClick}
									onKeyDown={(e) => {
										if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
											e.preventDefault();
											const video = videoRef.current;
											if (!video) return;
											const step = duration * 0.05;
											video.currentTime = Math.max(
												0,
												Math.min(duration, video.currentTime + (e.key === 'ArrowRight' ? step : -step)),
											);
										}
									}}
									role="slider"
									tabIndex={0}
									aria-valuenow={Math.round(progress * 100)}
									aria-valuemin={0}
									aria-valuemax={100}
									aria-label={t`Video progress`}
								>
									<div className={styles.progressTrack}>
										<div className={styles.progressFill} style={{width: `${progress * 100}%`}} />
									</div>
								</div>

								<span className={styles.timeDisplay}>
									{formatTime(currentTime)} / {formatTime(duration)}
								</span>
							</div>
						</div>
					</motion.div>
				)}
			</AnimatePresence>
		</div>
	);
});
