"use client"

import type { ITimelinePanelProps } from "./TimelinePanel.d"
import type { JSX } from "react"

import { useMediaRemote, useMediaState } from "@vidstack/react"
import cs from "classnames"
import { Film, Loader2, Minus, Plus } from "lucide-react"
import {
	FunctionComponent,
	useCallback,
	useEffect,
	useRef,
	useState,
} from "react"

import api from "@shared/api"
import { useWorkspaceFiles } from "@shared/context/WorkspaceContext"
import { useAppSelector } from "@shared/hooks/useAppSelector"
import { useTimelineTracks } from "@shared/hooks/useTimelineTracks"

import styles from "./TimelinePanel.module.scss"

const MIN_PPS = 20
const MAX_PPS = 500
const DEFAULT_PPS = 100
const ZOOM_STEP = 1.25
const AUTO_SCROLL_GUTTER = 40
const AUTO_SCROLL_OFFSET_RATIO = 0.3

const RULER_HEIGHT = 24
const VIDEO_FRAMES_HEIGHT = 60
const WAVEFORM_HEIGHT = 80
const TRACK_HEIGHT = 40
const VIRTUAL_CANVAS_OVERSCAN_PX = 600

function getVirtualCanvasWindow(
	totalWidth: number,
	scrollLeft: number,
	viewportWidth: number,
): {
	startX: number
	endX: number
	width: number
} {
	const safeViewportWidth = Math.max(1, viewportWidth)
	const overscan = Math.max(VIRTUAL_CANVAS_OVERSCAN_PX, safeViewportWidth * 0.5)
	const startX = Math.max(0, Math.floor(scrollLeft - overscan))
	const endX = Math.min(
		totalWidth,
		Math.ceil(scrollLeft + safeViewportWidth + overscan),
	)

	return {
		startX,
		endX,
		width: Math.max(1, endX - startX),
	}
}

function isMacPlatform(): boolean {
	if (typeof navigator === "undefined") return false
	const nav = navigator as Navigator & {
		userAgentData?: { platform?: string }
	}
	const platform = nav.userAgentData?.platform || navigator.platform || ""
	return /mac|iphone|ipad|ipod/i.test(platform)
}

function formatTime(seconds: number): string {
	const m = Math.floor(seconds / 60)
	const s = Math.floor(seconds % 60)
	return `${m}:${s.toString().padStart(2, "0")}`
}

export const TimelinePanel: FunctionComponent<
	ITimelinePanelProps
