"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { ReactNode } from "react";
import {
  Download,
  Eye,
  FileText,
  ImageIcon,
  Plus,
  Trash2,
  Upload,
  X,
  ZoomIn,
  ZoomOut,
} from "lucide-react";

import ConfirmDialog from "@/components/common/ConfirmDialog";
import { useToast } from "@/components/common/ToastProvider";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Button, buttonVariants } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { api } from "@/lib/api";
import type { RepairDocument, RepairDocumentType, RepairDocumentVisibility } from "@/types/repair";

const documentTypeLabels: Record<RepairDocumentType, string> = {
  device_photo: "Gerätefoto",
  fault_photo: "Fehlerbild",
  measurement: "Messbild",
  estimate: "Kostenvoranschlag",
  repair_report: "Reparaturbericht",
  shipping: "Versandbeleg",
  other: "Sonstiges",
};

const documentTypes = Object.entries(documentTypeLabels) as Array<[RepairDocumentType, string]>;

function getErrorMessage(error: unknown) {
  if (typeof error === "object" && error !== null && "response" in error) {
    const response = (error as { response?: { data?: { detail?: string; message?: string } } }).response;
    return response?.data?.detail ?? response?.data?.message ?? "Aktion konnte nicht abgeschlossen werden";
  }
  return "Aktion konnte nicht abgeschlossen werden";
}

function formatDate(value: string) {
  return new Intl.DateTimeFormat("de-DE", { dateStyle: "short", timeStyle: "short" }).format(new Date(value));
}

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

function isImage(document: RepairDocument) {
  return document.mime_type.startsWith("image/");
}

function isPdf(document: RepairDocument) {
  return document.mime_type === "application/pdf";
}

function downloadUrl(repairId: number, documentId: number, disposition: "inline" | "attachment" = "inline") {
  return `/api/repairs/${repairId}/documents/${documentId}/download?disposition=${disposition}`;
}

type Props = {
  repairId: number;
  canUpdate: boolean;
};

