"use client";

import { useMemo, useState } from "react";
import Link from "next/link";
import { ChevronRight, Download, Eye, FolderInput, FolderOpen, ShieldAlert } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { FieldGroup } from "@/components/ui/field";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { FormErrorAlert, SelectField, TextField } from "@/components/app/form-fields";
import { cn } from "@/lib/utils";
import { FILE_KIND_STYLES, fileKind } from "../documents/file-utils";
import { recordNode } from "../documents/file-nodes";
import { FileTree } from "../documents/file-tree";
import { useFileCategories, useFileTree } from "../documents/useFiles";
import { apiUrl } from "./mailbox-api";
import { formatBytes, linkTypeLabel } from "./mail-shared";
import { useSaveToFilesForm } from "./useMailForms";

// Attachment tiles on a message: preview, download, and "Save to files" (promote to a Document on a record).

const UNAVAILABLE = {
  blocked: "Blocked file type. It was not stored, for safety. Open it in webmail if you trust the sender.",
  too_large: "Too large to import. Open it in webmail.",
};

const asDocument = (attachment) => ({ originalFilename: attachment.filename, mimeType: attachment.mimeType });
const savable = (attachment) => attachment.status === "stored" && !attachment.document;

function AttachmentIcon({ attachment, unavailable }) {
  const style = unavailable ? { icon: ShieldAlert, className: "bg-muted text-muted-foreground" } : FILE_KIND_STYLES[fileKind(asDocument(attachment))];
  const Icon = style.icon;
  return <span className={cn("grid size-9 shrink-0 place-items-center rounded-md", style.className)}><Icon className="size-4" /></span>;
}

// Where an already-saved attachment lives: "Saved in PO-… ›".
function savedLocation(document) {
  const last = (document?.path || []).filter((entry) => entry?.node && entry.node !== "root").at(-1);
  return last ? { label: last.label, href: `/documents?node=${encodeURIComponent(last.node)}` } : null;
}

/**
 * <AttachmentList message={message} links={thread.links} canSave={can("documents.upload")} onSaved={reader.refresh} />
 */
export function AttachmentList({ message, links = [], canSave, onSaved }) {
  const [preview, setPreview] = useState(null);
  const [saving, setSaving] = useState(null);
  const attachments = (message.attachments || []).filter((attachment) => !attachment.isInline);
  if (!attachments.length) return null;
  const toSave = attachments.filter(savable);

  return <div className="grid gap-2" aria-label="Attachments">
    <div className="flex flex-wrap items-center justify-between gap-2">
      <p className="text-xs text-muted-foreground">{attachments.length === 1 ? "1 attachment" : `${attachments.length} attachments`} · {formatBytes(attachments.reduce((sum, item) => sum + (item.sizeBytes || 0), 0))}</p>
      {canSave && toSave.length > 1 && <Button variant="outline" size="sm" onClick={() => setSaving(toSave)}><FolderInput /> Save all to files</Button>}
    </div>
    <ul className="grid gap-2 @xl:grid-cols-2 @4xl:grid-cols-3">
      {attachments.map((attachment) => <AttachmentTile key={attachment.id} attachment={attachment} canSave={canSave} onPreview={() => setPreview(attachment)} onSave={() => setSaving([attachment])} />)}
    </ul>
    {preview && <AttachmentPreviewDialog attachment={preview} onClose={() => setPreview(null)} />}
    {saving && <SaveToFilesDialog attachments={saving} links={links} onClose={() => setSaving(null)} onSaved={() => { setSaving(null); onSaved?.(); }} />}
  </div>;
}