> = ({ className, projectId, audioUrl, playerRef }): JSX.Element => {
	const { selectedFile, setSelectedFile, usedFiles } = useWorkspaceFiles()

	const currentTime = useMediaState("currentTime", playerRef)
	const duration = useMediaState("duration", playerRef)
	const isPlaying = useMediaState("playing", playerRef)
	const remote = useMediaRemote(playerRef)

	const tracks = useTimelineTracks(usedFiles, selectedFile)
	const transcriptionArtifactId =
		usedFiles.find((f) => f.artifactType === "TRANSCRIPTION_JSON")?.id ?? null
	const [pixelsPerSecond, setPixelsPerSecond] = useState(DEFAULT_PPS)
	const scrollRef = useRef<HTMLDivElement>(null)
	const rulerCanvasRef = useRef<HTMLCanvasElement>(null)

	// Viewport tracking for child tracks
	const [scrollLeft, setScrollLeft] = useState(0)
	const [viewportWidth, setViewportWidth] = useState(2000)
	const scrollRafRef = useRef(0)

	const handleSegmentClick = useCallback(
		(segmentIndex: number, artifactId: string) => {
			setSelectedFile({
				id: artifactId,
				path: "transcription",
				source: "artifact",
				artifactType: "TRANSCRIPTION_JSON",
				scrollToSegmentIndex: segmentIndex,
			})
		},
		[setSelectedFile],
	)

	// Frame extraction
	const frameExtractJob = useAppSelector((state) =>
		state.notifications.items.find(
			(n) =>
				n.job_type === "FRAME_EXTRACT" &&
				(n.status === "RUNNING" || n.status === "PENDING"),
		),
	)

	const frameExtractMutation = api.useMutation(
		"post",
		"/api/tasks/frame-extract/",
	)
	const isExtractingFrames =
		!!frameExtractJob || frameExtractMutation.isPending

	const handleExtractFrames = useCallback(() => {
		if (!selectedFile || isExtractingFrames) return
		frameExtractMutation.mutate({
			body: {
				file_key: selectedFile.path,
				project_id: projectId || null,
				regenerate: false,
			},
		})
	}, [selectedFile, isExtractingFrames, projectId, frameExtractMutation])

	const totalWidth = Math.max(duration * pixelsPerSecond, 200)
	const rulerVirtualWindow = getVirtualCanvasWindow(
		totalWidth,
		scrollLeft,
		viewportWidth,
	)

	const seekTo = useCallback(
		(time: number) => {
			remote.seek(time)
		},
		[remote],
	)

	const zoomIn = useCallback(() => {
		setPixelsPerSecond((prev) => Math.min(prev * ZOOM_STEP, MAX_PPS))
	}, [])

	const zoomOut = useCallback(() => {
		setPixelsPerSecond((prev) => Math.max(prev / ZOOM_STEP, MIN_PPS))
	}, [])

	const handleTracksScroll = useCallback(
		(e: React.UIEvent<HTMLDivElement>) => {
			const container = e.currentTarget
			if (scrollRafRef.current) return
			scrollRafRef.current = requestAnimationFrame(() => {
				scrollRafRef.current = 0
				setScrollLeft(container.scrollLeft)
				setViewportWidth(container.clientWidth)
			})
		},
		[],
	)

	const handleWheel = useCallback(
		(e: React.WheelEvent) => {
			const shouldZoom = isMacPlatform() ? e.metaKey : e.ctrlKey
			if (!shouldZoom) return

			e.preventDefault()
			if (e.deltaY < 0) zoomIn()
			else zoomOut()
		},
		[zoomIn, zoomOut],
	)

	const handleRulerClick = useCallback(
		(e: React.MouseEvent<HTMLCanvasElement>) => {
			const rect = e.currentTarget.getBoundingClientRect()
			const x = e.clientX - rect.left
			const time = (rulerVirtualWindow.startX + x) / pixelsPerSecond
			if (time >= 0 && time <= duration) seekTo(time)
		},
		[duration, pixelsPerSecond, rulerVirtualWindow.startX, seekTo],
	)

	// Sync scroll metrics when a file is selected / timeline remounts.
	useEffect(() => {
		if (!selectedFile) {
			setScrollLeft(0)
			setViewportWidth(2000)
			return
		}

		const container = scrollRef.current
		if (!container) return

		setScrollLeft(container.scrollLeft)
		setViewportWidth(container.clientWidth)
		return undefined
	}, [selectedFile])

	useEffect(() => {
		return () => {
			if (scrollRafRef.current) cancelAnimationFrame(scrollRafRef.current)
		}
	}, [])

	// Draw ruler
	useEffect(() => {
		const canvas = rulerCanvasRef.current
		if (!canvas || duration <= 0) return

		const ctx = canvas.getContext("2d")
		if (!ctx) return

		const dpr = window.devicePixelRatio || 1
		canvas.width = rulerVirtualWindow.width * dpr
		canvas.height = RULER_HEIGHT * dpr
		canvas.style.width = `${rulerVirtualWindow.width}px`
		canvas.style.height = `${RULER_HEIGHT}px`
		canvas.style.left = `${rulerVirtualWindow.startX}px`
		ctx.setTransform(dpr, 0, 0, dpr, 0, 0)

		ctx.clearRect(0, 0, rulerVirtualWindow.width, RULER_HEIGHT)

		// Resolve CSS variables for canvas (canvas API doesn't support var())
		const rootStyles = getComputedStyle(document.documentElement)
		const textTertiary =
			rootStyles.getPropertyValue("--text-tertiary").trim() || "#94a3b8"
		const textSecondary =
			rootStyles.getPropertyValue("--text-secondary").trim() || "#64748b"
		const borderSubtle =
			rootStyles.getPropertyValue("--border-subtle").trim() || "#e8edf3"

		// Determine tick interval based on zoom
		let tickInterval = 1
		if (pixelsPerSecond < 30) tickInterval = 10
		else if (pixelsPerSecond < 60) tickInterval = 5
		else if (pixelsPerSecond < 150) tickInterval = 2
		else tickInterval = 1

		const majorEvery = tickInterval >= 5 ? 1 : 5

		ctx.fillStyle = textTertiary
		ctx.font = "10px system-ui, sans-serif"
		ctx.textAlign = "center"

		const firstTick = Math.max(
			0,
			Math.floor(
				rulerVirtualWindow.startX / (pixelsPerSecond * tickInterval),
			) * tickInterval,
		)
		const lastTickX = rulerVirtualWindow.endX + pixelsPerSecond * tickInterval

		for (
			let t = firstTick;
			t <= duration && t * pixelsPerSecond <= lastTickX;
			t += tickInterval
		) {
			const x = t * pixelsPerSecond - rulerVirtualWindow.startX
			const isMajor = t % (tickInterval * majorEvery) === 0

			ctx.strokeStyle = isMajor ? textSecondary : borderSubtle
			ctx.lineWidth = 1
			ctx.beginPath()
			ctx.moveTo(x, isMajor ? 0 : RULER_HEIGHT * 0.5)
			ctx.lineTo(x, RULER_HEIGHT)
			ctx.stroke()

			if (isMajor) {
				ctx.fillText(formatTime(t), x, 12)
			}
		}
	}, [
		duration,
		pixelsPerSecond,
		rulerVirtualWindow.endX,
		rulerVirtualWindow.startX,
		rulerVirtualWindow.width,
		totalWidth,
	])

	// Auto-scroll only while playing, so manual scroll works when paused.
	useEffect(() => {
		if (!isPlaying || duration <= 0) return
		const container = scrollRef.current
		if (!container) return

		const playheadX = currentTime * pixelsPerSecond
		const scrollLeft = container.scrollLeft
		const viewport = container.clientWidth

		if (
			playheadX < scrollLeft ||
			playheadX > scrollLeft + viewport - AUTO_SCROLL_GUTTER
		) {
			container.scrollLeft = Math.max(
				playheadX - viewport * AUTO_SCROLL_OFFSET_RATIO,
				0,
			)
		}
	}, [currentTime, duration, isPlaying, pixelsPerSecond])

	if (!selectedFile) {
		return (
			<div
				className={cs(styles.root, styles.empty, className)}
				data-testid="TimelinePanel"
			>
				<span className={styles.emptyText}>
					Выберите файл для предпросмотра
				</span>
			</div>
		)
	}

	const playheadX = currentTime * pixelsPerSecond
	const zoomLabel = (pixelsPerSecond / DEFAULT_PPS).toFixed(1)

	return (
		<div
			className={cs(styles.root, className)}
			data-testid="TimelinePanel"
			onWheel={handleWheel}
		>
			{/* Toolbar */}
			<div className={styles.toolbar}>
				<div className={styles.zoomControls}>
					<button
						className={styles.zoomBtn}
						onClick={zoomOut}
						title="Уменьшить"
					>
						<Minus size={14} />
					</button>
					<span className={styles.zoomLabel}>{zoomLabel}x</span>
					<button
						className={styles.zoomBtn}
						onClick={zoomIn}
						title="Увеличить"
					>
						<Plus size={14} />
					</button>
				</div>
				<span className={styles.timeDisplay}>
					{formatTime(currentTime)} / {formatTime(duration)}
				</span>
			</div>

			{/* Tracks area */}
			<div className={styles.tracksArea}>
				{/* Labels column */}
				<div className={styles.labelsColumn}>
					<div
						className={styles.labelCell}
						style={{ height: RULER_HEIGHT }}
					/>
					{tracks.showVideoFrames && (
						<div
							className={styles.labelCell}
							style={{ height: VIDEO_FRAMES_HEIGHT }}
						>
							<span>Видео</span>
							<button
								className={styles.extractBtn}
								onClick={handleExtractFrames}
								disabled={isExtractingFrames}
								title={
									isExtractingFrames
										? "Извлечение кадров..."
										: "Извлечь кадры"
								}
							>
								{isExtractingFrames ? (
									<Loader2 size={12} className={styles.spin} />
								) : (
									<Film size={12} />
								)}
							</button>
						</div>
					)}
					{tracks.showWaveform && (
						<div
							className={styles.labelCell}
							style={{ height: WAVEFORM_HEIGHT }}
						>
							Аудио
						</div>
					)}
					{tracks.showSubtitles && (
						<div
							className={styles.labelCell}
							style={{ height: TRACK_HEIGHT }}
						>
							Субт.
						</div>
					)}
					{tracks.showSilence && (
						<div
							className={styles.labelCell}
							style={{ height: TRACK_HEIGHT }}
						>
							Тиш.
						</div>
					)}
				</div>

				{/* Scrollable tracks */}
				<div
					className={styles.scrollContainer}
					ref={scrollRef}
					onScroll={handleTracksScroll}
				>
					<div
						className={styles.tracksContent}
						style={{ width: totalWidth }}
					>
						{/* Ruler */}
						<div
							className={styles.rulerHost}
							style={{ height: RULER_HEIGHT }}
						>
							<canvas
								ref={rulerCanvasRef}
								className={styles.ruler}
								style={{ height: RULER_HEIGHT }}
								onClick={handleRulerClick}
							/>
						</div>

						{/* Video frames */}
						{tracks.showVideoFrames && (
							<div
								className={styles.trackRow}
								style={{ height: VIDEO_FRAMES_HEIGHT }}
								data-track="video-frames"
							>
								{audioUrl && duration > 0 && selectedFile && (
									<VideoFramesTrackLazy
										videoUrl={audioUrl}
										fileKey={selectedFile.path}
										pixelsPerSecond={pixelsPerSecond}
										height={VIDEO_FRAMES_HEIGHT}
										duration={duration}
										scrollLeft={scrollLeft}
										viewportWidth={viewportWidth}
									/>
								)}
							</div>
						)}

						{/* Waveform */}
						{tracks.showWaveform && (
							<div
								className={styles.trackRow}
								style={{ height: WAVEFORM_HEIGHT }}
								data-track="waveform"
							>
								{audioUrl && duration > 0 && (
									<WaveformTrackLazy
										audioUrl={audioUrl}
										pixelsPerSecond={pixelsPerSecond}
										height={WAVEFORM_HEIGHT}
										duration={duration}
									/>
								)}
							</div>
						)}

						{/* Subtitles */}
						{tracks.showSubtitles && (
							<div
								className={styles.trackRow}
								style={{ height: TRACK_HEIGHT }}
								data-track="subtitles"
							>
								{transcriptionArtifactId && (
									<SubtitlesTrackLazy
										artifactId={transcriptionArtifactId}
										pixelsPerSecond={pixelsPerSecond}
										height={TRACK_HEIGHT}
										duration={duration}
										scrollLeft={scrollLeft}
										viewportWidth={viewportWidth}
										videoUrl={audioUrl ?? undefined}
										onSegmentClick={handleSegmentClick}
									/>
								)}
							</div>
						)}

						{/* Silence */}
						{tracks.showSilence && (
							<div
								className={styles.trackRow}
								style={{ height: TRACK_HEIGHT }}
								data-track="silence"
							>
								<SilenceTrackPlaceholder height={TRACK_HEIGHT} />
							</div>
						)}

						{/* Playhead */}
						{duration > 0 && (
							<div
								className={styles.playhead}
								style={{ left: playheadX }}
							/>
						)}
					</div>
				</div>
			</div>
		</div>
	)
}

