import { useEffect, useRef, useState } from "react";
import {
  Check,
  CheckCircle2,
  ChevronsUpDown,
  FileText,
  ImageIcon,
  Loader2,
  UploadCloud,
  X,
} from "lucide-react";
import type { Client, Invoice } from "@/lib/ledger-types";
import type { AuthPlatform } from "@/lib/auth";
import {
  DOCUMENT_ACCEPT,
  formatFileSize,
  MAX_DOCUMENT_SIZE_MB,
  validateDocumentFile,
} from "@/lib/document-upload";
import { Button } from "@/components/ui/button";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Progress } from "@/components/ui/progress";
import { cn } from "@/lib/utils";

type Phase = "pick" | "preview" | "uploading" | "success";

function fileTypeLabel(file: File): string {
  if (file.type === "application/pdf" || file.name.toLowerCase().endsWith(".pdf")) return "PDF";
  const part = file.type.split("/")[1];
  return part ? part.toUpperCase() : "File";
}

function ClientCombobox({
  clients,
  value,
  onChange,
  disabled,
}: {
  clients: Client[];
  value: string;
  onChange: (id: string) => void;
  disabled?: boolean;
}) {
  const [open, setOpen] = useState(false);
  const selected = clients.find((c) => c.id === value);

  return (
    <Popover open={open} onOpenChange={setOpen} modal>
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant="outline"
          role="combobox"
          aria-expanded={open}
          aria-label="Select client"
          disabled={disabled}
          className="h-10 w-full justify-between gap-2 px-3 font-normal"
        >
          <span className="flex min-w-0 flex-1 items-center gap-2 text-left">
            {selected ? (
              <>
                <Check className="size-4 shrink-0 text-primary" aria-hidden="true" />
                <span className="truncate text-[13px] text-foreground">{selected.name}</span>
              </>
            ) : (
              <span className="truncate text-[13px] text-muted-foreground">
                Search or select client…
              </span>
            )}
          </span>
          <ChevronsUpDown className="size-4 shrink-0 opacity-50" aria-hidden="true" />
        </Button>
      </PopoverTrigger>
      <PopoverContent
        align="start"
        sideOffset={4}
        className="z-[60] w-[var(--radix-popover-trigger-width)] p-0 shadow-md"
        onOpenAutoFocus={(e) => e.preventDefault()}
      >
        <Command>
          <CommandInput placeholder="Search clients…" aria-label="Search clients" />
          <CommandList className="max-h-[min(220px,40vh)]">
            <CommandEmpty className="py-4 text-[13px]">No clients found</CommandEmpty>
            <CommandGroup>
              {clients.map((client) => (
                <CommandItem
                  key={client.id}
                  value={client.name}
                  onSelect={() => {
                    onChange(client.id);
                    setOpen(false);
                  }}
                  className="text-[13px]"
                >
                  <Check
                    className={cn(
                      "size-4 shrink-0",
                      value === client.id ? "opacity-100 text-primary" : "opacity-0",
                    )}
                    aria-hidden="true"
                  />
                  <span className="truncate">{client.name}</span>
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

function SelectedFileCard({
  file,
  previewUrl,
  onRemove,
}: {
  file: File;
  previewUrl: string | null;
  onRemove: () => void;
}) {
  const isImage = file.type.startsWith("image/");

  return (
    <div className="flex items-center gap-3 rounded-lg border border-border bg-surface px-3 py-2.5">
      {isImage && previewUrl ? (
        <img
          src={previewUrl}
          alt=""
          className="size-10 shrink-0 rounded-md object-cover ring-1 ring-border/60"
        />
      ) : (
        <span className="flex size-10 shrink-0 items-center justify-center rounded-md bg-primary-soft text-primary">
          {file.type === "application/pdf" ? (
            <FileText size={18} aria-hidden="true" />
          ) : (
            <ImageIcon size={18} aria-hidden="true" />
          )}
        </span>
      )}
      <div className="min-w-0 flex-1">
        <p className="truncate text-[13px] font-medium text-foreground" title={file.name}>
          {file.name}
        </p>
        <p className="text-[12px] text-muted-foreground">
          {fileTypeLabel(file)} · {formatFileSize(file.size)}
        </p>
      </div>
      <Button
        type="button"
        variant="ghost"
        size="icon"
        className="size-8 shrink-0 text-muted-foreground hover:text-foreground"
        aria-label="Remove file"
        onClick={onRemove}
      >
        <X size={15} />
      </Button>
    </div>
  );
}

export function DocumentUploadDialog({
  open,
  onOpenChange,
  platform,
  clients,
  defaultClientId,
  onUpload,
  title = platform === "client" ? "Send a document" : "Upload document",
  description =
    platform === "client"
      ? "Upload an invoice, receipt, or other document for your accountant."
      : "Upload a document on behalf of a client.",
  submitLabel = platform === "client" ? "Send document" : "Upload document",
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  platform: AuthPlatform;
  clients: Client[];
  defaultClientId?: string | undefined;
  actor: string;
  email: string;
  onUpload: (
    file: File,
    options: { clientId?: string; onProgress?: (pct: number) => void },
  ) => Promise<Invoice>;
  title?: string;
  description?: string;
  submitLabel?: string;
}) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [phase, setPhase] = useState<Phase>("pick");
  const [dragging, setDragging] = useState(false);
  const [file, setFile] = useState<File | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [progress, setProgress] = useState(0);
  const [clientId, setClientId] = useState(defaultClientId ?? clients[0]?.id ?? "");
  const [uploaded, setUploaded] = useState<Invoice | null>(null);

  const needsClient = platform === "firm";
  const canSubmit = Boolean(file && (!needsClient || clientId));
  const isBusy = phase === "uploading";

  useEffect(() => {
    if (!open) {
      setPhase("pick");
      setDragging(false);
      setFile(null);
      setError(null);
      setProgress(0);
      setUploaded(null);
      if (previewUrl) URL.revokeObjectURL(previewUrl);
      setPreviewUrl(null);
      setClientId(defaultClientId ?? clients[0]?.id ?? "");
    } else {
      setClientId(defaultClientId ?? clients[0]?.id ?? "");
    }
  }, [open, defaultClientId, clients]);

  useEffect(() => {
    return () => {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
    };
  }, [previewUrl]);

  const pickFile = (list: FileList | null) => {
    const next = list?.[0];
    if (!next) return;
    const validationError = validateDocumentFile(next);
    if (validationError) {
      setError(validationError);
      return;
    }
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setFile(next);
    setError(null);
    if (next.type.startsWith("image/")) {
      setPreviewUrl(URL.createObjectURL(next));
    } else {
      setPreviewUrl(null);
    }
    setPhase("preview");
  };

  const clearFile = () => {
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
    setFile(null);
    setError(null);
    setPhase("pick");
  };

  const handleSubmit = async () => {
    if (!file || !canSubmit) return;
    setError(null);
    setPhase("uploading");
    setProgress(0);
    try {
      const uploadOptions: { clientId?: string; onProgress?: (pct: number) => void } = {
        onProgress: setProgress,
      };
      if (needsClient && clientId) uploadOptions.clientId = clientId;
      const invoice = await onUpload(file, uploadOptions);
      setUploaded(invoice);
      setPhase("success");
    } catch (err) {
      setPhase("preview");
      setError(
        err instanceof Error
          ? err.message
          : "We couldn't upload this document. Please try again.",
      );
    }
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className={cn(
          "flex max-h-[min(90vh,680px)] w-[calc(100%-1.5rem)] max-w-[520px] flex-col gap-0 overflow-hidden p-0",
          "sm:w-full",
        )}
      >
        <DialogHeader className="shrink-0 space-y-1.5 px-6 pt-6 pb-2 text-left">
          <DialogTitle>{phase === "success" ? "Document sent" : title}</DialogTitle>
          <DialogDescription>
            {phase === "success"
              ? "We've received your document and will process it shortly."
              : description}
          </DialogDescription>
        </DialogHeader>

        <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 py-4">
          {phase === "success" && uploaded ? (
            <div className="flex flex-col items-center rounded-xl border border-brand/25 bg-brand-soft/40 px-4 py-8 text-center">
              <span className="flex size-11 items-center justify-center rounded-full bg-surface text-brand shadow-sm">
                <CheckCircle2 size={22} aria-hidden="true" />
              </span>
              <p className="mt-3 max-w-full truncate text-[15px] font-semibold text-foreground">
                {uploaded.file}
              </p>
              <p className="mt-1 text-[13px] text-muted-foreground">Status: Processing</p>
            </div>
          ) : null}

          {phase === "uploading" && file ? (
            <div className="space-y-3" role="status" aria-live="polite">
              <div className="flex items-center gap-3 rounded-lg border border-border bg-muted/30 px-3 py-3">
                <Loader2 className="size-5 shrink-0 animate-spin text-brand" aria-hidden="true" />
                <div className="min-w-0">
                  <p className="text-[14px] font-semibold text-foreground">Uploading…</p>
                  <p className="truncate text-[13px] text-muted-foreground">{file.name}</p>
                </div>
              </div>
              <Progress value={progress || undefined} className="h-1.5" />
              {progress > 0 ? (
                <p className="text-center text-[12px] text-muted-foreground">{progress}%</p>
              ) : null}
            </div>
          ) : null}

          {(phase === "pick" || phase === "preview") && (
            <div className="space-y-5">
              {needsClient ? (
                <div className="space-y-2">
                  <Label htmlFor="upload-client-combobox">Client</Label>
                  <ClientCombobox
                    clients={clients}
                    value={clientId}
                    onChange={setClientId}
                    disabled={isBusy}
                  />
                </div>
              ) : null}

              <div className="space-y-2">
                <Label>Document</Label>

                {!file ? (
                  <div
                    className={cn(
                      "flex flex-col items-center justify-center rounded-lg border border-dashed px-4 py-6 text-center transition-colors",
                      dragging
                        ? "border-brand bg-brand-soft/40"
                        : "border-border bg-muted/20 hover:border-brand/30",
                    )}
                    onDragOver={(e) => {
                      e.preventDefault();
                      setDragging(true);
                    }}
                    onDragLeave={() => setDragging(false)}
                    onDrop={(e) => {
                      e.preventDefault();
                      setDragging(false);
                      pickFile(e.dataTransfer.files);
                    }}
                  >
                    <span className="flex size-10 items-center justify-center rounded-full bg-brand-soft text-brand-foreground">
                      <UploadCloud size={20} aria-hidden="true" />
                    </span>
                    <p className="mt-2.5 text-[13px] font-medium text-foreground">
                      {dragging ? "Drop your document here" : "Drag & drop your document"}
                    </p>
                    <p className="mt-0.5 text-[12px] text-muted-foreground">or choose a file</p>
                    <Button
                      type="button"
                      variant="outline"
                      size="sm"
                      className="mt-3"
                      onClick={() => inputRef.current?.click()}
                    >
                      Choose document
                    </Button>
                    <p className="mt-2.5 text-[11.5px] text-muted-foreground">
                      PDF, JPG, PNG or WEBP · Max {MAX_DOCUMENT_SIZE_MB} MB
                    </p>
                  </div>
                ) : (
                  <SelectedFileCard file={file} previewUrl={previewUrl} onRemove={clearFile} />
                )}
              </div>

              {error ? (
                <p className="text-[13px] font-medium text-destructive" role="alert">
                  {error}
                </p>
              ) : null}

              <input
                ref={inputRef}
                type="file"
                accept={DOCUMENT_ACCEPT}
                className="sr-only"
                aria-label="Choose document to upload"
                onChange={(e) => {
                  pickFile(e.target.files);
                  e.target.value = "";
                }}
              />
            </div>
          )}
        </div>

        <DialogFooter className="shrink-0 gap-2 border-t border-border px-6 py-4 sm:justify-end">
          {phase === "success" ? (
            <Button type="button" onClick={() => onOpenChange(false)}>
              Done
            </Button>
          ) : phase === "uploading" ? (
            <Button type="button" variant="outline" disabled>
              Cancel
            </Button>
          ) : (
            <>
              <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
                Cancel
              </Button>
              <Button
                type="button"
                disabled={!canSubmit || isBusy}
                onClick={() => void handleSubmit()}
              >
                {submitLabel}
              </Button>
            </>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