function AttachmentTile({ attachment, canSave, onPreview, onSave }) {
  const unavailable = UNAVAILABLE[attachment.status];
  const saved = attachment.document ? savedLocation(attachment.document) : null;
  const download = apiUrl(attachment.downloadUrl);

  return <li className={cn("flex min-w-0 items-center gap-2.5 rounded-lg border bg-card p-2 pr-1", unavailable && "bg-muted/40")}>
    <AttachmentIcon attachment={attachment} unavailable={unavailable} />
    <div className="grid min-w-0 flex-1 gap-0.5">
      {unavailable || !attachment.previewable
        ? <span className={cn("truncate text-sm font-medium", unavailable && "text-muted-foreground")} title={attachment.filename}>{attachment.filename}</span>
        : <button type="button" className="truncate text-left text-sm font-medium hover:underline focus-visible:underline focus-visible:outline-none" title={`Preview ${attachment.filename}`} onClick={onPreview}>{attachment.filename}</button>}
      <span className="truncate text-xs text-muted-foreground">
        {unavailable ? unavailable : <>
          {formatBytes(attachment.sizeBytes)}
          {saved && <> · <Link href={saved.href} className="inline-flex items-center hover:text-foreground hover:underline">Saved in {saved.label}<ChevronRight className="size-3" /></Link></>}
          {attachment.document && !saved && " · Saved to files"}
        </>}
      </span>
    </div>
    {!unavailable && <div className="flex shrink-0 items-center">
      {attachment.previewable && <Tooltip>
        <TooltipTrigger asChild><Button variant="ghost" size="icon-sm" aria-label={`Preview ${attachment.filename}`} onClick={onPreview}><Eye /></Button></TooltipTrigger>
        <TooltipContent>Preview</TooltipContent>
      </Tooltip>}
      <Tooltip>
        <TooltipTrigger asChild><Button variant="ghost" size="icon-sm" asChild><a href={download} download={attachment.filename} aria-label={`Download ${attachment.filename}`}><Download /></a></Button></TooltipTrigger>
        <TooltipContent>Download</TooltipContent>
      </Tooltip>
      {canSave && savable(attachment) && <Tooltip>
        <TooltipTrigger asChild><Button variant="ghost" size="icon-sm" aria-label={`Save ${attachment.filename} to files`} onClick={onSave}><FolderInput /></Button></TooltipTrigger>
        <TooltipContent>Save to files</TooltipContent>
      </Tooltip>}
    </div>}
  </li>;
}

function AttachmentPreviewDialog({ attachment, onClose }) {
  const src = apiUrl(attachment.previewUrl);
  const image = (attachment.mimeType || "").startsWith("image/");
  return <Dialog open onOpenChange={(open) => { if (!open) onClose(); }}>
    <DialogContent className="flex max-h-[calc(100dvh-2rem)] flex-col sm:max-w-4xl">
      <DialogHeader>
        <DialogTitle className="truncate pr-6">{attachment.filename}</DialogTitle>
        <DialogDescription>{formatBytes(attachment.sizeBytes)} · Preview from the email. Download it or save it to files to keep it.</DialogDescription>
      </DialogHeader>
      <div className="min-h-0 flex-1 overflow-auto rounded-lg border bg-muted/40">
        {image
          ? <img src={src} alt={attachment.filename} className="mx-auto max-h-[70dvh] w-auto object-contain" />
          : <iframe src={src} title={`Preview of ${attachment.filename}`} className="h-[70dvh] w-full border-0" sandbox="allow-same-origin" />}
      </div>
      <DialogFooter>
        <Button variant="outline" onClick={onClose}>Close</Button>
        <Button asChild><a href={apiUrl(attachment.downloadUrl)} download={attachment.filename}><Download /> Download</a></Button>
      </DialogFooter>
    </DialogContent>
  </Dialog>;
}

const OTHER = "__other__";
const TARGET_ORDER = ["shipment", "inspection", "purchase_order", "invoice", "letter_of_credit", "payment", "quote", "cost_sheet", "factory_quote", "style", "inquiry", "buyer", "factory"];
const rank = (type) => (TARGET_ORDER.includes(type) ? TARGET_ORDER.indexOf(type) : TARGET_ORDER.length);

/**
 * Pick the record to file under (the conversation's linked records first, or any record from the file tree),
 * an optional category and folder, then POST /mail/attachments/save-to-files.
 */
