"use client";

import { useRef, useState } from "react";
import { Controller } from "react-hook-form";
import { Check, CloudUpload, FolderInput, Info, Paperclip, Trash2, Upload } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Progress } from "@/components/ui/progress";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
import { ComboboxField, FormErrorAlert, TextField, TextareaField } from "@/components/app/form-fields";
import { cn } from "@/lib/utils";
import { notify } from "../components/ui/feedback-store";
import { formatBytes, visibilityOptions } from "./file-utils";
import { FileIcon } from "./file-views";
import { FileTree } from "./file-tree";
import { useCategoryForm, useDocumentEditForm, useDocumentSupersedeForm, useUploadFilesForm } from "./useFileManager";
import { useQuickAdd } from "../lib/useQuickAdd";
import { useFileCategories, useFileTree } from "./useFiles";

// Nested dialogs render through portals, but React still bubbles submit events up the component tree;
// stop them so a host page form (e.g. one containing DocumentSelectField) is never submitted by accident.
const isolatedSubmit = (submit) => (event) => { event.stopPropagation(); submit(event); };

// Visibility picker with the explanation of each option.
function VisibilityField({ control, name = "visibility", label = "Visibility" }) {
  return <Controller control={control} name={name} render={({ field, fieldState }) => {
    const selected = visibilityOptions.find((option) => option.value === field.value);
    return <Field data-invalid={Boolean(fieldState.error)}>
      <FieldLabel htmlFor={name}>{label}</FieldLabel>
      <Select value={field.value || ""} onValueChange={field.onChange}>
        <SelectTrigger id={name} className="w-full" aria-invalid={fieldState.invalid} onBlur={field.onBlur}><SelectValue placeholder="Choose visibility" /></SelectTrigger>
        <SelectContent>
          {visibilityOptions.map((option) => <SelectItem key={option.value} value={option.value}><option.icon /> {option.label}</SelectItem>)}
        </SelectContent>
      </Select>
      {fieldState.error ? <FieldError>{fieldState.error.message}</FieldError> : selected && <FieldDescription>{selected.description}</FieldDescription>}
    </Field>;
  }} />;
}

/**
 * Upload into a record folder. node: target node (record), folder: optional manual folder path,
 * targetLabel: what the user sees ("SHP-0003", "H&M"), category: preset category code,
 * initialFiles: files dropped on the explorer. onUploaded(saved[]) runs after every batch.
 */
