import React, { useEffect, useMemo, useRef, useState } from "react";
import {
  Alert,
  AppBar,
  Box,
  Button,
  Chip,
  Container,
  CssBaseline,
  Drawer,
  FormControlLabel,
  Grid,
  IconButton,
  LinearProgress,
  Paper,
  Slider,
  Stack,
  Step,
  StepContent,
  StepLabel,
  Stepper,
  Switch,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TextField,
  InputAdornment,
  Tooltip,
  Toolbar,
  Typography,
} from "@mui/material";
import { ThemeProvider } from "@mui/material/styles";
import {
  Brightness4,
  Brightness7,
  CallSplit,
  ContentPaste,
  ContentCopy,
  Delete,
  Link,
  LinkOff,
  MyLocation,
  Settings,
  SkipNext,
  SkipPrevious,
  UploadFile,
} from "@mui/icons-material";

import {
  autoCutVideo,
  createProject,
  deleteVideo,
  getCurrentProject,
  getJob,
  getVideoMetadata,
  listMarkers,
  listSegmentEdits,
  listVideos,
  replaceMarkers,
  replaceSegmentEdits,
  splitAllVideos,
  splitVideo,
  updateProject,
  uploadVideos,
} from "./api";
import { Job, Project, Video } from "./types";
import { buildTheme, ColorMode } from "./theme";