function SaveToFilesDialog({ attachments, links, onClose, onSaved }) {
  // Most specific record first (the order or shipment beats the buyer), so the default is usually right.
  const targets = useMemo(() => links
    .map((link) => ({ node: recordNode(link.type, link.code), label: link.code || link.label, type: link.type }))
    .filter((target) => target.node)
    .sort((a, b) => rank(a.type) - rank(b.type)), [links]);
  const defaultTarget = targets[0] || null;
  const { form, submit } = useSaveToFilesForm({ attachments, defaultTarget, onSaved });
  const { categories } = useFileCategories();
  const [choice, setChoice] = useState(defaultTarget ? defaultTarget.node : OTHER);
  const node = form.watch("node");
  const isSubmitting = form.formState.isSubmitting;
  const nodeError = form.formState.errors.node?.message;
  const label = attachments.length === 1 ? attachments[0].filename : `${attachments.length} attachments`;

  const pick = (value) => {
    setChoice(value);
    const target = targets.find((entry) => entry.node === value);
    form.setValue("node", target ? target.node : "", { shouldValidate: false });
    form.setValue("targetLabel", target ? target.label : "");
    form.clearErrors("node");
  };

  return <Dialog open onOpenChange={(open) => { if (!open && !isSubmitting) onClose(); }}>
    <DialogContent className="flex max-h-[calc(100dvh-2rem)] flex-col sm:max-w-lg">
      <form onSubmit={(event) => { event.stopPropagation(); submit(event); }} className="flex min-h-0 flex-col gap-4" noValidate>
        <DialogHeader>
          <DialogTitle>Save to files</DialogTitle>
          <DialogDescription className="break-words">File {label} under a record so the team finds it with the order, not only in this email.</DialogDescription>
        </DialogHeader>
        <FormErrorAlert form={form} />
        <div className="grid min-h-0 gap-4 overflow-y-auto">
          <fieldset className="grid gap-2">
            <legend className="mb-2 text-sm font-medium">Save under</legend>
            <RadioGroup value={choice} onValueChange={pick} className="gap-1.5">
              {targets.map((target) => <Label key={target.node} htmlFor={`target-${target.node}`} className="flex cursor-pointer items-center gap-2.5 rounded-md border px-3 py-2 font-normal has-[[data-state=checked]]:border-primary/40 has-[[data-state=checked]]:bg-primary/5">
                <RadioGroupItem id={`target-${target.node}`} value={target.node} />
                <span className="min-w-0 truncate"><span className="font-medium">{target.label}</span> <span className="text-muted-foreground">· {linkTypeLabel(target.type)}</span></span>
              </Label>)}
              <Label htmlFor="target-other" className="flex cursor-pointer items-center gap-2.5 rounded-md border px-3 py-2 font-normal has-[[data-state=checked]]:border-primary/40 has-[[data-state=checked]]:bg-primary/5">
                <RadioGroupItem id="target-other" value={OTHER} />
                <span className="flex items-center gap-1.5"><FolderOpen className="size-4 text-muted-foreground" /> {targets.length ? "Another record…" : "Choose a record…"}</span>
              </Label>
            </RadioGroup>
            {choice === OTHER && <OtherRecordPicker selected={node} onPick={(item) => {
              form.setValue("node", item?.node || "");
              form.setValue("targetLabel", item?.label || "");
              if (item?.folder) form.setValue("folder", item.folder);
              if (item) form.clearErrors("node");
            }} />}
            {nodeError && <p className="text-sm text-destructive" role="alert">{nodeError}</p>}
          </fieldset>
          <FieldGroup className="gap-4">
            <SelectField control={form.control} name="category" label="Category" options={categories || []} allowEmpty emptyLabel="No category" placeholder="No category" description="Helps the file show up in checklists and filters." />
            <TextField control={form.control} name="folder" label="Folder (optional)" placeholder="e.g. Buyer comments" description="A folder inside the record's files. Leave empty to file at the top." />
          </FieldGroup>
        </div>
        <DialogFooter>
          <Button type="button" variant="outline" onClick={onClose} disabled={isSubmitting}>Cancel</Button>
          <Button type="submit" disabled={isSubmitting}>{isSubmitting ? <Spinner /> : <FolderInput />} {attachments.length === 1 ? "Save file" : `Save ${attachments.length} files`}</Button>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}

function OtherRecordPicker({ selected, onPick }) {
  const tree = useFileTree("root");
  const [picked, setPicked] = useState(null);
  const uploadable = Boolean(picked && (picked.uploadable || picked.folder));
  return <div className="grid gap-1.5">
    <div className="h-64 min-h-0 overflow-y-auto rounded-md border p-1">
      <FileTree tree={tree} selectedNode={picked?.node || selected} selectedFolder={picked?.folder} dense label="Choose a record" onSelect={(item) => {
        setPicked(item);
        onPick(item.uploadable || item.folder ? item : null);
      }} />
    </div>
    <p className="text-xs text-muted-foreground" aria-live="polite">
      {!picked ? "Pick a buyer, inquiry, style, PO, shipment or other record." : uploadable ? <>Saving to <strong className="font-medium text-foreground">{picked.label}</strong>.</> : <>{picked.label} groups records. Open it and pick a record inside.</>}
    </p>
  </div>;
}
