"use client";

import { useState, useCallback, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Upload,
  FileVideo,
  CheckCircle2,
  XCircle,
  Loader2,
  ChevronRight,
  ArrowRight,
  RotateCcw,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useToast } from "@/components/ui/use-toast";
import type { PreviewItem, PreviewItemStatus } from "@/app/api/batch-upload/preview/route";

interface Project {
  id: string;
  name: string;
  code: string;
}

type UploadStatus = "pending" | "uploading" | "success" | "error";

interface UploadState {
  status: UploadStatus;
  error?: string;
}

interface BatchUploadClientProps {
  projects: Project[];
}

export function BatchUploadClient({ projects }: BatchUploadClientProps) {
  const { toast } = useToast();
  const [projectId, setProjectId] = useState<string>("");
  const [files, setFiles] = useState<File[]>([]);
  const [isDragging, setIsDragging] = useState(false);
  const [isLoadingPreview, setIsLoadingPreview] = useState(false);
  const [preview, setPreview] = useState<PreviewItem[] | null>(null);
  const [uploadStates, setUploadStates] = useState<Record<string, UploadState>>({});
  const [isUploading, setIsUploading] = useState(false);
  const [uploadComplete, setUploadComplete] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // ── Helpers ────────────────────────────────────────────────────────────────

  const reset = () => {
    setFiles([]);
    setPreview(null);
    setUploadStates({});
    setUploadComplete(false);
  };

  const acceptFile = (f: File) =>
    f.name.toLowerCase().endsWith(".mp4") || f.name.toLowerCase().endsWith(".mov");

  // ── Drag & Drop ────────────────────────────────────────────────────────────

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  }, []);

  const handleDragLeave = useCallback(() => {
    setIsDragging(false);
  }, []);

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(false);
    const dropped = Array.from(e.dataTransfer.files).filter(acceptFile);
    if (dropped.length > 0) {
      setFiles(dropped);
      setPreview(null);
      setUploadStates({});
      setUploadComplete(false);
    } else {
      toast({ title: "No supported files", description: "Only .mp4 and .mov files are accepted." });
    }
  }, [toast]);

  const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    const selected = Array.from(e.target.files ?? []).filter(acceptFile);
    if (selected.length > 0) {
      setFiles(selected);
      setPreview(null);
      setUploadStates({});
      setUploadComplete(false);
    }
    // Reset so the same file can be re-selected
    e.target.value = "";
  };

  // ── Preview ────────────────────────────────────────────────────────────────

  const fetchPreview = async () => {
    if (!projectId || files.length === 0) return;
    setIsLoadingPreview(true);
    try {
      const res = await fetch("/api/batch-upload/preview", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ projectId, fileNames: files.map((f) => f.name) }),
      });
      if (!res.ok) throw new Error("Preview request failed");
      const data = await res.json();
      setPreview(data.items);
      // Initialise upload states
      const states: Record<string, UploadState> = {};
      for (const item of data.items as PreviewItem[]) {
        states[item.fileName] = { status: "pending" };
      }
      setUploadStates(states);
    } catch {
      toast({
        title: "Preview failed",
        description: "Could not load the upload preview. Please try again.",
        variant: "destructive",
      });
    } finally {
      setIsLoadingPreview(false);
    }
  };

  // ── Upload ─────────────────────────────────────────────────────────────────

  const startUpload = async () => {
    if (!preview || !projectId) return;
    setIsUploading(true);

    const uploadable = preview.filter(
      (item) => item.status !== "no-shot" && item.status !== "unsupported"
    );

    for (const item of uploadable) {
      const file = files.find((f) => f.name === item.fileName);
      if (!file) continue;

      setUploadStates((prev) => ({
        ...prev,
        [item.fileName]: { status: "uploading" },
      }));

      try {
        if (item.status === "update-highres") {
          // MOV high-res files: use XHR so the browser streams the file from
          // disk chunk-by-chunk, matching the behaviour of HighResUploadDialog
          // which is known to work reliably for large files.
          await new Promise<void>((resolve, reject) => {
            const fd = new FormData();
            fd.append("file", file);
            fd.append("action", "update-highres");
            fd.append("shotId", item.shotId!);
            fd.append("projectId", projectId);

            const xhr = new XMLHttpRequest();
            xhr.open("POST", "/api/batch-upload/upload");

            xhr.addEventListener("load", () => {
              if (xhr.status >= 200 && xhr.status < 300) {
                resolve();
              } else {
                try {
                  const json = JSON.parse(xhr.responseText) as { error?: string };
                  reject(new Error(json.error ?? `HTTP ${xhr.status}`));
                } catch {
                  reject(new Error(`HTTP ${xhr.status}`));
                }
              }
            });

            xhr.addEventListener("error", () => reject(new Error("Network error")));
            xhr.addEventListener("abort", () => reject(new Error("Upload cancelled")));
            xhr.send(fd);
          });
        } else {
          // MP4 version uploads: existing fetch-based flow
          const fd = new FormData();
          fd.append("file", file);
          fd.append("action", item.status);
          fd.append("shotId", item.shotId!);
          fd.append("projectId", projectId);
          if (item.taskId) fd.append("taskId", item.taskId);
          if (item.fallbackTaskId) fd.append("fallbackTaskId", item.fallbackTaskId);
          if (item.newTaskTitle) fd.append("newTaskTitle", item.newTaskTitle);

          const res = await fetch("/api/batch-upload/upload", {
            method: "POST",
            body: fd,
          });
          if (!res.ok) {
            const data = await res.json().catch(() => ({ error: "Upload failed" }));
            throw new Error(data.error ?? "Upload failed");
          }
        }

        setUploadStates((prev) => ({
          ...prev,
          [item.fileName]: { status: "success" },
        }));
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : "Upload failed";
        setUploadStates((prev) => ({
          ...prev,
          [item.fileName]: { status: "error", error: message },
        }));
      }
    }

    setIsUploading(false);
    setUploadComplete(true);
  };

  // ── Derived counts ─────────────────────────────────────────────────────────

  const uploadable = preview?.filter(
    (i) => i.status !== "no-shot" && i.status !== "unsupported"
  ) ?? [];
  const skipped = preview?.filter(
    (i) => i.status === "no-shot" || i.status === "unsupported"
  ) ?? [];
  const successCount = Object.values(uploadStates).filter((s) => s.status === "success").length;
  const errorCount = Object.values(uploadStates).filter((s) => s.status === "error").length;
  const doneCount = successCount + errorCount;
  const progress = uploadable.length > 0 ? (doneCount / uploadable.length) * 100 : 0;

  // ── Render ─────────────────────────────────────────────────────────────────

  return (
    <div className="max-w-5xl mx-auto px-6 py-8 space-y-6">
      {/* Header */}
      <div>
        <h1 className="text-2xl font-semibold text-white">Batch Upload</h1>
        <p className="text-zinc-400 text-sm mt-1">
          Drop <span className="text-zinc-300 font-mono">.mp4</span> files to upload new versions to tasks, and{" "}
          <span className="text-zinc-300 font-mono">.mov</span> files to replace a shot's high-res deliverable.
          Files are matched to shots and tasks by filename.
        </p>
      </div>

      {/* Project selector */}
      <Card>
        <CardContent className="pt-5 pb-5">
          <label className="text-sm font-medium text-zinc-300 block mb-2">Project</label>
          <Select
            value={projectId}
            onValueChange={(v) => {
              setProjectId(v);
              reset();
            }}
          >
            <SelectTrigger className="w-80 bg-zinc-800 border-zinc-700">
              <SelectValue placeholder="Select a project…" />
            </SelectTrigger>
            <SelectContent>
              {projects.map((p) => (
                <SelectItem key={p.id} value={p.id}>
                  {p.name}{" "}
                  <span className="text-zinc-500 font-mono text-xs ml-1">({p.code})</span>
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </CardContent>
      </Card>

      {/* Drop zone */}
      {projectId && !uploadComplete && (
        <Card>
          <CardContent className="pt-5">
            <div
              onDragOver={handleDragOver}
              onDragLeave={handleDragLeave}
              onDrop={handleDrop}
              onClick={() => fileInputRef.current?.click()}
              className={cn(
                "border-2 border-dashed rounded-xl p-12 text-center cursor-pointer transition-colors select-none",
                isDragging
                  ? "border-amber-400 bg-amber-400/5"
                  : "border-zinc-700 hover:border-zinc-500 hover:bg-zinc-800/30"
              )}
            >
              <Upload className="h-10 w-10 text-zinc-500 mx-auto mb-3" />
              <p className="text-zinc-300 font-medium">
                Drop <span className="font-mono">.mp4</span> /{" "}
                <span className="font-mono">.mov</span> files here
              </p>
              <p className="text-zinc-500 text-sm mt-1">or click to browse</p>
              {files.length > 0 && (
                <p className="text-amber-400 text-sm mt-3 font-medium">
                  {files.length} file{files.length !== 1 ? "s" : ""} selected
                </p>
              )}
              <input
                ref={fileInputRef}
                type="file"
                multiple
                accept=".mp4,.mov,video/mp4,video/quicktime"
                className="hidden"
                onChange={handleFileInput}
              />
            </div>

            {files.length > 0 && !preview && (
              <div className="mt-4 flex items-center gap-3">
                <Button onClick={fetchPreview} disabled={isLoadingPreview}>
                  {isLoadingPreview ? (
                    <>
                      <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                      Loading preview…
                    </>
                  ) : (
                    <>
                      Preview upload
                      <ChevronRight className="h-4 w-4 ml-2" />
                    </>
                  )}
                </Button>
                <Button variant="ghost" onClick={reset} disabled={isLoadingPreview}>
                  Clear
                </Button>
              </div>
            )}
          </CardContent>
        </Card>
      )}

      {/* Preview table */}
      {preview && !uploadComplete && (
        <Card>
          <CardHeader className="pb-0">
            <CardTitle className="text-base font-medium text-zinc-200">
              Upload preview —{" "}
              <span className="text-zinc-400 font-normal">
                {preview.length} file{preview.length !== 1 ? "s" : ""}
              </span>
            </CardTitle>
          </CardHeader>
          <CardContent className="p-0 mt-4">
            <div className="overflow-x-auto">
              <table className="w-full text-sm">
                <thead>
                  <tr className="border-b border-zinc-800">
                    <th className="text-left text-xs text-zinc-500 font-normal px-6 py-2.5">
                      File
                    </th>
                    <th className="text-left text-xs text-zinc-500 font-normal px-6 py-2.5">
                      Shot
                    </th>
                    <th className="text-left text-xs text-zinc-500 font-normal px-6 py-2.5">
                      Action
                    </th>
                    <th className="text-left text-xs text-zinc-500 font-normal px-4 py-2.5 w-28">
                      Status
                    </th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-zinc-800/60">
                  {preview.map((item) => (
                    <PreviewRow
                      key={item.fileName}
                      item={item}
                      uploadState={uploadStates[item.fileName]}
                    />
                  ))}
                </tbody>
              </table>
            </div>

            {/* Progress bar while uploading */}
            {isUploading && (
              <div className="px-6 py-3 border-t border-zinc-800">
                <div className="flex justify-between text-xs text-zinc-400 mb-1.5">
                  <span>
                    Uploading {Math.min(doneCount + 1, uploadable.length)} of{" "}
                    {uploadable.length}…
                  </span>
                  <span>{Math.round(progress)}%</span>
                </div>
                <Progress value={progress} className="h-1.5" />
              </div>
            )}

            {/* Footer actions */}
            <div className="px-6 py-4 border-t border-zinc-800 flex items-center justify-between gap-4">
              <p className="text-zinc-500 text-xs">
                {uploadable.length} file{uploadable.length !== 1 ? "s" : ""} will be
                uploaded
                {skipped.length > 0 && (
                  <span className="text-zinc-600">
                    {" "}
                    · {skipped.length} skipped (not matched or unsupported)
                  </span>
                )}
              </p>
              <div className="flex gap-3 shrink-0">
                <Button
                  variant="ghost"
                  onClick={reset}
                  disabled={isUploading}
                  size="sm"
                >
                  Change files
                </Button>
                <Button
                  onClick={startUpload}
                  disabled={isUploading || uploadable.length === 0}
                  size="sm"
                >
                  {isUploading ? (
                    <>
                      <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                      Uploading…
                    </>
                  ) : (
                    `Upload ${uploadable.length} file${uploadable.length !== 1 ? "s" : ""}`
                  )}
                </Button>
              </div>
            </div>
          </CardContent>
        </Card>
      )}

      {/* Done summary */}
      {uploadComplete && (
        <Card>
          <CardContent className="pt-8 pb-8 flex flex-col items-center gap-4 text-center">
            <CheckCircle2 className="h-12 w-12 text-green-500" />
            <div>
              <p className="text-white font-semibold text-lg">Upload complete</p>
              <p className="text-zinc-400 text-sm mt-1">
                {successCount} succeeded
                {errorCount > 0 && (
                  <span className="text-red-400"> · {errorCount} failed</span>
                )}
              </p>
            </div>
            <Button variant="outline" onClick={reset} className="mt-2">
              <RotateCcw className="h-4 w-4 mr-2" />
              Upload more files
            </Button>
          </CardContent>
        </Card>
      )}
    </div>
  );
}

// ── Sub-components ─────────────────────────────────────────────────────────────

function PreviewRow({
  item,
  uploadState,
}: {
  item: PreviewItem;
  uploadState: UploadState | undefined;
}) {
  const ext = item.fileName.split(".").pop()?.toLowerCase() ?? "";
  return (
    <tr className="hover:bg-zinc-800/30 transition-colors">
      {/* File */}
      <td className="px-6 py-3">
        <div className="flex items-center gap-2">
          <FileVideo className="h-4 w-4 text-zinc-500 shrink-0" />
          <span className="text-zinc-200 font-mono text-xs truncate max-w-[260px]">
            {item.fileName}
          </span>
          <span
            className={cn(
              "inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-mono shrink-0",
              ext === "mp4"
                ? "border-blue-500/40 text-blue-400"
                : "border-purple-500/40 text-purple-400"
            )}
          >
            .{ext}
          </span>
        </div>
      </td>

      {/* Shot */}
      <td className="px-6 py-3">
        {item.shotCode ? (
          <span className="text-zinc-300 font-mono text-xs">{item.shotCode}</span>
        ) : (
          <span className="text-red-400 text-xs">Not found</span>
        )}
      </td>

      {/* Action */}
      <td className="px-6 py-3">
        <ActionCell item={item} />
      </td>

      {/* Upload status */}
      <td className="px-4 py-3">
        <StatusCell status={item.status} uploadState={uploadState} />
      </td>
    </tr>
  );
}

function ActionCell({ item }: { item: PreviewItem }) {
  switch (item.status) {
    case "new-version":
      return (
        <div className="space-y-1">
          <ActionBadge color="green">New version</ActionBadge>
          <p className="text-zinc-400 text-[11px] font-mono">{item.currentTaskTitle}</p>
        </div>
      );

    case "rename-and-upload":
      return (
        <div className="space-y-1">
          <ActionBadge color="amber">Rename task + upload</ActionBadge>
          <div className="flex items-center gap-1 text-[11px] font-mono text-zinc-500 flex-wrap">
            <span className="line-through text-zinc-600">{item.currentTaskTitle}</span>
            <ArrowRight className="h-3 w-3 shrink-0" />
            <span className="text-zinc-300">{item.newTaskTitle}</span>
          </div>
        </div>
      );

    case "create-task":
      return (
        <div className="space-y-1">
          {item.fallbackTaskId ? (
            <>
              <ActionBadge color="blue">Rename Comp task + upload</ActionBadge>
              <div className="flex items-center gap-1 text-[11px] font-mono text-zinc-500 flex-wrap">
                <span className="line-through text-zinc-600">{item.fallbackTaskTitle}</span>
                <ArrowRight className="h-3 w-3 shrink-0" />
                <span className="text-zinc-300">{item.newTaskTitle}</span>
              </div>
            </>
          ) : (
            <>
              <ActionBadge color="blue">Create new task</ActionBadge>
              <p className="text-zinc-300 text-[11px] font-mono">{item.newTaskTitle}</p>
            </>
          )}
        </div>
      );

    case "update-highres":
      return (
        <div className="space-y-1">
          <ActionBadge color="purple">Replace high-res</ActionBadge>
          {item.currentHighResFilename && (
            <p className="text-zinc-500 text-[11px]">
              Replaces: <span className="font-mono">{item.currentHighResFilename}</span>
            </p>
          )}
        </div>
      );

    case "no-shot":
      return <ActionBadge color="red">Shot not found</ActionBadge>;

    case "unsupported":
      return <ActionBadge color="zinc">Unsupported file type</ActionBadge>;

    default:
      return null;
  }
}

function StatusCell({
  status,
  uploadState,
}: {
  status: PreviewItemStatus;
  uploadState: UploadState | undefined;
}) {
  if (status === "no-shot" || status === "unsupported") {
    return <span className="text-zinc-600 text-xs">Skipped</span>;
  }
  if (!uploadState || uploadState.status === "pending") {
    return <span className="text-zinc-500 text-xs">Pending</span>;
  }
  if (uploadState.status === "uploading") {
    return (
      <span className="flex items-center gap-1 text-amber-400 text-xs">
        <Loader2 className="h-3 w-3 animate-spin" />
        Uploading
      </span>
    );
  }
  if (uploadState.status === "success") {
    return (
      <span className="flex items-center gap-1 text-green-400 text-xs">
        <CheckCircle2 className="h-3 w-3" />
        Done
      </span>
    );
  }
  if (uploadState.status === "error") {
    return (
      <span
        className="flex items-center gap-1 text-red-400 text-xs"
        title={uploadState.error}
      >
        <XCircle className="h-3 w-3" />
        Error
      </span>
    );
  }
  return null;
}

function ActionBadge({
  children,
  color,
}: {
  children: React.ReactNode;
  color: "green" | "amber" | "blue" | "purple" | "red" | "zinc";
}) {
  const map: Record<string, string> = {
    green: "bg-green-500/10 text-green-400 border-green-500/30",
    amber: "bg-amber-500/10 text-amber-400 border-amber-500/30",
    blue: "bg-blue-500/10 text-blue-400 border-blue-500/30",
    purple: "bg-purple-500/10 text-purple-400 border-purple-500/30",
    red: "bg-red-500/10 text-red-400 border-red-500/30",
    zinc: "bg-zinc-500/10 text-zinc-400 border-zinc-500/30",
  };
  return (
    <span
      className={cn(
        "inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium",
        map[color]
      )}
    >
      {children}
    </span>
  );
}