const formatTime = (value: number) => {
  const total = Math.max(0, value);
  const hours = Math.floor(total / 3600);
  const minutes = Math.floor((total % 3600) / 60);
  const seconds = total % 60;
  const padded = seconds.toFixed(3).padStart(6, "0");
  return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${padded}`;
};

const parseTimecode = (value: string) => {
  const trimmed = value.trim();
  const match = /^(\d+):([0-5]\d):([0-5]\d)(?:\.(\d{1,3}))?$/.exec(trimmed);
  if (!match) {
    return null;
  }
  const hours = Number(match[1]);
  const minutes = Number(match[2]);
  const seconds = Number(match[3]);
  const millisRaw = match[4] ?? "0";
  const millis = Number(millisRaw.padEnd(3, "0"));
  if (Number.isNaN(hours) || Number.isNaN(minutes) || Number.isNaN(seconds) || Number.isNaN(millis)) {
    return null;
  }
  return hours * 3600 + minutes * 60 + seconds + millis / 1000;
};

const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));

const DEFAULT_PASS1_TEMPLATE =
  "ffmpeg -hwaccel auto -y -ss {start} -t {duration} -i {input} -map_metadata -1 -map_chapters -1 -an -sn -b:v 2M -x265-params no-slow-firstpass=1:pass=1:open-gop=0:keyint=60:min-keyint=60:scenecut=0:vbv-maxrate=4000:vbv-bufsize=8000:stats={stats}:pools=+ -tune animation -c:v libx265 -f null {null}";
const DEFAULT_PASS2_TEMPLATE =
  "ffmpeg -hwaccel auto -y -ss {start} -t {duration} -i {input} -map_metadata -1 -map_chapters -1 -map 0:v:0 -map 0:a? -disposition:v:0 default -b:v 2M -x265-params pass=2:open-gop=0:keyint=60:min-keyint=60:scenecut=0:vbv-maxrate=4000:vbv-bufsize=8000:stats={stats}:pools=+ -tune animation -b:a 128k -ac:a 2 -c:v libx265 -c:a libopus -c:s subrip {output}";

export default function App() {
  const [mode, setMode] = useState<ColorMode>("dark");
  const theme = useMemo(() => buildTheme(mode), [mode]);

  const [activeStep, setActiveStep] = useState(0);
  const [project, setProject] = useState<Project | null>(null);
  const [projectDrawerOpen, setProjectDrawerOpen] = useState(false);
  const [projectForm, setProjectForm] = useState({
    name: "Season",
    segments: 4,
    intro: 90,
    outro: 90,
    reencodeEnabled: false,
    ffmpegPass1Template: DEFAULT_PASS1_TEMPLATE,
    ffmpegPass2Template: DEFAULT_PASS2_TEMPLATE,
  });

  const [video, setVideo] = useState<Video | null>(null);
  const [videoFiles, setVideoFiles] = useState<File[]>([]);
  const [videoList, setVideoList] = useState<Video[]>([]);
  const [isLoadingVideos, setIsLoadingVideos] = useState(false);
  const [previewUrl, setPreviewUrl] = useState("");
  const [duration, setDuration] = useState(0);
  const [cursorTime, setCursorTime] = useState(0);
  const [selectedTime, setSelectedTime] = useState(0);
  const [playheadText, setPlayheadText] = useState(formatTime(0));
  const [frameRate, setFrameRate] = useState<number | null>(null);
  const [markers, setMarkers] = useState<number[]>([]);
  const [segmentDrafts, setSegmentDrafts] = useState<{ start: string; end: string }[]>([]);
  const [pendingSegmentEditPersist, setPendingSegmentEditPersist] = useState(false);
  const [autoDetectVideoId, setAutoDetectVideoId] = useState<string | null>(null);
  const autoWindow = 15;
  const [outputPrefix, setOutputPrefix] = useState("");
  const [job, setJob] = useState<Job | null>(null);
  const [outputs, setOutputs] = useState<string[]>([]);
  const [isUploading, setIsUploading] = useState(false);
  const [uploadProgress, setUploadProgress] = useState(0);
  const [error, setError] = useState<string | null>(null);

  type SegmentRow = {
    kind: "intro" | "core" | "outro";
    label: string;
    start: number;
    end: number;
    startEditable: boolean;
    endEditable: boolean;
    coreIndex?: number;
  };

  const [isLinked, setIsLinked] = useState(true);
  const [customSegmentRows, setCustomSegmentRows] = useState<SegmentRow[]>([]);

  const videoRef = useRef<HTMLVideoElement | null>(null);

  useEffect(() => {
    document.body.dataset.theme = mode;
  }, [mode]);

  useEffect(() => {
    let active = true;
    getCurrentProject()
      .then((data) => {
        if (!active) return;
        setProject(data);
        setProjectForm({
          name: data.name,
          segments: data.segments_count,
          intro: data.intro_seconds,
          outro: data.outro_seconds,
          reencodeEnabled: data.reencode_enabled,
          ffmpegPass1Template: data.ffmpeg_pass1_template ?? DEFAULT_PASS1_TEMPLATE,
          ffmpegPass2Template: data.ffmpeg_pass2_template ?? DEFAULT_PASS2_TEMPLATE,
        });
      })
      .catch((err) => {
        if (!active) return;
        if (err?.status !== 404) {
          setError(err.message || "Failed to load project");
        }
      });

    return () => {
      active = false;
    };
  }, []);

  useEffect(() => {
    if (videoFiles.length !== 1) {
      setPreviewUrl("");
      return;
    }

    const url = URL.createObjectURL(videoFiles[0]);
    setPreviewUrl(url);
    return () => {
      URL.revokeObjectURL(url);
    };
  }, [videoFiles]);

  useEffect(() => {
    if (duration && cursorTime > duration) {
      setCursorTime(duration);
    }
  }, [duration, cursorTime]);

  useEffect(() => {
    if (duration && selectedTime > duration) {
      setSelectedTime(duration);
    }
  }, [duration, selectedTime]);

  useEffect(() => {
    setPlayheadText(formatTime(cursorTime));
  }, [cursorTime]);

  useEffect(() => {
    if (!project) {
      setVideoList([]);
    }
  }, [project]);

  useEffect(() => {
    if (!video) return;
    setFrameRate(null);
    getVideoMetadata(video.id)
      .then((meta) => {
        if (meta?.fps && Number.isFinite(meta.fps)) {
          setFrameRate(Math.round(meta.fps * 1000) / 1000);
        }
      })
      .catch(() => undefined);
  }, [video]);

  useEffect(() => {
    if (!job || job.status === "completed" || job.status === "failed") {
      return;
    }

    const interval = window.setInterval(async () => {
      try {
        const latest = await getJob(job.id);
        setJob(latest);
        if (latest.status === "completed") {
          if (latest.kind === "autocut" && latest.result && Array.isArray(latest.result["markers"])) {
            setMarkers(normalizeMarkers(latest.result["markers"] as number[]));
            setPendingSegmentEditPersist(true);
          }
          if ((latest.kind === "split" || latest.kind === "split_all") && latest.result && Array.isArray(latest.result["outputs"])) {
            setOutputs(latest.result["outputs"] as string[]);
          }
        }
      } catch (err) {
        setError((err as Error).message);
      }
    }, 1000);

    return () => window.clearInterval(interval);
  }, [job]);

  const refreshVideos = async () => {
    if (!project) return;
    setIsLoadingVideos(true);
    try {
      const items = await listVideos();
      setVideoList(items);
    } catch (err) {
      setError((err as Error).message);
    } finally {
      setIsLoadingVideos(false);
    }
  };

  useEffect(() => {
    if (!project) return;
    refreshVideos();
  }, [project]);

  const effectiveFrameRate = frameRate ?? 24;
  const frameStep = useMemo(() => 1 / Math.max(effectiveFrameRate, 1), [effectiveFrameRate]);

  const snapToFrame = (value: number) => {
    if (!Number.isFinite(effectiveFrameRate) || effectiveFrameRate <= 0) {
      return value;
    }
    const snapped = Math.round(value * effectiveFrameRate) / effectiveFrameRate;
    return duration ? clamp(snapped, 0, duration) : snapped;
  };

  const selectedFrameTime = snapToFrame(selectedTime);

  const apiBase = import.meta.env.VITE_API_BASE ?? "";
  const frameBase = useMemo(() => {
    if (!video) return "";
    const base = apiBase.endsWith("/") ? apiBase.slice(0, -1) : apiBase;
    return `${base}/api/videos/${video.id}/frame`;
  }, [apiBase, video]);

  const frameUrl = useMemo(() => {
    if (!frameBase) return "";
    return `${frameBase}?ts=${selectedFrameTime.toFixed(3)}&fast=0&w=960`;
  }, [frameBase, selectedFrameTime]);

  const frameStrip = useMemo(() => {
    if (!frameBase || !duration) return [];
    const step = frameStep;
    const frames = [] as { offset: number; ts: number; url: string }[];
    for (let offset = -5; offset <= 5; offset += 1) {
      if (offset === 0) continue;
      const ts = clamp(selectedFrameTime + offset * step, 0, duration);
      frames.push({
        offset,
        ts,
        url: `${frameBase}?ts=${ts.toFixed(3)}&fast=1&w=240`,
      });
    }
    return frames;
  }, [frameBase, duration, frameStep, selectedFrameTime]);

  const frameStripLeft = useMemo(
    () => frameStrip.filter((frame) => frame.offset < 0).sort((a, b) => a.offset - b.offset),
    [frameStrip]
  );
  const frameStripRight = useMemo(
    () => frameStrip.filter((frame) => frame.offset > 0).sort((a, b) => a.offset - b.offset),
    [frameStrip]
  );

  const normalizeMarkers = (values: number[]) => {
    if (!duration) return values;
    const rounded = values.map((value) => Math.round(clamp(value, 0, duration) * 1000) / 1000);
    return Array.from(new Set(rounded)).sort((a, b) => a - b);
  };

  const playheadMarks = useMemo(() => {
    const markMap = new Map<number, { value: number; label?: string }>();

    markers.forEach((marker) => {
      markMap.set(marker, { value: marker });
    });

    if (project?.intro_seconds && duration) {
      const introValue = clamp(project.intro_seconds, 0, duration);
      if (introValue > 0) {
        markMap.set(introValue, { value: introValue, label: "Intro" });
      }
    }

    if (project?.outro_seconds && duration) {
      const outroValue = clamp(duration - project.outro_seconds, 0, duration);
      if (outroValue > 0 && outroValue < duration) {
        markMap.set(outroValue, { value: outroValue, label: "Outro" });
      }
    }

    return Array.from(markMap.values()).sort((a, b) => a.value - b.value);
  }, [markers, project, duration]);

  const introBoundary = useMemo(() => {
    if (!project) return 0;
    const maxDuration = duration || project.intro_seconds;
    return clamp(project.intro_seconds, 0, maxDuration);
  }, [project, duration]);

  const outroBoundary = useMemo(() => {
    if (!project) return duration || 0;
    if (!duration) return 0;
    return clamp(duration - project.outro_seconds, 0, duration);
  }, [project, duration]);

  const lastFrameTime = useMemo(() => {
    if (!duration) return 0;
    if (!Number.isFinite(effectiveFrameRate) || effectiveFrameRate <= 0) return duration;
    const last = Math.floor(duration * effectiveFrameRate) / effectiveFrameRate;
    return clamp(last, 0, duration);
  }, [duration, effectiveFrameRate]);

  const alignedOutroBoundary = useMemo(() => {
    if (!duration) return 0;
    return Math.min(outroBoundary, lastFrameTime || duration);
  }, [duration, outroBoundary, lastFrameTime]);

  const coreBoundaries = useMemo(() => {
    if (!duration) return [] as number[];
    const filtered = markers.filter((marker) => marker > introBoundary && marker < alignedOutroBoundary);
    const sorted = normalizeMarkers(filtered);
    return [introBoundary, ...sorted, alignedOutroBoundary];
  }, [duration, markers, introBoundary, alignedOutroBoundary]);

  const segments = useMemo(() => {
    if (!duration || coreBoundaries.length < 2) return [] as { index: number; start: number; end: number }[];
    return coreBoundaries.slice(0, -1).map((start, idx) => {
      const endBoundary = coreBoundaries[idx + 1];
      const end = Math.max(start, endBoundary - frameStep);
      return {
        index: idx + 1,
        start,
        end,
      };
    });
  }, [duration, coreBoundaries, frameStep]);

  const derivedSegmentRows = useMemo(() => {
    if (!duration || !project || coreBoundaries.length < 2) return [] as SegmentRow[];
    const introEnd = Math.max(0, introBoundary - frameStep);
    const outroStart = clamp(alignedOutroBoundary, 0, duration);
    const rows: SegmentRow[] = [
      {
        kind: "intro",
        label: "Intro",
        start: 0,
        end: introEnd,
        startEditable: false,
        endEditable: true,
      },
    ];
    segments.forEach((segment, index) => {
      rows.push({
        kind: "core",
        label: `Segment ${segment.index}`,
        start: segment.start,
        end: segment.end,
        startEditable: true,
        endEditable: true,
        coreIndex: index,
      });
    });
    rows.push({
      kind: "outro",
      label: "Outro/Credits",
      start: outroStart,
      end: lastFrameTime,
      startEditable: true,
      endEditable: false,
    });
    return rows;
  }, [duration, project, coreBoundaries, introBoundary, alignedOutroBoundary, lastFrameTime, frameStep, segments]);

  const segmentRows = isLinked ? derivedSegmentRows : customSegmentRows;

  useEffect(() => {
    if (!segmentRows.length) {
      setSegmentDrafts([]);
      return;
    }
    setSegmentDrafts(
      segmentRows.map((row) => ({
        start: formatTime(row.start),
        end: formatTime(row.end),
      }))
    );
  }, [segmentRows]);

  const segmentKeyForRow = (row: SegmentRow) => {
    if (row.kind === "core") {
      return `segment_${String((row.coreIndex ?? 0) + 1).padStart(2, "0")}`;
    }
    return row.kind;
  };

  useEffect(() => {
    if (!pendingSegmentEditPersist || !video || segmentRows.length === 0) return;

    let cancelled = false;
    const persist = async () => {
      try {
        await replaceSegmentEdits(
          video.id,
          segmentRows.map((row) => ({
            segment_key: segmentKeyForRow(row),
            start_seconds: row.start,
            end_seconds: row.end,
          }))
        );
      } catch (err) {
        if (!cancelled) {
          setError((err as Error).message);
        }
      } finally {
        if (!cancelled) {
          setPendingSegmentEditPersist(false);
        }
      }
    };

    void persist();
    return () => {
      cancelled = true;
    };
  }, [pendingSegmentEditPersist, video, segmentRows]);

  const segmentColors = [
    "info.main",
    "warning.main",
    "success.main",
    "secondary.main",
    "error.main",
    "primary.main",
  ];
  const segmentEndColors = [
    "info.dark",
    "warning.dark",
    "success.dark",
    "secondary.dark",
    "error.dark",
    "primary.dark",
  ];

  const segmentColorForIndex = (index?: number) => {
    if (index === undefined || index < 0) return "text.disabled";
    return segmentColors[index % segmentColors.length] ?? "primary.main";
  };

  const segmentColorForTime = (timestamp: number) => {
    const matchIndex = segments.findIndex(
      (segment) => timestamp >= segment.start && timestamp <= segment.end
    );
    if (matchIndex < 0) return null;
    return segmentColorForIndex(matchIndex);
  };

  const activeSegmentColor = useMemo(() => {
    const matchIndex = segments.findIndex(
      (segment) => selectedFrameTime >= segment.start && selectedFrameTime <= segment.end
    );
    if (matchIndex < 0) return null;
    return segmentColorForIndex(matchIndex);
  }, [segments, selectedFrameTime]);

  const frameHighlight = useMemo(() => {
    const tolerance = Math.max(frameStep / 2, 0.005);
    for (let i = 0; i < segments.length; i += 1) {
      const segment = segments[i];
      if (Math.abs(selectedFrameTime - segment.start) <= tolerance) {
        return { color: segmentColorForIndex(i), label: `Start S${segment.index}` };
      }
      if (Math.abs(selectedFrameTime - segment.end) <= tolerance) {
        return { color: segmentEndColors[i % segmentEndColors.length] ?? "secondary.main", label: `End S${segment.index}` };
      }
    }
    return null;
  }, [segments, selectedFrameTime, frameStep, segmentEndColors]);

  const outputPreview = useMemo(() => {
    const count = markers.length ? markers.length + 1 : project?.segments_count ?? 0;
    if (!count) return [];
    const prefix = outputPrefix.trim() || "episode";
    return Array.from({ length: count }, (_, index) =>
      `${prefix}_${String(index + 1).padStart(2, "0")}.mkv`
    );
  }, [markers.length, project, outputPrefix]);

  const isBusy = isUploading || (!!job && (job.status === "running" || job.status === "queued"));

  const saveProject = async (
    overrides: Partial<{
      name: string;
      segments_count: number;
      intro_seconds: number;
      outro_seconds: number;
      reencode_enabled: boolean;
      ffmpeg_pass1_template: string | null;
      ffmpeg_pass2_template: string | null;
    }> = {}
  ) => {
    setError(null);
    try {
      const payload = {
        name: projectForm.name.trim() || "Season",
        segments_count: Number(projectForm.segments),
        intro_seconds: Number(projectForm.intro),
        outro_seconds: Number(projectForm.outro),
        reencode_enabled: projectForm.reencodeEnabled,
        ffmpeg_pass1_template: projectForm.ffmpegPass1Template.trim() || null,
        ffmpeg_pass2_template: projectForm.ffmpegPass2Template.trim() || null,
        ...overrides,
      };

      const saved = project ? await updateProject(payload) : await createProject(payload);
      setProject(saved);
      setProjectForm({
        name: saved.name,
        segments: saved.segments_count,
        intro: saved.intro_seconds,
        outro: saved.outro_seconds,
        reencodeEnabled: saved.reencode_enabled,
        ffmpegPass1Template: saved.ffmpeg_pass1_template ?? DEFAULT_PASS1_TEMPLATE,
        ffmpegPass2Template: saved.ffmpeg_pass2_template ?? DEFAULT_PASS2_TEMPLATE,
      });
      return saved;
    } catch (err) {
      setError((err as Error).message);
      return null;
    }
  };

  const handleProjectSave = async () => {
    const saved = await saveProject();
    if (saved) {
      setProjectDrawerOpen(false);
    }
  };

  const handleUpload = async () => {
    if (!project) {
      setError("Configure project settings before uploading.");
      setProjectDrawerOpen(true);
      return;
    }
    if (videoFiles.length === 0) return;
    setError(null);
    setIsUploading(true);
    setUploadProgress(0);

    try {
      const uploadedItems = await uploadVideos(videoFiles, (index, total, progress) => {
        setUploadProgress((index + progress) / total);
      });
      const uploaded = uploadedItems[0];
      await refreshVideos();
      if (uploaded) {
        setVideo(uploaded);
        setDuration(uploaded.duration_seconds);
        setCursorTime(0);
        setSelectedTime(0);
        setMarkers([]);
        setOutputs([]);
        setJob(null);
        setOutputPrefix(uploaded.filename.replace(/\.[^.]+$/, ""));
        setIsLinked(true);
      }
      setVideoFiles([]);
      setPreviewUrl("");
      setActiveStep(1);
    } catch (err) {
      setError((err as Error).message);
    } finally {
      setIsUploading(false);
    }
  };

  const handleSelectVideo = async (item: Video) => {
    setError(null);
    setVideo(item);
    setDuration(item.duration_seconds);
    setCursorTime(0);
    setSelectedTime(0);
    setMarkers([]);
    setOutputs([]);
    setJob(null);
    setOutputPrefix(item.filename.replace(/\.[^.]+$/, ""));
    setVideoFiles([]);
    setPreviewUrl("");
    setIsLinked(true);
    try {
      const response = await listMarkers(item.id);
      setMarkers(normalizeMarkers(response.markers ?? []));
      
      const editsResponse = await listSegmentEdits(item.id);
      if (editsResponse.segments && editsResponse.segments.length > 0) {
        setIsLinked(false);
        setCustomSegmentRows(
          editsResponse.segments.map((se) => ({
            kind: se.segment_key.startsWith("segment_") ? "core" : (se.segment_key as any),
            label: se.segment_key,
            start: se.start_seconds,
            end: se.end_seconds,
            startEditable: true,
            endEditable: true,
            coreIndex: se.segment_key.startsWith("segment_") ? parseInt(se.segment_key.split("_")[1]) - 1 : undefined,
          }))
        );
      } else {
        setIsLinked(true);
        setCustomSegmentRows([]);
      }
    } catch (err) {
      setError((err as Error).message);
    }
    setActiveStep(1);
  };

  const handleDeleteVideo = async (videoId: string) => {
    setError(null);
    try {
      await deleteVideo(videoId);
      await refreshVideos();
      if (video?.id === videoId) {
        setVideo(null);
        setDuration(0);
        setCursorTime(0);
        setSelectedTime(0);
        setFrameRate(null);
        setMarkers([]);
        setOutputs([]);
        setJob(null);
      }
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const handleAutoCut = async () => {
    if (!video) return;
    setError(null);
    setAutoDetectVideoId(video.id);
    try {
      const created = await autoCutVideo(video.id, autoWindow);
      setJob(created);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  useEffect(() => {
    if (activeStep !== 1 || !video || markers.length > 0 || autoDetectVideoId === video.id || isBusy) {
      return;
    }

    void handleAutoCut();
  }, [activeStep, video, markers.length, autoDetectVideoId, isBusy]);

  const handleCopySelectedFrameTime = async () => {
    const timestamp = formatTime(selectedFrameTime);
    try {
      await navigator.clipboard.writeText(timestamp);
      setError(null);
    } catch {
      setError("Could not copy timestamp to clipboard.");
    }
  };

  const handleSplit = async () => {
    if (!video || markers.length === 0) return;
    setError(null);
    try {
      const created = await splitVideo(video.id, markers, outputPrefix.trim() || undefined);
      setJob(created);
      setActiveStep(2);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const handleSplitAll = async () => {
    if (videoList.length === 0) return;
    setError(null);
    try {
      const created = await splitAllVideos(videoList.map((item) => item.id));
      setOutputs([]);
      setJob(created);
      setActiveStep(2);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const handleSaveMarkers = async () => {
    if (!video) return;
    setError(null);
    try {
      const updated = await replaceMarkers(video.id, markers);
      setMarkers(normalizeMarkers(updated.markers));
      setPendingSegmentEditPersist(true);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const commitPlayheadText = () => {
    const parsed = parseTimecode(playheadText);
    if (parsed === null) {
      setError("Invalid timecode. Use hh:mm:ss.sss");
      setPlayheadText(formatTime(cursorTime));
      return;
    }
    const next = clamp(parsed, 0, duration || parsed);
    const snapped = snapToFrame(next);
    setCursorTime(snapped);
    setSelectedTime(snapped);
  };

  const applyBoundaryUpdate = async (boundaryIndex: number, rawBoundary: number) => {
    if (!duration || coreBoundaries.length < 2) return;
    const minBoundary = boundaryIndex > 0 ? coreBoundaries[boundaryIndex - 1] + frameStep : 0;
    const maxBoundary = boundaryIndex < coreBoundaries.length - 1
      ? coreBoundaries[boundaryIndex + 1] - frameStep
      : lastFrameTime || duration;
    const clamped = clamp(rawBoundary, minBoundary, maxBoundary);
    const nextBoundaries = [...coreBoundaries];
    nextBoundaries[boundaryIndex] = clamped;
    const nextMarkers = normalizeMarkers(nextBoundaries.slice(1, -1));
    setMarkers(nextMarkers);
    setPendingSegmentEditPersist(true);
    if (video) {
      await replaceMarkers(video.id, nextMarkers);
    }

    if (!project) return;
    if (boundaryIndex === 0) {
      await saveProject({ intro_seconds: clamped });
    } else if (boundaryIndex === coreBoundaries.length - 1) {
      const nextOutroSeconds = clamp(duration - clamped, 0, duration);
      await saveProject({ outro_seconds: nextOutroSeconds });
    }
  };

  const handleSegmentDraftChange = (rowIndex: number, field: "start" | "end", value: string) => {
    setSegmentDrafts((prev) =>
      prev.map((entry, idx) => (idx === rowIndex ? { ...entry, [field]: value } : entry))
    );
  };

  const commitSegmentDraft = async (rowIndex: number, field: "start" | "end") => {
    const row = segmentRows[rowIndex];
    if (!row) return;
    if (field === "start" && !row.startEditable) return;
    if (field === "end" && !row.endEditable) return;
    const draft = segmentDrafts[rowIndex]?.[field] ?? formatTime(field === "start" ? row.start : row.end);
    const parsed = parseTimecode(draft);
    if (parsed === null) {
      setError("Invalid timecode. Use hh:mm:ss.sss");
      setSegmentDrafts((prev) =>
        prev.map((entry, idx) =>
          idx === rowIndex
            ? { ...entry, [field]: formatTime(field === "start" ? row.start : row.end) }
            : entry
        )
      );
      return;
    }

    const snapped = parsed;

    if (!isLinked) {
      setCustomSegmentRows((prev) => {
        const next = [...prev];
        next[rowIndex] = { ...next[rowIndex], [field]: snapped };
        return next;
      });
      setPendingSegmentEditPersist(true);
      return;
    }

    let boundaryIndex = -1;
    let boundaryValue = snapped;

    if (row.kind === "intro") {
      if (field !== "end") return;
      boundaryIndex = 0;
      boundaryValue = snapped + frameStep;
    } else if (row.kind === "outro") {
      if (field !== "start") return;
      boundaryIndex = coreBoundaries.length - 1;
      boundaryValue = snapped;
    } else {
      const coreIndex = row.coreIndex ?? 0;
      if (field === "start") {
        boundaryIndex = coreIndex;
        boundaryValue = snapped;
      } else {
        boundaryIndex = coreIndex + 1;
        boundaryValue = snapped + frameStep;
      }
    }

    if (boundaryIndex < 0) return;
    await applyBoundaryUpdate(boundaryIndex, boundaryValue);
  };

  const handleCursorCommit = () => {
    const snapped = snapToFrame(cursorTime);
    setCursorTime(snapped);
    setSelectedTime(snapped);
  };

  const handleFrameStep = (direction: number) => {
    if (!duration) return;
    setCursorTime((prev) => {
      const next = clamp(prev + direction * frameStep, 0, duration);
      setSelectedTime(next);
      return next;
    });
  };

  const handleAddMarker = async () => {
    if (!video || !duration) return;
    const snapped = snapToFrame(cursorTime);
    const nextMarkers = normalizeMarkers([...markers, snapped]);
    setMarkers(nextMarkers);
    try {
      await replaceMarkers(video.id, nextMarkers);
      setPendingSegmentEditPersist(true);
    } catch (err) {
      setError((err as Error).message);
    }
  };

  const handleGoToTime = (value: number) => {
    const snapped = snapToFrame(value);
    setCursorTime(snapped);
    setSelectedTime(snapped);
  };

  const handleDeleteSegment = async (row: SegmentRow) => {
    setError(null);
    if (!isLinked) {
      setCustomSegmentRows((prev) => prev.filter((r) => r !== row));
      setPendingSegmentEditPersist(true);
      return;
    }

    if (row.kind === "intro") {
      await saveProject({ intro_seconds: 0 });
      setPendingSegmentEditPersist(true);
      return;
    }

    if (row.kind === "outro") {
      await saveProject({ outro_seconds: 0 });
      setPendingSegmentEditPersist(true);
      return;
    }

    if (row.coreIndex === undefined || coreBoundaries.length <= 2) return;
    const boundaryToRemove =
      row.coreIndex < segments.length - 1 ? row.coreIndex + 1 : row.coreIndex;
    const nextMarkers = coreBoundaries
      .filter((_, index) => index !== boundaryToRemove)
      .slice(1, -1);
    const normalizedMarkers = normalizeMarkers(nextMarkers);
    setMarkers(normalizedMarkers);
    if (video) {
      await replaceMarkers(video.id, normalizedMarkers);
    }
    setPendingSegmentEditPersist(true);
  };

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <AppBar position="static" color="inherit" elevation={1}>
        <Toolbar>
          <Typography variant="h6" sx={{ flexGrow: 1, fontWeight: 600 }}>
            Video Cutter Studio
          </Typography>
          {project && <Chip label={`Project: ${project.name}`} color="primary" sx={{ mr: 2 }} />}
          {!project && <Chip label="No project" color="warning" sx={{ mr: 2 }} />}
          <IconButton onClick={() => setProjectDrawerOpen(true)} aria-label="Project settings">
            <Settings />
          </IconButton>
          <IconButton
            onClick={() => setMode(mode === "dark" ? "light" : "dark")}
            aria-label="Toggle theme"
          >
            {mode === "dark" ? <Brightness7 /> : <Brightness4 />}
          </IconButton>
        </Toolbar>
      </AppBar>

      <Drawer anchor="right" open={projectDrawerOpen} onClose={() => setProjectDrawerOpen(false)}>
        <Box sx={{ width: { xs: 340, sm: 560 }, p: 3 }}>
          <Typography variant="h6" sx={{ mb: 2 }}>
            Project settings
          </Typography>
          <Stack spacing={2}>
            <TextField
              fullWidth
              label="Project name"
              value={projectForm.name}
              onChange={(event) => setProjectForm((prev) => ({ ...prev, name: event.target.value }))}
            />
            <TextField
              fullWidth
              type="number"
              label="Episodes (core)"
              value={projectForm.segments}
              onChange={(event) =>
                setProjectForm((prev) => ({ ...prev, segments: Number(event.target.value) }))
              }
            />
            <FormControlLabel
              control={
                <Switch
                  checked={projectForm.reencodeEnabled}
                  onChange={(event) =>
                    setProjectForm((prev) => ({ ...prev, reencodeEnabled: event.target.checked }))
                  }
                />
              }
              label="Reencode for frame-accurate cuts"
            />
            <TextField
              fullWidth
              multiline
              minRows={4}
              label="FFmpeg pass 1 template"
              value={projectForm.ffmpegPass1Template}
              onChange={(event) =>
                setProjectForm((prev) => ({ ...prev, ffmpegPass1Template: event.target.value }))
              }
              disabled={!projectForm.reencodeEnabled}
            />
            <TextField
              fullWidth
              multiline
              minRows={5}
              label="FFmpeg pass 2 template"
              value={projectForm.ffmpegPass2Template}
              onChange={(event) =>
                setProjectForm((prev) => ({ ...prev, ffmpegPass2Template: event.target.value }))
              }
              disabled={!projectForm.reencodeEnabled}
            />
            <Button variant="contained" onClick={handleProjectSave} disabled={isBusy}>
              {project ? "Save" : "Create project"}
            </Button>
            <Typography variant="caption" color="text.secondary">
              Intro/outro timing is edited in the timeline table and saved for the next upload.
            </Typography>
          </Stack>
        </Box>
      </Drawer>

      <Container maxWidth={false} sx={{ mt: 4, mb: 8, px: { xs: 2, md: 4 } }}>
        {error && (
          <Alert severity="error" sx={{ mb: 3 }} onClose={() => setError(null)}>
            {error}
          </Alert>
        )}

        <Paper elevation={3} sx={{ p: { xs: 2, sm: 4 }, width: "100%" }}>
          <Stepper activeStep={activeStep} orientation="vertical">
            <Step>
              <StepLabel>Upload Video</StepLabel>
              <StepContent>
                <Stack spacing={2} sx={{ mt: 1 }}>
                  <Button
                    variant="outlined"
                    component="label"
                    startIcon={<UploadFile />}
                    disabled={isBusy}
                    sx={{ width: "fit-content" }}
                  >
                    Select MKV files
                    <input
                      type="file"
                      accept=".mkv"
                      multiple
                      hidden
                      onChange={(event) => setVideoFiles(Array.from(event.target.files ?? []))}
                    />
                  </Button>
                  {videoFiles.length > 0 && (
                    <Typography variant="body2">
                      {videoFiles.length === 1
                        ? videoFiles[0].name
                        : `${videoFiles.length} files selected`}
                    </Typography>
                  )}
                  <Box>
                    <Button
                      variant="contained"
                      onClick={handleUpload}
                      disabled={videoFiles.length === 0 || isBusy || !project}
                    >
                      {isUploading ? "Uploading..." : "Upload to Backend"}
                    </Button>
                    <Button sx={{ ml: 1 }} onClick={() => setProjectDrawerOpen(true)}>
                      Project settings
                    </Button>
                  </Box>
                  {isUploading && (
                    <Box sx={{ mt: 1 }}>
                      <LinearProgress variant="determinate" value={uploadProgress * 100} />
                      <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
                        Uploading... {Math.round(uploadProgress * 100)}%
                      </Typography>
                    </Box>
                  )}
                  <Box>
                    <Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>
                      <Typography variant="subtitle2">Recent uploads</Typography>
                      <Button size="small" onClick={refreshVideos} disabled={isLoadingVideos || !project}>
                        Refresh
                      </Button>
                    </Stack>
                    {videoList.length === 0 ? (
                      <Typography variant="body2" color="text.secondary">
                        No uploads yet
                      </Typography>
                    ) : (
                      <TableContainer component={Paper} variant="outlined">
                        <Table size="small">
                          <TableHead>
                            <TableRow>
                              <TableCell>Filename</TableCell>
                              <TableCell>Duration</TableCell>
                              <TableCell align="right">Actions</TableCell>
                            </TableRow>
                          </TableHead>
                          <TableBody>
                            {videoList.map((item) => (
                              <TableRow key={item.id} selected={video?.id === item.id}>
                                <TableCell>{item.filename}</TableCell>
                                <TableCell>{formatTime(item.duration_seconds)}</TableCell>
                                <TableCell align="right">
                                  <Stack direction="row" spacing={1} justifyContent="flex-end">
                                    <Button size="small" variant="outlined" onClick={() => handleSelectVideo(item)}>
                                      Resume
                                    </Button>
                                    <Button
                                      size="small"
                                      color="error"
                                      variant="outlined"
                                      onClick={() => handleDeleteVideo(item.id)}
                                    >
                                      Delete
                                    </Button>
                                  </Stack>
                                </TableCell>
                              </TableRow>
                            ))}
                          </TableBody>
                        </Table>
                      </TableContainer>
                    )}
                  </Box>
                  {previewUrl && (
                    <Box
                      sx={{
                        mt: 2,
                        bgcolor: "background.paper",
                        borderRadius: 1,
                        border: "1px solid",
                        borderColor: "divider",
                        overflow: "hidden",
                      }}
                    >
                      <video
                        ref={videoRef}
                        controls
                        src={previewUrl}
                        style={{ width: "100%", display: "block" }}
                        onLoadedMetadata={(event) => {
                          const current = event.currentTarget.duration;
                          if (Number.isFinite(current)) {
                            setDuration(current);
                          }
                        }}
                      />
                    </Box>
                  )}
                </Stack>
              </StepContent>
            </Step>

            <Step>
              <StepLabel>Timeline & Auto Cut</StepLabel>
              <StepContent>
                <Grid
                  container
                  spacing={3}
                  sx={{
                    mt: 0,
                    "--thumb-h": "64px",
                    "--thumb-gap": "8px",
                  }}
                >
                  <Grid item xs={12} md={3}>
                    <Typography variant="subtitle2" sx={{ mb: 1 }}>
                      Frames before
                    </Typography>
                    {frameStripLeft.length === 0 ? (
                      <Typography variant="body2" color="text.secondary">
                        Load a video to see frames
                      </Typography>
                    ) : (
                      <Box
                        sx={{
                          display: "grid",
                          gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
                          gridAutoRows: "var(--thumb-h)",
                          gap: "var(--thumb-gap)",
                          height: "calc(5 * var(--thumb-h) + 4 * var(--thumb-gap))",
                        }}
                      >
                        {frameStripLeft.map((frame) => {
                          const label = frame.offset > 0 ? `+${frame.offset}` : `${frame.offset}`;
                          const segmentColor = segmentColorForTime(frame.ts);
                          return (
                            <Box
                              key={`left-${frame.offset}-${frame.ts}`}
                              onClick={() => handleGoToTime(frame.ts)}
                              sx={{
                                borderRadius: 1,
                                overflow: "hidden",
                                border: "2px solid",
                                borderColor: segmentColor ?? "divider",
                                cursor: "pointer",
                                position: "relative",
                                backgroundColor: "background.default",
                              }}
                            >
                              <Box
                                component="img"
                                src={frame.url}
                                alt={`Frame ${frame.offset}`}
                                sx={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
                              />
                              <Box
                                sx={{
                                  position: "absolute",
                                  top: 4,
                                  left: 4,
                                  px: 0.5,
                                  py: 0,
                                  borderRadius: 0.5,
                                  fontSize: 11,
                                  backgroundColor: "rgba(0,0,0,0.6)",
                                  color: "#fff",
                                }}
                              >
                                {label}
                              </Box>
                            </Box>
                          );
                        })}
                      </Box>
                    )}
                  </Grid>
                  <Grid item xs={12} md={6}>
                    <Box
                      sx={{
                        bgcolor: "background.paper",
                        borderRadius: 1,
                        border: "3px solid",
                        borderColor: frameHighlight?.color ?? activeSegmentColor ?? "divider",
                        height: "calc(5 * var(--thumb-h) + 4 * var(--thumb-gap))",
                        minHeight: "calc(5 * var(--thumb-h) + 4 * var(--thumb-gap))",
                        display: "flex",
                        alignItems: "center",
                        justifyContent: "center",
                        mb: 2,
                        overflow: "hidden",
                        position: "relative",
                      }}
                    >
                      {frameUrl ? (
                        <Box
                          component="img"
                          src={frameUrl}
                          alt="Frame"
                          sx={{ width: "100%", height: "100%", objectFit: "contain" }}
                        />
                      ) : (
                        <Typography color="text.secondary">Load a video to preview frames</Typography>
                      )}
                      {frameUrl && (
                        <Box
                          sx={{
                            position: "absolute",
                            bottom: 12,
                            left: 12,
                            px: 1.25,
                            py: 0.5,
                            borderRadius: 1,
                            backgroundColor: "rgba(0,0,0,0.6)",
                            color: "#fff",
                            fontSize: 12,
                            fontWeight: 600,
                          }}
                        >
                          {`Frame ${formatTime(selectedFrameTime)}`}
                        </Box>
                      )}
                      {frameHighlight && (
                        <Box
                          sx={{
                            position: "absolute",
                            top: 12,
                            left: 12,
                            px: 1.25,
                            py: 0.5,
                            borderRadius: 1,
                            backgroundColor: "rgba(0,0,0,0.6)",
                            color: "#fff",
                            fontSize: 12,
                            fontWeight: 600,
                          }}
                        >
                          {frameHighlight.label}
                        </Box>
                      )}
                    </Box>
                  </Grid>
                  <Grid item xs={12} md={3}>
                    <Typography variant="subtitle2" sx={{ mb: 1 }}>
                      Frames after
                    </Typography>
                    {frameStripRight.length === 0 ? (
                      <Typography variant="body2" color="text.secondary">
                        Load a video to see frames
                      </Typography>
                    ) : (
                      <Box
                        sx={{
                          display: "grid",
                          gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
                          gridAutoRows: "var(--thumb-h)",
                          gap: "var(--thumb-gap)",
                          height: "calc(5 * var(--thumb-h) + 4 * var(--thumb-gap))",
                        }}
                      >
                        {frameStripRight.map((frame) => {
                          const label = frame.offset > 0 ? `+${frame.offset}` : `${frame.offset}`;
                          const segmentColor = segmentColorForTime(frame.ts);
                          return (
                            <Box
                              key={`right-${frame.offset}-${frame.ts}`}
                              onClick={() => handleGoToTime(frame.ts)}
                              sx={{
                                borderRadius: 1,
                                overflow: "hidden",
                                border: "2px solid",
                                borderColor: segmentColor ?? "divider",
                                cursor: "pointer",
                                position: "relative",
                                backgroundColor: "background.default",
                              }}
                            >
                              <Box
                                component="img"
                                src={frame.url}
                                alt={`Frame ${frame.offset}`}
                                sx={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
                              />
                              <Box
                                sx={{
                                  position: "absolute",
                                  top: 4,
                                  left: 4,
                                  px: 0.5,
                                  py: 0,
                                  borderRadius: 0.5,
                                  fontSize: 11,
                                  backgroundColor: "rgba(0,0,0,0.6)",
                                  color: "#fff",
                                }}
                              >
                                {label}
                              </Box>
                            </Box>
                          );
                        })}
                      </Box>
                    )}
                  </Grid>
                  <Grid item xs={12}>
                    <Stack direction={{ xs: "column", sm: "row" }} spacing={2} alignItems={{ sm: "center" }}>
                      <Stack direction="row" spacing={1} alignItems="center">
                        <IconButton onClick={() => handleFrameStep(-1)} disabled={!duration}>
                          <SkipPrevious />
                        </IconButton>
                        <IconButton onClick={() => handleFrameStep(1)} disabled={!duration}>
                          <SkipNext />
                        </IconButton>
                      </Stack>
                      <TextField
                        size="small"
                        label="Playhead (hh:mm:ss.sss)"
                        placeholder="00:00:00.000"
                        value={playheadText}
                        onChange={(event) => setPlayheadText(event.target.value)}
                        onBlur={commitPlayheadText}
                        onKeyDown={(event) => {
                          if (event.key === "Enter") {
                            event.preventDefault();
                            commitPlayheadText();
                          }
                        }}
                        sx={{ width: 190 }}
                      />
                      <Tooltip title="Copy central frame timestamp">
                        <span>
                          <IconButton
                            onClick={handleCopySelectedFrameTime}
                            disabled={!duration}
                            aria-label="Copy central frame timestamp"
                          >
                            <ContentCopy />
                          </IconButton>
                        </span>
                      </Tooltip>
                      <Chip
                        variant="outlined"
                        label={`FFmpeg FPS: ${frameRate === null ? "unknown" : frameRate.toFixed(3)}`}
                      />
                    </Stack>
                  </Grid>
                  <Grid item xs={12}>
                    <Typography gutterBottom>Playhead: {formatTime(cursorTime)}</Typography>
                    <Slider
                      value={cursorTime}
                      min={0}
                      max={duration || 1}
                      step={frameStep}
                      onChange={(_, value) => setCursorTime(value as number)}
                      onChangeCommitted={handleCursorCommit}
                      marks={playheadMarks}
                      disabled={!duration}
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <Stack direction={{ xs: "column", sm: "row" }} spacing={2} alignItems={{ sm: "center" }}>
                      <Button variant="outlined" onClick={handleAddMarker} disabled={!duration}>
                        Add marker at playhead
                      </Button>


                    </Stack>
                  </Grid>
                  <Grid item xs={12}>
                    <TableContainer component={Paper} variant="outlined">
                      <Table size="small">
                        <TableHead>
                          <TableRow>
                            <TableCell>Segment</TableCell>
                            <TableCell align="center">Color</TableCell>
                            <TableCell>Start</TableCell>
                            <TableCell>End</TableCell>
                            <TableCell align="right">Delete</TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {segmentRows.length === 0 ? (
                            <TableRow>
                              <TableCell colSpan={5} align="center">
                                <Typography variant="body2" color="text.secondary">
                                  Load a video to see segments
                                </Typography>
                              </TableCell>
                            </TableRow>
                          ) : (
                            segmentRows.map((row, rowIndex) => (
                              <TableRow key={`${row.kind}-${row.label}-${rowIndex}`}>
                                <TableCell>{row.label}</TableCell>
                                <TableCell align="center">
                                  <Box
                                    sx={{
                                      width: 12,
                                      height: 12,
                                      borderRadius: "50%",
                                      bgcolor: row.kind === "core" ? segmentColorForIndex(row.coreIndex) : "text.disabled",
                                      mx: "auto",
                                    }}
                                  />
                                </TableCell>
                                <TableCell sx={{ minWidth: 260 }}>
                                  <Stack direction="row" spacing={1} alignItems="center">
                                    <TextField
                                      size="small"
                                      fullWidth
                                      value={segmentDrafts[rowIndex]?.start ?? formatTime(row.start)}
                                      onChange={(event) =>
                                        handleSegmentDraftChange(rowIndex, "start", event.target.value)
                                      }
                                      onBlur={() => commitSegmentDraft(rowIndex, "start")}
                                      onKeyDown={(event) => {
                                        if (event.key === "Enter") {
                                          event.preventDefault();
                                          commitSegmentDraft(rowIndex, "start");
                                        }
                                      }}
                                      disabled={!row.startEditable}
                                    />
                                    <Tooltip title="Go to start time">
                                      <span>
                                        <IconButton
                                          size="small"
                                          onClick={() => handleGoToTime(row.start)}
                                          disabled={!duration}
                                          aria-label={`Go to ${row.label} start`}
                                        >
                                          <MyLocation fontSize="small" />
                                        </IconButton>
                                      </span>
                                    </Tooltip>
                                  </Stack>
                                </TableCell>
                                <TableCell sx={{ minWidth: 260 }}>
                                  <Stack direction="row" spacing={1} alignItems="center">
                                    <TextField
                                      size="small"
                                      fullWidth
                                      value={segmentDrafts[rowIndex]?.end ?? formatTime(row.end)}
                                      onChange={(event) => handleSegmentDraftChange(rowIndex, "end", event.target.value)}
                                      onBlur={() => commitSegmentDraft(rowIndex, "end")}
                                      onKeyDown={(event) => {
                                        if (event.key === "Enter") {
                                          event.preventDefault();
                                          commitSegmentDraft(rowIndex, "end");
                                        }
                                      }}
                                      disabled={!row.endEditable}
                                    />
                                    <Tooltip title="Go to end time">
                                      <span>
                                        <IconButton
                                          size="small"
                                          onClick={() => handleGoToTime(row.end)}
                                          disabled={!duration}
                                          aria-label={`Go to ${row.label} end`}
                                        >
                                          <MyLocation fontSize="small" />
                                        </IconButton>
                                      </span>
                                    </Tooltip>
                                  </Stack>
                                </TableCell>
                                <TableCell align="right">
                                  <Tooltip title={`Delete ${row.label}`}>
                                    <span>
                                      <IconButton
                                        size="small"
                                        color="error"
                                        onClick={() => handleDeleteSegment(row)}
                                        disabled={!duration || (row.kind === "core" && segments.length <= 1)}
                                        aria-label={`Delete ${row.label}`}
                                      >
                                        <Delete fontSize="small" />
                                      </IconButton>
                                    </span>
                                  </Tooltip>
                                </TableCell>
                              </TableRow>
                            ))
                          )}
                        </TableBody>
                      </Table>
                    </TableContainer>
                  </Grid>
                  {job?.kind === "autocut" && job.status !== "completed" && (
                    <Grid item xs={12}>
                      <Box>
                        <LinearProgress variant="determinate" value={Math.round(job.progress * 100)} />
                        <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
                          {job.message || `Status: ${job.status}`}
                        </Typography>
                      </Box>
                    </Grid>
                  )}
                </Grid>
                <Box sx={{ mt: 4 }}>
                  <Button variant="contained" onClick={() => setActiveStep(2)} disabled={markers.length === 0}>
                    Proceed to Export
                  </Button>
                  <Button sx={{ ml: 1 }} onClick={() => setActiveStep(0)}>
                    Back
                  </Button>
                </Box>
              </StepContent>
            </Step>

            <Step>
              <StepLabel>Export Episodes</StepLabel>
              <StepContent>
                <Typography color="text.secondary" gutterBottom>
                  Review markers and export your final files.
                </Typography>
                <Stack spacing={2} sx={{ mt: 2 }}>
                  <TextField
                    fullWidth
                    label="Output prefix"
                    value={outputPrefix}
                    onChange={(event) => setOutputPrefix(event.target.value)}
                  />
                  {outputPreview.length > 0 && (
                    <Box>
                      <Typography variant="subtitle2">Filename preview</Typography>
                      <Stack spacing={0.5} sx={{ mt: 1 }}>
                        {outputPreview.map((name) => (
                          <Typography key={name} variant="body2">
                            {name}
                          </Typography>
                        ))}
                      </Stack>
                    </Box>
                  )}
                  <Box>
                    <Button
                      variant="contained"
                      color="success"
                      startIcon={<CallSplit />}
                      onClick={handleSplit}
                      disabled={isBusy || markers.length === 0}
                    >
                      Split Video
                    </Button>
                    <Button
                      sx={{ ml: 1 }}
                      variant="outlined"
                      color="success"
                      startIcon={<CallSplit />}
                      onClick={handleSplitAll}
                      disabled={isBusy || videoList.length === 0}
                    >
                      Export All
                    </Button>
                    <Button sx={{ ml: 1 }} onClick={() => setActiveStep(1)}>
                      Back
                    </Button>
                  </Box>
                  {(job?.kind === "split" || job?.kind === "split_all") && job.status !== "completed" && (
                    <Box sx={{ p: 2, bgcolor: "background.paper", borderRadius: 4, border: "1px solid", borderColor: "divider" }}>
                      <Typography variant="h6" gutterBottom>
                        {job.kind === "split_all" ? "Bulk Export Progress" : "Export Progress"}
                      </Typography>
                      
                      {job.kind === "split_all" && job.details?.label && (() => {
                        const match = /^(\d+)\/(\d+) (.+)$/.exec(job.details.label);
                        if (match) {
                          const index = Number(match[1]);
                          const total = Number(match[2]);
                          const filename = match[3];
                          const videoProgress = Math.max(0, Math.min(100, (job.progress - (index - 1) / total) * total * 100));
                          
                          return (
                            <Box sx={{ mb: 3 }}>
                              <Typography variant="body2" color="text.secondary" gutterBottom>
                                Overall Bulk Progress ({index}/{total} videos)
                              </Typography>
                              <LinearProgress 
                                variant="determinate" 
                                value={Math.round(job.progress * 100)} 
                                sx={{ height: 12, borderRadius: 6, mb: 2, bgcolor: "primary.light" }} 
                              />
                              
                              <Typography variant="body2" color="text.primary" sx={{ fontWeight: "bold" }}>
                                Current Video: {filename}
                              </Typography>
                              <LinearProgress 
                                variant="determinate" 
                                value={Math.round(videoProgress)} 
                                color="secondary"
                                sx={{ height: 8, borderRadius: 4, mb: 2 }} 
                              />
                              
                              <Typography variant="body2" color="text.secondary">
                                Current Stage: {job.details.stage || "working"}
                              </Typography>
                              <LinearProgress 
                                variant="determinate" 
                                value={job.details.stage_percent || 0} 
                                color="info"
                                sx={{ height: 4, borderRadius: 2 }} 
                              />
                            </Box>
                          );
                        }
                        return null;
                      })()}

                      {job.kind === "split" && (
                        <Box sx={{ mb: 2 }}>
                          <Typography variant="body2" color="text.primary" gutterBottom>
                            Video Progress
                          </Typography>
                          <LinearProgress 
                            variant="determinate" 
                            value={Math.round(job.progress * 100)} 
                            color="secondary"
                            sx={{ height: 8, borderRadius: 4, mb: 2 }} 
                          />
                          <Typography variant="body2" color="text.secondary">
                            Current Stage: {job.details?.stage || "working"}
                          </Typography>
                          <LinearProgress 
                            variant="determinate" 
                            value={job.details?.stage_percent || 0} 
                            color="info"
                            sx={{ height: 4, borderRadius: 2 }} 
                          />
                        </Box>
                      )}

                      <Stack direction={{ xs: "column", sm: "row" }} spacing={1} sx={{ mt: 2 }}>
                        {job.details?.stage_eta && <Chip size="small" label={`Stage ETA: ${job.details.stage_eta}`} />}
                        {job.details?.total_eta && <Chip size="small" label={`Total ETA: ${job.details.total_eta}`} />}
                        <Chip size="small" color={job.status === "running" ? "primary" : "default"} label={`Status: ${job.status}`} />
                      </Stack>

                      {job.kind === "split_all" && (
                        <Box sx={{ mt: 3, p: 2, bgcolor: "background.default", borderRadius: 2 }}>
                          <Typography variant="subtitle2" gutterBottom>
                            Queue Queue
                          </Typography>
                          <Stack spacing={0.5}>
                            {videoList.map((v, i) => {
                              const match = job.details?.label ? /^(\d+)\/(\d+) (.+)$/.exec(job.details.label) : null;
                              const currentIndex = match ? Number(match[1]) - 1 : -1;
                              const status = i < currentIndex ? "completed" : i === currentIndex ? "running" : "pending";
                              return (
                                <Typography 
                                  key={v.id} 
                                  variant="body2" 
                                  color={status === "completed" ? "success.main" : status === "running" ? "primary.main" : "text.disabled"}
                                  sx={{ fontWeight: status === "running" ? "bold" : "normal" }}
                                >
                                  {status === "completed" ? "✓ " : status === "running" ? "▶ " : "• "}
                                  {v.filename}
                                </Typography>
                              );
                            })}
                          </Stack>
                        </Box>
                      )}
                    </Box>
                  )}
                  {job?.logs && job.logs.length > 0 && (
                    <Box>
                      <Typography variant="subtitle2">Backend log</Typography>
                      <Box
                        sx={{
                          mt: 1,
                          p: 1,
                          borderRadius: 1,
                          border: "1px solid",
                          borderColor: "divider",
                          maxHeight: 160,
                          overflow: "auto",
                          fontFamily: "monospace",
                          fontSize: 12,
                        }}
                      >
                        {job.logs.slice(-12).map((entry) => (
                          <Box key={`${entry.at}-${entry.message}`}>{`${entry.at} ${entry.message}`}</Box>
                        ))}
                      </Box>
                    </Box>
                  )}
                  {outputs.length > 0 && (
                    <Box>
                      <Typography variant="subtitle2">Outputs</Typography>
                      <Stack spacing={0.5} sx={{ mt: 1 }}>
                        {outputs.map((output) => (
                          <Typography key={output} variant="body2">
                            {output}
                          </Typography>
                        ))}
                      </Stack>
                    </Box>
                  )}
                  {job?.result && Array.isArray(job.result["skipped"]) && (job.result["skipped"] as unknown[]).length > 0 && (
                    <Box>
                      <Typography variant="subtitle2">Skipped videos</Typography>
                      <Stack spacing={0.5} sx={{ mt: 1 }}>
                        {(job.result["skipped"] as { filename?: string; reason?: string }[]).map((item, index) => (
                          <Typography key={`${item.filename ?? "video"}-${index}`} variant="body2" color="text.secondary">
                            {`${item.filename ?? "Video"}: ${item.reason ?? "Not ready"}`}
                          </Typography>
                        ))}
                      </Stack>
                    </Box>
                  )}
                </Stack>
              </StepContent>
            </Step>
          </Stepper>
        </Paper>
      </Container>
    </ThemeProvider>
  );
}
