"use client";

import { useActionState, useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import dynamic from "next/dynamic";
import { Download, Eye, FileText, ImageIcon, Layers, Loader2, Trash2, Upload } from "lucide-react";
import { toast } from "sonner";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Progress } from "@/components/ui/progress";
import { deleteJobFileAction } from "@/lib/appwrite/job-file-actions";
import {
  initialJobFileActionState,
  JOB_FILE_KIND_LABELS,
} from "@/lib/appwrite/job-file-types";
import type { JobFileWithUrl } from "@/lib/appwrite/job-file-queries";

// three.js + react-three is ~500KB minified; only load it when the user
// actually opens the viewer dialog.
const STLViewer = dynamic(
  () => import("@/components/stl-viewer").then((m) => m.STLViewer),
  { ssr: false, loading: () => null },
);

const VIEWABLE_RE = /\.(stl|ply|obj)$/i;

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}

function kindIcon(kind: string) {
  if (kind === "image") return <ImageIcon className="size-4" />;
  if (kind === "scan") return <Layers className="size-4" />;
  return <FileText className="size-4" />;
}

export function JobFilesPanel({
  jobId,
  files,
}: {
  jobId: string;
  files: JobFileWithUrl[];
}) {
  return (
    <div className="space-y-4">
      <UploadForm jobId={jobId} />
      {files.length === 0 ? (
        <p className="text-muted-foreground py-4 text-center text-sm">
          Henüz dosya yok.
        </p>
      ) : (
        <ul className="divide-y rounded-md border">
          {files.map((f) => (
            <FileRow key={f.$id} file={f} />
          ))}
        </ul>
      )}
    </div>
  );
}

const MAX_FILE_BYTES = 200 * 1024 * 1024;
const MAX_BATCH_BYTES = 200 * 1024 * 1024; // proxy cap; one large file fills the whole batch

type UploadPhase = "idle" | "uploading" | "processing";