export default function RepairDocumentsSection({ repairId, canUpdate }: Props) {
  const { showToast } = useToast();
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const [documents, setDocuments] = useState<RepairDocument[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [uploadOpen, setUploadOpen] = useState(false);
  const [title, setTitle] = useState("");
  const [documentType, setDocumentType] = useState<RepairDocumentType>("device_photo");
  const [visibility, setVisibility] = useState<RepairDocumentVisibility>("internal");
  const [note, setNote] = useState("");
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [uploading, setUploading] = useState(false);
  const [uploadProgress, setUploadProgress] = useState(0);
  const [deleteTarget, setDeleteTarget] = useState<RepairDocument | null>(null);
  const [deleting, setDeleting] = useState(false);
  const [previewDocument, setPreviewDocument] = useState<RepairDocument | null>(null);
  const [zoom, setZoom] = useState(1);

  const loadDocuments = useCallback(async () => {
    setLoading(true);
    setError("");
    try {
      const response = await api.get<RepairDocument[]>(`/repairs/${repairId}/documents`);
      setDocuments(response.data);
    } catch (err) {
      setError(getErrorMessage(err));
    } finally {
      setLoading(false);
    }
  }, [repairId]);

  useEffect(() => {
    queueMicrotask(() => {
      void loadDocuments();
    });
  }, [loadDocuments]);

  const imageDocuments = useMemo(() => documents.filter(isImage), [documents]);

  useEffect(() => {
    if (!previewDocument) return;

    function handleKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") {
        setPreviewDocument(null);
      }
    }

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [previewDocument]);

  function resetUploadForm() {
    setTitle("");
    setDocumentType("device_photo");
    setVisibility("internal");
    setNote("");
    setSelectedFile(null);
    setUploadProgress(0);
    if (fileInputRef.current) {
      fileInputRef.current.value = "";
    }
  }

  async function uploadDocument() {
    if (!selectedFile) {
      showToast({ type: "error", title: "Keine Datei ausgewählt" });
      return;
    }

    const formData = new FormData();
    formData.append("file", selectedFile);
    formData.append("title", title.trim() || selectedFile.name);
    formData.append("document_type", documentType);
    formData.append("visibility", visibility);
    formData.append("note", note.trim());

    setUploading(true);
    setUploadProgress(0);
    try {
      await api.post<RepairDocument>(`/repairs/${repairId}/documents/upload`, formData, {
        onUploadProgress: (event) => {
          if (event.total) {
            setUploadProgress(Math.round((event.loaded / event.total) * 100));
          }
        },
      });
      await loadDocuments();
      resetUploadForm();
      setUploadOpen(false);
      showToast({ type: "success", title: "Dokument hochgeladen" });
    } catch (err) {
      showToast({
        type: "error",
        title: "Upload fehlgeschlagen",
        description: getErrorMessage(err),
      });
    } finally {
      setUploading(false);
    }
  }

  async function deleteDocument() {
    if (!deleteTarget) return;
    setDeleting(true);
    try {
      await api.delete(`/repairs/${repairId}/documents/${deleteTarget.id}`);
      setDocuments((current) => current.filter((item) => item.id !== deleteTarget.id));
      setDeleteTarget(null);
      showToast({ type: "success", title: "Dokument gelöscht" });
    } catch (err) {
      showToast({
        type: "error",
        title: "Dokument konnte nicht gelöscht werden",
        description: getErrorMessage(err),
      });
    } finally {
      setDeleting(false);
    }
  }

  function openDocument(document: RepairDocument) {
    if (isImage(document)) {
      setPreviewDocument(document);
      setZoom(1);
      return;
    }
    window.open(downloadUrl(repairId, document.id, "inline"), "_blank", "noopener,noreferrer");
  }

  if (loading) {
    return <div className="rounded-lg border bg-slate-50 p-5 text-sm text-slate-500">Dokumente werden geladen...</div>;
  }

  if (error) {
    return <div className="rounded-lg border bg-red-50 p-5 text-sm text-red-700">{error}</div>;
  }

  return (
    <div className="space-y-4">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <p className="text-sm text-slate-500">
          {documents.length} Datei{documents.length === 1 ? "" : "en"} gespeichert
        </p>
        {canUpdate && (
          <Button type="button" onClick={() => setUploadOpen(true)}>
            <Plus />
            Dokument hochladen
          </Button>
        )}
      </div>

      {documents.length === 0 ? (
        <div className="rounded-lg border border-dashed bg-slate-50 p-8 text-center">
          <h3 className="text-lg font-semibold text-slate-950">Noch keine Dokumente oder Bilder vorhanden.</h3>
          <p className="mx-auto mt-2 max-w-xl text-sm text-slate-500">
            Gerätefotos, Messbilder, PDF-Berichte und Belege werden sicher im Olympus Storage gespeichert.
          </p>
          {canUpdate && (
            <div className="mt-5 flex justify-center">
              <Button type="button" onClick={() => setUploadOpen(true)}><Upload />Upload starten</Button>
            </div>
          )}
        </div>
      ) : (
        <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
          {documents.map((document) => (
            <article key={document.id} className="overflow-hidden rounded-lg border bg-white">
              <button
                type="button"
                className="flex aspect-video w-full items-center justify-center bg-slate-100 text-slate-500"
                onClick={() => openDocument(document)}
                aria-label={`${document.title} öffnen`}
              >
                {isImage(document) ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={downloadUrl(repairId, document.id)} alt={document.title} className="h-full w-full object-cover" />
                ) : (
                  <div className="flex flex-col items-center gap-2">
                    <FileText className="h-10 w-10" />
                    <span className="text-xs font-medium">{isPdf(document) ? "PDF" : document.mime_type}</span>
                  </div>
                )}
              </button>

              <div className="space-y-4 p-4">
                <div>
                  <div className="flex items-start gap-2">
                    {isImage(document) ? <ImageIcon className="mt-0.5 h-4 w-4 shrink-0 text-slate-500" /> : <FileText className="mt-0.5 h-4 w-4 shrink-0 text-slate-500" />}
                    <div className="min-w-0">
                      <h3 className="truncate font-semibold text-slate-950">{document.title}</h3>
                      <p className="mt-1 truncate text-xs text-slate-500">{document.original_filename}</p>
                    </div>
                  </div>
                  {document.note && <p className="mt-3 line-clamp-2 text-sm text-slate-600">{document.note}</p>}
                </div>

                <dl className="grid grid-cols-2 gap-3 text-xs">
                  <Meta label="Typ" value={documentTypeLabels[document.document_type]} />
                  <Meta label="Größe" value={formatSize(document.size_bytes)} />
                  <Meta label="MIME" value={document.mime_type} />
                  <Meta label="Sichtbarkeit" value={document.visibility === "customer" ? "Kunde" : "Intern"} />
                  <Meta label="Upload" value={formatDate(document.created_at)} />
                  <Meta label="Von" value={document.uploaded_by_display_name || document.uploaded_by_username || "System"} />
                </dl>

                <div className="flex flex-wrap gap-2">
                  <Button type="button" size="sm" variant="outline" onClick={() => openDocument(document)}>
                    <Eye />
                    Vorschau
                  </Button>
                  <a href={downloadUrl(repairId, document.id, "attachment")} className={buttonVariants({ variant: "outline", size: "sm" })}>
                    <Download />
                    Download
                  </a>
                  {canUpdate && (
                    <Button type="button" size="sm" variant="destructive" onClick={() => setDeleteTarget(document)}>
                      <Trash2 />
                      Löschen
                    </Button>
                  )}
                </div>
              </div>
            </article>
          ))}
        </div>
      )}

      <Dialog open={uploadOpen} onOpenChange={(open) => {
        setUploadOpen(open);
        if (!open && !uploading) resetUploadForm();
      }}>
        <DialogContent className="sm:max-w-xl">
          <DialogHeader>
            <DialogTitle>Dokument hochladen</DialogTitle>
            <DialogDescription>JPG, PNG, WEBP und PDF werden im Reparatur-Storage abgelegt.</DialogDescription>
          </DialogHeader>

          <div className="grid gap-4">
            <Field label="Datei">
              <Input
                ref={fileInputRef}
                type="file"
                accept=".jpg,.jpeg,.png,.webp,.pdf,image/jpeg,image/png,image/webp,application/pdf"
                onChange={(event) => {
                  const file = event.target.files?.[0] ?? null;
                  setSelectedFile(file);
                  if (file && !title) setTitle(file.name);
                }}
              />
            </Field>
            <Field label="Titel">
              <Input value={title} onChange={(event) => setTitle(event.target.value)} />
            </Field>
            <div className="grid gap-4 sm:grid-cols-2">
              <Field label="Dokumenttyp">
                <select
                  className="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                  value={documentType}
                  onChange={(event) => setDocumentType(event.target.value as RepairDocumentType)}
                >
                  {documentTypes.map(([value, label]) => (
                    <option key={value} value={value}>{label}</option>
                  ))}
                </select>
              </Field>
              <Field label="Sichtbarkeit">
                <select
                  className="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                  value={visibility}
                  onChange={(event) => setVisibility(event.target.value as RepairDocumentVisibility)}
                >
                  <option value="internal">Intern</option>
                  <option value="customer">Kunde vorbereitet</option>
                </select>
              </Field>
            </div>
            <Field label="Notiz">
              <textarea
                className="min-h-24 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                value={note}
                onChange={(event) => setNote(event.target.value)}
              />
            </Field>
            {uploading && (
              <div className="rounded-lg border bg-slate-50 p-3">
                <div className="h-2 overflow-hidden rounded-full bg-slate-200">
                  <div className="h-full bg-slate-900 transition-all" style={{ width: `${uploadProgress}%` }} />
                </div>
                <p className="mt-2 text-xs text-slate-500">{uploadProgress}% hochgeladen</p>
              </div>
            )}
          </div>

          <DialogFooter>
            <Button type="button" variant="outline" onClick={() => setUploadOpen(false)} disabled={uploading}>
              Abbrechen
            </Button>
            <Button type="button" onClick={() => void uploadDocument()} disabled={uploading || !selectedFile}>
              <Upload />
              {uploading ? "Lädt hoch..." : "Upload"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <ConfirmDialog
        open={Boolean(deleteTarget)}
        title="Dokument löschen?"
        description="Die Datei wird aus dem Storage entfernt und kann nicht wiederhergestellt werden."
        pending={deleting}
        onOpenChange={(open) => {
          if (!open && !deleting) setDeleteTarget(null);
        }}
        onConfirm={() => void deleteDocument()}
      >
        {deleteTarget && (
          <p className="text-sm text-slate-600">
            {deleteTarget.title}
            <br />
            <span className="text-slate-500">{deleteTarget.original_filename}</span>
          </p>
        )}
      </ConfirmDialog>

      {previewDocument && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4" role="dialog" aria-modal="true">
          <div className="absolute right-4 top-4 flex gap-2">
            <Button type="button" variant="secondary" size="icon" onClick={() => setZoom((current) => Math.max(0.5, current - 0.25))} aria-label="Verkleinern">
              <ZoomOut />
            </Button>
            <Button type="button" variant="secondary" size="icon" onClick={() => setZoom((current) => Math.min(3, current + 0.25))} aria-label="Vergrößern">
              <ZoomIn />
            </Button>
            <Button type="button" variant="secondary" size="icon" onClick={() => setPreviewDocument(null)} aria-label="Vorschau schließen">
              <X />
            </Button>
          </div>
          <button type="button" className="absolute inset-0 cursor-default" onClick={() => setPreviewDocument(null)} aria-label="Vorschau schließen" />
          <div className="relative max-h-[90vh] max-w-[90vw] overflow-auto">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={downloadUrl(repairId, previewDocument.id)}
              alt={previewDocument.title}
              className="relative block max-h-[85vh] max-w-[85vw] object-contain transition-transform"
              style={{ transform: `scale(${zoom})` }}
            />
          </div>
          <p className="absolute bottom-4 left-1/2 max-w-[80vw] -translate-x-1/2 truncate rounded bg-black/60 px-3 py-2 text-sm text-white">
            {previewDocument.title} · {imageDocuments.findIndex((item) => item.id === previewDocument.id) + 1}/{imageDocuments.length}
          </p>
        </div>
      )}
    </div>
  );
}

function Meta({ label, value }: { label: string; value: string }) {
  return (
    <div>
      <dt className="font-medium uppercase tracking-wide text-slate-500">{label}</dt>
      <dd className="mt-1 break-words text-slate-950">{value}</dd>
    </div>
  );
}

function Field({ label, children }: { label: string; children: ReactNode }) {
  return (
    <label className="block">
      <span className="mb-1.5 block text-sm font-medium text-slate-700">{label}</span>
      {children}
    </label>
  );
}