/* ---------- Inline lazy wrappers to avoid circular imports ---------- */

import { lazy, Suspense } from "react"

const VideoFramesTrackComponent = lazy(() =>
	import("@features/project/VideoFramesTrack").then((m) => ({
		default: m.VideoFramesTrack,
	})),
)

const WaveformTrackComponent = lazy(() =>
	import("@features/project/WaveformTrack").then((m) => ({
		default: m.WaveformTrack,
	})),
)

const SubtitlesTrackComponent = lazy(() =>
	import("@features/project/SubtitlesTrack").then((m) => ({
		default: m.SubtitlesTrack,
	})),
)

function WaveformTrackLazy({
	audioUrl,
	pixelsPerSecond,
	height,
	duration,
}: {
	audioUrl: string
	pixelsPerSecond: number
	height: number
	duration: number
}) {
	return (
		<Suspense fallback={<div style={{ height }} />}>
			<WaveformTrackComponent
				audioUrl={audioUrl}
				pixelsPerSecond={pixelsPerSecond}
				height={height}
				duration={duration}
			/>
		</Suspense>
	)
}

function SubtitlesTrackLazy({
	artifactId,
	pixelsPerSecond,
	height,
	duration,
	scrollLeft,
	viewportWidth,
	videoUrl,
	onSegmentClick,
}: {
	artifactId: string
	pixelsPerSecond: number
	height: number
	duration: number
	scrollLeft: number
	viewportWidth: number
	videoUrl?: string
	onSegmentClick?: (segmentIndex: number, artifactId: string) => void
}) {
	return (
		<Suspense fallback={<div style={{ height }} />}>
			<SubtitlesTrackComponent
				artifactId={artifactId}
				pixelsPerSecond={pixelsPerSecond}
				height={height}
				duration={duration}
				scrollLeft={scrollLeft}
				viewportWidth={viewportWidth}
				videoUrl={videoUrl}
				onSegmentClick={onSegmentClick}
			/>
		</Suspense>
	)
}

function VideoFramesTrackLazy({
	videoUrl,
	fileKey,
	pixelsPerSecond,
	height,
	duration,
	scrollLeft,
	viewportWidth,
}: {
	videoUrl: string
	fileKey: string
	pixelsPerSecond: number
	height: number
	duration: number
	scrollLeft: number
	viewportWidth: number
}) {
	return (
		<Suspense fallback={<div style={{ height, background: "#1a1a2e" }} />}>
			<VideoFramesTrackComponent
				videoUrl={videoUrl}
				fileKey={fileKey}
				pixelsPerSecond={pixelsPerSecond}
				height={height}
				duration={duration}
				scrollLeft={scrollLeft}
				viewportWidth={viewportWidth}
			/>
		</Suspense>
	)
}

function SilenceTrackPlaceholder({ height }: { height: number }) {
	return (
		<div
			className={styles.placeholderTrack}
			style={{ height }}
		>
			<span className={styles.placeholderLabel}>Тишина</span>
		</div>
	)
}