export function UploadDialog({ node, folder = "", targetLabel, category = "", initialFiles, note = null, onUploaded, onClose }) {
  const { categories } = useFileCategories();
  const addCategory = useQuickAdd("document_type", { valueKey: "code" });
  const { form, submit, fields, addFiles, removeFile, isUploading, progress, failures } = useUploadFilesForm({ node, folder, category, initialFiles, onUploaded, onDone: onClose });
  const [dragging, setDragging] = useState(false);
  const inputRef = useRef(null);
  const files = form.watch("files");
  const totalSize = files.reduce((sum, item) => sum + (item.file?.size || 0), 0);
  const listError = form.formState.errors.files?.root?.message || form.formState.errors.files?.message;

  return <Dialog open onOpenChange={(open) => { if (!open && !isUploading) onClose(); }}>
    <DialogContent className="flex max-h-[calc(100dvh-2rem)] flex-col sm:max-w-2xl">
      <form onSubmit={isolatedSubmit(submit)} className="flex min-h-0 flex-col gap-4" noValidate>
        <DialogHeader>
          <DialogTitle>Upload files</DialogTitle>
          <DialogDescription>
            Filed in <strong className="text-foreground">{targetLabel}</strong>{folder ? <> › <strong className="text-foreground">{folder.split("/").join(" › ")}</strong></> : null}. Visible to your organization; change visibility or notes later from <em>Edit details</em>.
          </DialogDescription>
        </DialogHeader>

        {note && <Alert><Info /><AlertDescription>{note}</AlertDescription></Alert>}

        <div
          role="button"
          tabIndex={0}
          aria-label="Choose files to upload"
          aria-disabled={isUploading}
          className={cn(
            "grid cursor-pointer justify-items-center gap-1.5 rounded-lg border-2 border-dashed p-6 text-center transition-colors outline-none hover:bg-accent/50 focus-visible:ring-[3px] focus-visible:ring-ring/50",
            dragging && "border-primary bg-accent",
            listError && "border-destructive",
            isUploading && "pointer-events-none opacity-60",
          )}
          onDragOver={(event) => { event.preventDefault(); setDragging(true); }}
          onDragLeave={() => setDragging(false)}
          onDrop={(event) => { event.preventDefault(); setDragging(false); addFiles(event.dataTransfer.files); }}
          onClick={() => inputRef.current?.click()}
          onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); inputRef.current?.click(); } }}
        >
          <CloudUpload className="size-8 text-muted-foreground" />
          <strong className="text-sm">Drop files here or click to browse</strong>
          <span className="text-xs text-muted-foreground">PDF, Office documents, images, and ZIP archives. Executable files are blocked.</span>
          <input ref={inputRef} type="file" multiple hidden onChange={(event) => { addFiles(event.target.files); event.target.value = ""; }} />
        </div>
        {listError && <p className="-mt-2 text-sm text-destructive">{listError}</p>}

        <ComboboxField control={form.control} name="category" label="Category" options={categories} allowEmpty emptyLabel="Uncategorized" placeholder="Uncategorized" searchPlaceholder="Search categories…" emptyText="No category matches." create={addCategory} description="Applies to every file in this upload. Helps others find it and pick it as evidence." />

        {fields.length > 0 && <div className="max-h-[40dvh] min-h-0 overflow-y-auto rounded-md border">
          <ul className="divide-y">
            {fields.map((item, index) => {
              const file = files[index]?.file || item.file;
              const titleError = form.formState.errors.files?.[index]?.title?.message;
              const failure = failures.find((entry) => entry.name === file?.name);

              return <li key={item.id} className={cn("flex items-start gap-3 p-3", failure && "bg-destructive/5")}>
                <FileIcon document={{ originalFilename: file?.name, mimeType: file?.type }} />
                <div className="grid min-w-0 flex-1 gap-1">
                  <Input
                    {...form.register(`files.${index}.title`)}
                    aria-label={`Title for ${file?.name}`}
                    aria-invalid={Boolean(titleError)}
                    disabled={isUploading}
                    className="h-8"
                  />
                  <span className="truncate text-xs text-muted-foreground">{file?.name} · {formatBytes(file?.size)}</span>
                  {titleError && <span className="text-xs text-destructive">{titleError}</span>}
                  {failure && <span className="text-xs text-destructive">{failure.message}</span>}
                </div>
                <Button type="button" variant="ghost" size="icon-sm" onClick={() => removeFile(index)} disabled={isUploading} aria-label={`Remove ${file?.name}`}><Trash2 /></Button>
              </li>;
            })}
          </ul>
        </div>}

        {failures.length > 0 && <Alert variant="destructive"><AlertDescription>{failures.length} {failures.length === 1 ? "file" : "files"} could not be uploaded. Fix the issue or remove the file, then try again. Other files were saved.</AlertDescription></Alert>}

        {isUploading && <div className="grid gap-1.5">
          <Progress value={progress.total ? (progress.done / progress.total) * 100 : 0} aria-label="Upload progress" />
          <span className="text-xs text-muted-foreground">Uploading {progress.done} of {progress.total}…</span>
        </div>}

        <DialogFooter className="items-center sm:justify-between">
          <span className="text-xs text-muted-foreground">{files.length ? `${files.length} ${files.length === 1 ? "file" : "files"} · ${formatBytes(totalSize)}` : "No files chosen"}</span>
          <div className="flex flex-col-reverse gap-2 sm:flex-row">
            <Button type="button" variant="outline" onClick={onClose} disabled={isUploading}>Cancel</Button>
            <Button type="submit" disabled={isUploading}>{isUploading ? <Spinner /> : <Upload />} Upload {files.length > 1 ? `${files.length} files` : "file"}</Button>
          </div>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}