function UploadForm({ jobId }: { jobId: string }) {
  const router = useRouter();
  const inputRef = useRef<HTMLInputElement>(null);
  const xhrRef = useRef<XMLHttpRequest | null>(null);
  const [selected, setSelected] = useState<File[]>([]);
  const [phase, setPhase] = useState<UploadPhase>("idle");
  const [progress, setProgress] = useState(0); // 0–100

  const totalBytes = selected.reduce((s, f) => s + f.size, 0);
  const overSize = selected.find((f) => f.size > MAX_FILE_BYTES);
  const overBatch = totalBytes > MAX_BATCH_BYTES;
  const blocked = Boolean(overSize) || overBatch;
  const busy = phase !== "idle";

  function reset() {
    setSelected([]);
    setProgress(0);
    setPhase("idle");
    if (inputRef.current) inputRef.current.value = "";
  }

  function startUpload() {
    if (selected.length === 0 || blocked) return;
    const formData = new FormData();
    for (const f of selected) formData.append("files", f);

    const xhr = new XMLHttpRequest();
    xhrRef.current = xhr;
    setPhase("uploading");
    setProgress(0);

    xhr.upload.addEventListener("progress", (e) => {
      if (!e.lengthComputable) return;
      const pct = Math.round((e.loaded / e.total) * 100);
      setProgress(pct);
    });

    // The browser has finished pushing bytes; the server is now writing to
    // Appwrite. Flip to "processing" so the user sees something is still
    // happening (large files take 30-60s to land in Storage).
    xhr.upload.addEventListener("load", () => {
      setPhase("processing");
      setProgress(100);
    });

    xhr.addEventListener("load", () => {
      let payload: { ok?: boolean; uploaded?: number; error?: string } = {};
      try {
        payload = JSON.parse(xhr.responseText);
      } catch {
        /* non-JSON response */
      }
      if (xhr.status >= 200 && xhr.status < 300 && payload.ok) {
        toast.success(`${payload.uploaded ?? selected.length} dosya yüklendi.`);
        reset();
        router.refresh();
      } else {
        toast.error(payload.error || `Yükleme başarısız (HTTP ${xhr.status}).`);
        setPhase("idle");
        setProgress(0);
      }
    });

    xhr.addEventListener("error", () => {
      setPhase("idle");
      setProgress(0);
      toast.error("Ağ hatası. Tekrar deneyin.");
    });

    xhr.addEventListener("abort", () => {
      setPhase("idle");
      setProgress(0);
      toast.message("Yükleme iptal edildi.");
    });

    xhr.open("POST", `/api/jobs/${jobId}/files`);
    xhr.send(formData);
  }

  function cancelUpload() {
    xhrRef.current?.abort();
  }

  return (
    <div className="bg-muted/30 flex flex-col gap-3 rounded-md border p-3">
      <div className="flex flex-wrap items-center gap-3">
        <input
          ref={inputRef}
          type="file"
          multiple
          className="hidden"
          accept=".pdf,.jpg,.jpeg,.png,.webp,.tiff,.tif,.bmp,.heic,.heif,.stl,.obj,.ply,.3mf,.zip,.rar,.7z,.dcm,.stm"
          onChange={(e) => {
            const list = e.target.files ? Array.from(e.target.files) : [];
            setSelected(list);
            setProgress(0);
          }}
        />
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={() => inputRef.current?.click()}
          disabled={busy}
        >
          <Upload className="size-4" />
          Dosya seç
        </Button>
        <span className="text-muted-foreground text-xs flex-1">
          {selected.length > 0 ? (
            overSize ? (
              <span className="text-destructive">
                {overSize.name} 200MB&apos;tan büyük (her dosya maksimum 200MB).
              </span>
            ) : overBatch ? (
              <span className="text-destructive">
                Toplam {formatSize(totalBytes)} — 200MB sınırını aşıyor. Daha az dosya seçin.
              </span>
            ) : (
              <>
                {selected.length} dosya seçildi ({formatSize(totalBytes)})
              </>
            )
          ) : (
            "Tarama (STL/OBJ), görsel veya PDF — max 200MB / dosya"
          )}
        </span>
        {phase === "uploading" ? (
          <Button type="button" size="sm" variant="outline" onClick={cancelUpload}>
            İptal
          </Button>
        ) : phase === "processing" ? (
          <Button type="button" size="sm" variant="outline" disabled>
            <Loader2 className="size-4 animate-spin" />
            İşleniyor
          </Button>
        ) : (
          <Button
            type="button"
            size="sm"
            onClick={startUpload}
            disabled={selected.length === 0 || blocked}
          >
            <Upload className="size-4" />
            Yükle
          </Button>
        )}
      </div>
      {phase === "uploading" && (
        <div className="flex items-center gap-3">
          <Progress value={progress} className="flex-1" />
          <span className="text-muted-foreground min-w-[3rem] text-right text-xs tabular-nums">
            {progress}%
          </span>
        </div>
      )}
      {phase === "processing" && (
        <div className="flex items-center gap-3">
          <Progress value={100} className="flex-1" />
          <span className="text-muted-foreground flex items-center gap-1.5 text-xs">
            <Loader2 className="size-3 animate-spin" />
            Sunucu Appwrite&apos;a yazıyor — büyük dosyalar 30-60 sn sürebilir
          </span>
        </div>
      )}
    </div>
  );
}