export function DocumentEditDialog({ document, documentTypes, onSaved, onClose }) {
  const { form, submit } = useDocumentEditForm(document, onSaved);
  const isSubmitting = form.formState.isSubmitting;
  const typeOptions = documentTypes.map((type) => ({ value: String(type.id), label: type.label }));
  const addCategory = useQuickAdd("document_type");

  return <Dialog open onOpenChange={(open) => { if (!open && !isSubmitting) onClose(); }}>
    <DialogContent className="max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-lg">
      <form onSubmit={isolatedSubmit(submit)} className="grid gap-5" noValidate>
        <DialogHeader>
          <DialogTitle>Edit details</DialogTitle>
          <DialogDescription>Update how <strong className="text-foreground">{document.title}</strong> is labelled and shared. Changes are recorded in the activity log with the previous values.</DialogDescription>
        </DialogHeader>
        <FormErrorAlert form={form} />
        <FieldGroup>
          <TextField control={form.control} name="title" label="Title" />
          <ComboboxField control={form.control} name="documentTypeId" label="Category" options={typeOptions} allowEmpty emptyLabel="Uncategorized" placeholder="Uncategorized" searchPlaceholder="Search categories…" emptyText="No category matches." create={addCategory} description="Optional. Used for the Categories filter and category version numbering." />
          <VisibilityField control={form.control} />
          <TextareaField control={form.control} name="notes" label="Notes" rows={3} placeholder="Context for colleagues, e.g. which comments this file answers" />
        </FieldGroup>
        <DialogFooter>
          <Button type="button" variant="outline" onClick={onClose} disabled={isSubmitting}>Cancel</Button>
          <Button type="submit" disabled={isSubmitting}>{isSubmitting ? <Spinner /> : <Check />} Save details</Button>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}

export function DocumentSupersedeDialog({ document, onSaved, onClose }) {
  const { form, submit } = useDocumentSupersedeForm(document, onSaved);
  const isSubmitting = form.formState.isSubmitting;
  const inputRef = useRef(null);

  return <Dialog open onOpenChange={(open) => { if (!open && !isSubmitting) onClose(); }}>
    <DialogContent className="max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-lg">
      <form onSubmit={isolatedSubmit(submit)} className="grid gap-5" noValidate>
        <DialogHeader>
          <DialogTitle>Upload new version</DialogTitle>
          <DialogDescription>v{document.version} of <strong className="text-foreground">{document.title}</strong> will be marked superseded and kept in history. The new file becomes v{document.version + 1}.</DialogDescription>
        </DialogHeader>
        <FormErrorAlert form={form} />
        <FieldGroup>
          <Controller control={form.control} name="file" render={({ field, fieldState }) => <Field data-invalid={Boolean(fieldState.error)}>
            <FieldLabel htmlFor="supersede-file">New file</FieldLabel>
            <div className="flex items-center gap-2">
              <Button id="supersede-file" type="button" variant="outline" className="min-w-0 flex-1 justify-start font-normal" onClick={() => inputRef.current?.click()} aria-invalid={fieldState.invalid}>
                <Paperclip /><span className={cn("truncate", !field.value && "text-muted-foreground")}>{field.value?.name || "Choose new file"}</span>
              </Button>
              {field.value && <span className="shrink-0 text-xs text-muted-foreground">{formatBytes(field.value.size)}</span>}
            </div>
            <input ref={inputRef} type="file" hidden aria-label="New version file" onChange={(event) => { field.onChange(event.target.files?.[0] || null); event.target.value = ""; }} />
            {fieldState.error ? <FieldError>{fieldState.error.message}</FieldError> : <FieldDescription>Same rules as uploads: PDF, Office documents, images, and ZIP archives.</FieldDescription>}
          </Field>} />
          <TextField control={form.control} name="title" label="Title" />
          <VisibilityField control={form.control} />
          <TextField control={form.control} name="notes" label="What changed?" placeholder="e.g. Updated measurements after fit comments" />
        </FieldGroup>
        <DialogFooter>
          <Button type="button" variant="outline" onClick={onClose} disabled={isSubmitting}>Cancel</Button>
          <Button type="submit" disabled={isSubmitting}>{isSubmitting ? <Spinner /> : <Upload />} Upload v{document.version + 1}</Button>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}

// "Set category…" for the selected files.
export function CategoryDialog({ documents, categorize, onDone, onClose }) {
  const { categories } = useFileCategories();
  const addCategory = useQuickAdd("document_type", { valueKey: "code" });
  const { form, submit } = useCategoryForm(documents, categorize, onDone);
  const isSubmitting = form.formState.isSubmitting;
  const label = documents.length === 1 ? `"${documents[0].title}"` : `${documents.length} files`;

  return <Dialog open onOpenChange={(open) => { if (!open && !isSubmitting) onClose(); }}>
    <DialogContent className="sm:max-w-md">
      <form onSubmit={isolatedSubmit(submit)} className="grid gap-5" noValidate>
        <DialogHeader>
          <DialogTitle>Set category</DialogTitle>
          <DialogDescription>Choose the category for {label}. Categories power the filters and the suggestions when someone picks evidence.</DialogDescription>
        </DialogHeader>
        <FormErrorAlert form={form} />
        <ComboboxField control={form.control} name="category" label="Category" options={categories} allowEmpty emptyLabel="Uncategorized" placeholder="Uncategorized" searchPlaceholder="Search categories…" emptyText="No category matches." create={addCategory} />
        <DialogFooter>
          <Button type="button" variant="outline" onClick={onClose} disabled={isSubmitting}>Cancel</Button>
          <Button type="submit" disabled={isSubmitting}>{isSubmitting ? <Spinner /> : <Check />} Save category</Button>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}

/**
 * "Move to…": pick any record folder (or one of its manual folders) in the tree. The file keeps its id,
 * versions and links; the server moves the whole version chain.
 */
export function MoveFilesDialog({ documents, move, initialNode = "", onDone, onClose }) {
  const tree = useFileTree("root");
  const [target, setTarget] = useState(null);
  const [error, setError] = useState(null);
  const [isMoving, setIsMoving] = useState(false);
  const label = documents.length === 1 ? `"${documents[0].title}"` : `${documents.length} files`;
  const uploadable = Boolean(target && (target.uploadable || target.folder));

  const submit = async (event) => {
    event.preventDefault();
    event.stopPropagation();
    if (!uploadable) return;
    setIsMoving(true);
    setError(null);
    try {
      await move(documents.map((document) => document.id), target.node, target.folder || "");
      notify.success(documents.length === 1 ? "File moved" : `${documents.length} files moved`, `Now filed in ${target.label}. Links to records are unchanged.`);
      onDone?.(target);
    } catch (nextError) {
      const fieldMessage = nextError?.errors ? Object.values(nextError.errors).flat()[0] : null;
      setError(fieldMessage || nextError?.message || "The files could not be moved.");
    } finally {
      setIsMoving(false);
    }
  };

  return <Dialog open onOpenChange={(open) => { if (!open && !isMoving) onClose(); }}>
    <DialogContent className="flex max-h-[calc(100dvh-2rem)] flex-col sm:max-w-lg">
      <form onSubmit={submit} className="flex min-h-0 flex-col gap-4">
        <DialogHeader>
          <DialogTitle>Move to…</DialogTitle>
          <DialogDescription>Choose where {label} should be filed. Moving keeps the same file and its links. Versions move together.</DialogDescription>
        </DialogHeader>
        <div className="h-80 min-h-0 overflow-y-auto rounded-md border p-1">
          <FileTree tree={tree} selectedNode={target?.node} selectedFolder={target?.folder} onSelect={(item) => { setTarget(item); setError(null); }} label="Destination" />
        </div>
        <p className="text-xs text-muted-foreground" aria-live="polite">
          {!target ? "Pick a buyer, inquiry, style, PO, shipment or other record."
            : uploadable ? <>Moving to <strong className="font-medium text-foreground">{target.label}</strong>{target.node === initialNode && !target.folder ? " (where it is now)" : ""}.</>
              : <>{target.label} groups records. Open it and pick a record inside.</>}
        </p>
        {error && <Alert variant="destructive"><AlertDescription>{error}</AlertDescription></Alert>}
        <DialogFooter>
          <Button type="button" variant="outline" onClick={onClose} disabled={isMoving}>Cancel</Button>
          <Button type="submit" disabled={isMoving || !uploadable}>{isMoving ? <Spinner /> : <FolderInput />} Move here</Button>
        </DialogFooter>
      </form>
    </DialogContent>
  </Dialog>;
}