function FileRow({ file }: { file: JobFileWithUrl }) {
  const [state, action, pending] = useActionState(
    deleteJobFileAction,
    initialJobFileActionState,
  );
  const router = useRouter();
  const [open, setOpen] = useState(false);
  const [downloadOpen, setDownloadOpen] = useState(false);
  const [viewerOpen, setViewerOpen] = useState(false);
  const isArchived = Boolean(file.archivedAt);
  const isViewable = !isArchived && VIEWABLE_RE.test(file.name);

  useEffect(() => {
    if (state.ok) {
      toast.success("Dosya silindi.");
      setOpen(false);
      router.refresh();
    } else if (state.error) {
      toast.error(state.error);
    }
  }, [state, router]);

  function triggerDownload() {
    // Use a programmatic anchor click — the server route streams the file
    // with Content-Disposition: attachment, so the browser hands it straight
    // to the download manager. Toast confirms it left our side.
    const a = document.createElement("a");
    a.href = file.url;
    a.download = file.name;
    document.body.appendChild(a);
    a.click();
    a.remove();
    setDownloadOpen(false);
    toast.success("İndirme başladı.", { description: file.name });
  }

  return (
    <li className={`flex items-center gap-3 px-3 py-2 ${isArchived ? "opacity-60" : ""}`}>
      <span className="text-muted-foreground">{kindIcon(file.kind)}</span>
      <div className="flex-1 min-w-0">
        <p className="truncate text-sm font-medium">{file.name}</p>
        <p className="text-muted-foreground text-xs">
          {JOB_FILE_KIND_LABELS[file.kind] ?? file.kind} · {formatSize(file.size)}
          {isArchived && (
            <>
              {" · "}
              <span className="text-amber-600 dark:text-amber-400">
                Arşivlendi {new Date(file.archivedAt!).toLocaleDateString("tr-TR")}
              </span>
            </>
          )}
        </p>
      </div>
      <Badge variant="outline" className="hidden sm:inline-flex">
        {JOB_FILE_KIND_LABELS[file.kind] ?? file.kind}
      </Badge>
      {isViewable && (
        <Dialog open={viewerOpen} onOpenChange={setViewerOpen}>
          <Button
            size="sm"
            variant="outline"
            onClick={() => setViewerOpen(true)}
          >
            <Eye className="size-4" />
          </Button>
          <DialogContent className="h-[85vh] max-w-5xl gap-0 p-0">
            <DialogHeader className="border-b px-4 py-3">
              <DialogTitle className="truncate text-base">{file.name}</DialogTitle>
              <DialogDescription className="text-xs">
                {formatSize(file.size)} · Sürükleyerek döndürün, kaydırarak yaklaşın.
              </DialogDescription>
            </DialogHeader>
            <div className="min-h-0 flex-1">
              {viewerOpen && <STLViewer url={file.url} filename={file.name} />}
            </div>
          </DialogContent>
        </Dialog>
      )}
      <Dialog open={downloadOpen} onOpenChange={setDownloadOpen}>
        <Button
          size="sm"
          variant="outline"
          onClick={() => setDownloadOpen(true)}
          disabled={isArchived}
          title={isArchived ? "Bu dosya arşivlendi; indirilebilir kopyası yok." : undefined}
        >
          <Download className="size-4" />
        </Button>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Dosya indirilsin mi?</DialogTitle>
            <DialogDescription>
              <span className="font-medium">{file.name}</span>
              <span className="text-muted-foreground"> · {formatSize(file.size)}</span>
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <DialogClose asChild>
              <Button type="button" variant="outline">
                Vazgeç
              </Button>
            </DialogClose>
            <Button type="button" onClick={triggerDownload}>
              <Download className="size-4" />
              İndir
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <Dialog open={open} onOpenChange={setOpen}>
        <Button size="sm" variant="outline" onClick={() => setOpen(true)}>
          <Trash2 className="size-4" />
        </Button>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Dosya silinsin mi?</DialogTitle>
            <DialogDescription>{file.name}</DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <DialogClose asChild>
              <Button type="button" variant="outline">
                Vazgeç
              </Button>
            </DialogClose>
            <form action={action}>
              <input type="hidden" name="rowId" value={file.$id} />
              <Button type="submit" variant="destructive" disabled={pending}>
                {pending ? <Loader2 className="size-4 animate-spin" /> : <Trash2 className="size-4" />}
                Sil
              </Button>
            </form>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </li>
  );
}
