"use client";

import { useRef, useState } from "react";
import Link from "next/link";
import { Controller } from "react-hook-form";
import { AlertTriangle, ChevronDown, FileText, Link2, Mail, Paperclip, Plus, Send, Upload, X } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Progress } from "@/components/ui/progress";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { FormErrorAlert } from "@/components/app/form-fields";
import { RichTextEditor } from "@/components/app/rich-text-editor";
import { cn } from "@/lib/utils";
import { VerifiedOnly, useEmailVerified } from "../auth/verified-only";
import { EmptyState, ErrorState } from "../components/ui/page-states";
import { humanize } from "../lib/format";
import { useUnsavedChangesGuard } from "../lib/useUnsavedChangesGuard";
import { FilePickerDialog } from "../documents/file-picker";
import { closeCompose, useComposeStore } from "./compose-store";
import { HtmlPreview } from "./html-preview";
import { formatSize, MAX_ATTACHMENT_BYTES, RECORD_TYPE_LABELS } from "./mail-utils";
import { RecipientInput } from "./recipient-input";
import { hookField, useCompose } from "./useCompose";
import { useRecordLinkSearch } from "./useRecordLinkSearch";

/**
 * Mounted once in the app shell. Any screen opens compose with openCompose({ action, context, replyTo, forward, draftId, accountId }).
 * The form mounts fresh for every open, so each email starts from its own pre-filled context.
 */
export function ComposeHost() {
  const request = useComposeStore((state) => state.request);
  const seq = useComposeStore((state) => state.seq);
  const closeRef = useRef(null);

  return <Sheet open={Boolean(request)} onOpenChange={(next) => { if (!next) (closeRef.current || closeCompose)(); }}>
    {request && <ComposeSheet key={seq} request={request} closeRef={closeRef} />}
  </Sheet>;
}

const TITLES = { reply: "Reply", forward: "Forward", draft: "Edit draft", failed: "Edit and resend", new: "New email" };

function ComposeSheet({ request, closeRef }) {
  const compose = useCompose(request, { onDone: closeCompose });
  const { form, values, context, busy } = compose;
  const isDirty = form.formState.isDirty;
  const guard = useUnsavedChangesGuard(isDirty && !busy, { interceptLinks: false });
  const [askClose, setAskClose] = useState(false);
  const [showCc, setShowCc] = useState(false);
  const [showBcc, setShowBcc] = useState(false);
  const [pickerOpen, setPickerOpen] = useState(false);
  const uploadRef = useRef(null);
  const editorRef = useRef(null);
  const mode = request.draftId ? (compose.draftStatus === "failed" ? "failed" : "draft") : request.forward ? "forward" : request.replyTo ? "reply" : "new";
  const pending = Boolean(busy);
  const verified = useEmailVerified();

  // Closing with unsaved work asks: save as draft, discard, or keep editing.
  const requestClose = () => {
    if (pending) return;
    if (isDirty) setAskClose(true);
    else closeCompose();
  };
  closeRef.current = requestClose;

  const onKeyDown = (event) => {
    if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
      event.preventDefault();
      // Same rule as the Send button: an unverified account cannot send (the button says why).
      if (!pending && verified) compose.send();
    }
  };

  const errors = form.formState.errors;
  const hasCc = showCc || values.cc?.length > 0;
  const hasBcc = showBcc || values.bcc?.length > 0;
  const noMailbox = !compose.isLoading && !compose.loadError && !compose.accountOptions.length;

  return <SheetContent
    side="right"
    showCloseButton={false}
    className="w-full gap-0 p-0 sm:max-w-3xl"
    // Esc first closes an open recipient suggestion list; only then does it ask to close compose.
    onEscapeKeyDown={(event) => { event.preventDefault(); if (event.target?.closest?.("[data-recipient-open]")) return; requestClose(); }}
    onInteractOutside={(event) => { event.preventDefault(); requestClose(); }}
    onKeyDown={onKeyDown}
  >
    <SheetHeader className="relative border-b px-4 py-3 pr-12 sm:px-6">
      <SheetTitle className="flex items-center gap-2 text-lg"><Mail className="size-5 text-muted-foreground" aria-hidden="true" />{TITLES[mode]}</SheetTitle>
      <SheetDescription>{context?.action && context.action !== "record.email" ? `${actionLabel(context.action)} · review everything, then send.` : "Review the recipients, message and files, then send."}</SheetDescription>
      <Button type="button" variant="ghost" size="icon-sm" className="absolute top-3 right-3" aria-label="Close compose" onClick={requestClose}><X /></Button>
    </SheetHeader>

    {compose.isLoading
      ? <ComposeSkeleton />
      : compose.loadError
        ? <div className="p-6"><ErrorState error={compose.loadError} title="The email could not be prepared" onRetry={compose.retry} compact /></div>
        : noMailbox
          ? <div className="p-6"><EmptyState icon={Mail} title="Connect a mailbox first" detail="Emails go out from a mailbox you can send from. Connect your own, or ask an admin to give you send access to a shared one."
            action={<Button asChild size="sm"><Link href="/settings/email-accounts" onClick={() => closeCompose()}>Set up a mailbox</Link></Button>} /></div>
          : <form id="compose-form" onSubmit={compose.send} noValidate className="flex min-h-0 flex-1 flex-col">
            <div className="grid flex-1 content-start gap-4 overflow-y-auto px-4 py-4 sm:px-6">
              <FormErrorAlert form={form} />
              {compose.warnings.length > 0 && <Alert className="border-warning/40 bg-warning/5 [&>svg]:text-warning">
                <AlertTriangle />
                <AlertTitle>Check before sending</AlertTitle>
                <AlertDescription><ul className="list-disc pl-4">{compose.warnings.map((warning) => <li key={warning}>{warning}</li>)}</ul></AlertDescription>
              </Alert>}

              <div className="grid gap-3 rounded-lg border p-3">
                <AddressRow label="From" htmlFor="compose-from" error={errors.accountId?.message}>
                  <Controller control={form.control} name="accountId" render={({ field, fieldState }) => <Select value={field.value} onValueChange={compose.changeAccount}>
                    <SelectTrigger id="compose-from" className="w-full min-w-0" aria-invalid={fieldState.invalid}><SelectValue placeholder="Choose a mailbox"><span className="truncate">{compose.accountOptions.find((option) => option.value === field.value)?.label}</span></SelectValue></SelectTrigger>
                    <SelectContent>{compose.accountOptions.map((option) => <SelectItem key={option.value} value={option.value}>{option.label}{option.description && <span className="text-xs text-muted-foreground">· {option.description}</span>}</SelectItem>)}</SelectContent>
                  </Select>} />
                </AddressRow>
                <AddressRow label="To" error={errors.to?.message} extra={<div className="flex gap-1">
                  {!hasCc && <Button type="button" variant="ghost" size="xs" onClick={() => setShowCc(true)}>Cc</Button>}
                  {!hasBcc && <Button type="button" variant="ghost" size="xs" onClick={() => setShowBcc(true)}>Bcc</Button>}
                </div>}>
                  <Controller control={form.control} name="to" render={({ field, fieldState }) => <RecipientInput id="compose-to" aria-label="To" value={field.value} onChange={field.onChange} onBlur={field.onBlur} record={request.record} invalid={fieldState.invalid} />} />
                </AddressRow>
                {hasCc && <AddressRow label="Cc" error={errors.cc?.message}>
                  <Controller control={form.control} name="cc" render={({ field, fieldState }) => <RecipientInput id="compose-cc" aria-label="Cc" value={field.value} onChange={field.onChange} onBlur={field.onBlur} record={request.record} invalid={fieldState.invalid} />} />
                </AddressRow>}
                {hasBcc && <AddressRow label="Bcc" error={errors.bcc?.message}>
                  <Controller control={form.control} name="bcc" render={({ field, fieldState }) => <RecipientInput id="compose-bcc" aria-label="Bcc" value={field.value} onChange={field.onChange} onBlur={field.onBlur} record={request.record} invalid={fieldState.invalid} placeholder="Hidden copies" />} />
                </AddressRow>}
                <AddressRow label="Subject" htmlFor="compose-subject" error={errors.subject?.message}>
                  <Controller control={form.control} name="subject" render={({ field, fieldState }) => <Input id="compose-subject" {...field} aria-invalid={fieldState.invalid} placeholder="What is this email about?" />} />
                </AddressRow>
              </div>

              <Field>
                <FieldLabel htmlFor="compose-body" className="sr-only">Message</FieldLabel>
                <Controller control={form.control} name="bodyHtml" render={({ field }) => <RichTextEditor
                  ref={editorRef}
                  id="compose-body"
                  aria-label="Message"
                  value={field.value}
                  onChange={field.onChange}
                  placeholder="Write your message…"
                  minHeight="min-h-56"
                  disabled={busy === "template"}
                  toolbarEnd={compose.templateOptions.length > 0 && <TemplatePicker options={compose.templateOptions} value={values.templateId} busy={busy === "template"} onApply={compose.applyTemplate} />}
                />} />
              </Field>

              {compose.signatureHtml && <div className="grid gap-1 rounded-md border border-dashed px-3 py-2">
                <span className="text-xs text-muted-foreground">Signature · added when sent</span>
                <HtmlPreview html={compose.signatureHtml} title="Signature preview" />
              </div>}

              {context?.quotedHtml && <QuotedText html={context.quotedHtml} control={form.control} />}

              <AttachmentsSection compose={compose} onPick={() => setPickerOpen(true)} onUpload={() => uploadRef.current?.click()} error={errors.attachments?.message || errors.forwardAttachments?.message} />
              <input ref={uploadRef} type="file" multiple hidden onChange={(event) => { compose.uploadAttachments(event.target.files); event.target.value = ""; }} />

              <LinkedRecords links={values.links || []} onAdd={compose.addLink} onRemove={compose.removeLink} />

              {(context?.afterSend || []).length > 0 && <AfterSend hooks={context.afterSend} control={form.control} />}
            </div>

            <div className="flex flex-wrap items-center gap-2 border-t bg-background px-4 py-3 sm:px-6">
              {compose.draftId
                ? <Button type="button" variant="ghost" className="text-destructive hover:text-destructive" disabled={pending} onClick={compose.deleteDraft}>{busy === "discard" && <Spinner />}{compose.draftStatus === "failed" ? "Discard email" : "Delete draft"}</Button>
                : <Button type="button" variant="ghost" disabled={pending} onClick={requestClose}>Discard</Button>}
              <span className="mr-auto hidden text-xs text-muted-foreground lg:inline">Ctrl+Enter to send</span>
              <Button type="button" variant="outline" disabled={pending} onClick={() => compose.saveDraft()}>{busy === "draft" && <Spinner />}Save draft</Button>
              <VerifiedOnly action="send email (you can still save drafts)"><Button type="submit" disabled={pending}>{busy === "send" ? <Spinner /> : <Send />}Send</Button></VerifiedOnly>
            </div>
          </form>}

    {pickerOpen && <FilePickerDialog
      multiple
      node={compose.uploadNode}
      recordLabel={compose.uploadLabel}
      excludeIds={(values.attachments || []).map((item) => item.documentId)}
      confirmLabel={(count) => (count > 1 ? `Attach ${count} files` : "Attach file")}
      onClose={() => setPickerOpen(false)}
      onSelectMany={(documents) => { compose.addDocuments(documents); setPickerOpen(false); }}
    />}

    <AlertDialog open={askClose} onOpenChange={setAskClose}>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Save this email as a draft?</AlertDialogTitle>
          <AlertDialogDescription>You have changes that aren&apos;t saved. A draft keeps them under Mail → Drafts.</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Keep editing</AlertDialogCancel>
          <Button type="button" variant="outline" onClick={() => { setAskClose(false); guard.allowNextNavigation(); form.reset(form.getValues()); closeCompose(); }}>Discard</Button>
          <AlertDialogAction onClick={(event) => { event.preventDefault(); setAskClose(false); compose.saveDraft(); }}>Save draft</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  </SheetContent>;
}

function actionLabel(action) {
  const labels = {
    "quote.send": "Send the quotation to the buyer",
    "factory_costing.request": "Ask the factory for costing",
    "po.confirm_to_factory": "Confirm the order to the factory",
    "approval.submit_to_buyer": "Submit the sample to the buyer",
    "inspection.report_to_buyer": "Share the inspection report",
    "shipment.documents_to_buyer": "Send shipping documents to the buyer",
    "invoice.send": "Send the invoice",
    "payment.reminder": "Remind about payment",
  };
  return labels[action] || humanize(action.replaceAll(".", " "));
}

function AddressRow({ label, htmlFor, error, extra, children }) {
  return <div className="grid gap-1 sm:grid-cols-[64px_minmax(0,1fr)_auto] sm:items-start sm:gap-3" data-invalid={Boolean(error) || undefined}>
    <div className="flex items-center justify-between sm:contents">
      {htmlFor ? <Label htmlFor={htmlFor} className="text-muted-foreground sm:pt-2.5">{label}</Label> : <span className="text-sm font-medium text-muted-foreground sm:pt-2.5" aria-hidden="true">{label}</span>}
      {extra && <span className="sm:hidden">{extra}</span>}
    </div>
    <div className="grid min-w-0 gap-1">
      {children}
      {error && <FieldError>{error}</FieldError>}
    </div>
    {extra ? <span className="hidden sm:block sm:pt-1">{extra}</span> : <span className="hidden sm:block" />}
  </div>;
}

function TemplatePicker({ options, value, busy, onApply }) {
  return <Select value={value || ""} onValueChange={onApply} disabled={busy}>
    <SelectTrigger size="sm" className="h-7 w-auto max-w-60 gap-1 border-0 bg-transparent text-xs shadow-none" aria-label="Apply a template">
      {busy ? <Spinner /> : <FileText className="size-3.5!" />}
      <SelectValue placeholder="Template" />
    </SelectTrigger>
    <SelectContent align="end">
      {options.map((option) => <SelectItem key={option.value} value={option.value}>{option.label}{option.description && <Badge variant="secondary" className="ml-1 h-4 px-1 text-xs font-normal">{option.description}</Badge>}</SelectItem>)}
    </SelectContent>
  </Select>;
}

function QuotedText({ html, control }) {
  const [open, setOpen] = useState(false);
  return <Collapsible open={open} onOpenChange={setOpen} className="grid gap-2">
    <div className="flex flex-wrap items-center gap-3">
      <CollapsibleTrigger asChild>
        <Button type="button" variant="ghost" size="sm" className="-ml-2 text-muted-foreground"><ChevronDown className={cn("transition-transform", open && "rotate-180")} />{open ? "Hide quoted text" : "Show quoted text"}</Button>
      </CollapsibleTrigger>
      <Controller control={control} name="includeQuote" render={({ field }) => <div className="flex items-center gap-2">
        <Checkbox id="compose-include-quote" checked={field.value} onCheckedChange={(checked) => field.onChange(checked === true)} />
        <Label htmlFor="compose-include-quote" className="text-sm font-normal">Include the earlier message</Label>
      </div>} />
    </div>
    <CollapsibleContent>
      <div className="rounded-md border bg-muted/30 px-3 py-2"><HtmlPreview html={html} title="Quoted message" /></div>
    </CollapsibleContent>
  </Collapsible>;
}

function AttachmentsSection({ compose, onPick, onUpload, error }) {
  const { values, totalBytes, attachmentReminder, busy } = compose;
  const attachments = values.attachments || [];
  const forwarded = values.forwardAttachments || [];
  const count = attachments.length + forwarded.length;
  const percent = Math.min(100, Math.round((totalBytes / MAX_ATTACHMENT_BYTES) * 100));
  const over = totalBytes > MAX_ATTACHMENT_BYTES;

  return <section className="grid gap-2" aria-label="Attachments">
    <div className="flex flex-wrap items-center gap-2">
      <h3 className="flex items-center gap-2 text-sm font-medium"><Paperclip className="size-4 text-muted-foreground" aria-hidden="true" />Attachments{count > 0 && <Badge variant="secondary" className="h-5 px-1.5 tabular-nums">{count}</Badge>}</h3>
      <div className="ml-auto flex gap-2">
        <Button type="button" variant="outline" size="sm" onClick={onPick}><Plus />Attach from files</Button>
        <Tooltip>
          <TooltipTrigger asChild><Button type="button" variant="outline" size="sm" onClick={onUpload} disabled={busy === "upload"}>{busy === "upload" ? <Spinner /> : <Upload />}Upload</Button></TooltipTrigger>
          <TooltipContent>Saved to {compose.uploadLabel}, then attached</TooltipContent>
        </Tooltip>
      </div>
    </div>
    {count > 0
      ? <ul className="flex flex-wrap gap-2">
        {attachments.map((item) => <AttachmentChip key={`d-${item.documentId}`} title={item.title} meta={[item.version ? `v${item.version}` : null, item.sizeBytes ? formatSize(item.sizeBytes) : null].filter(Boolean).join(" · ")} reason={item.reason} onRemove={() => compose.removeAttachment(item.documentId)} />)}
        {forwarded.map((item) => <AttachmentChip key={`f-${item.id}`} title={item.filename} meta={item.sizeBytes ? formatSize(item.sizeBytes) : ""} reason="From the forwarded email" onRemove={() => compose.removeForwardAttachment(item.id)} />)}
      </ul>
      : <p className="text-sm text-muted-foreground">No files attached. Attach from the file manager, or upload a new file.</p>}
    {count > 0 && <div className="grid gap-1">
      <Progress value={percent} className={cn("h-1.5", over && "*:data-[slot=progress-indicator]:bg-destructive")} aria-label="Attachment size used" />
      <span className={cn("text-xs tabular-nums text-muted-foreground", over && "text-destructive")}>{formatSize(totalBytes)} of 20 MB</span>
    </div>}
    {error && <FieldError>{error}</FieldError>}
    {attachmentReminder && <Alert className="border-warning/40 bg-warning/5 [&>svg]:text-warning">
      <Paperclip />
      <AlertDescription>Your message mentions an attachment, but nothing is attached yet.</AlertDescription>
    </Alert>}
  </section>;
}

function AttachmentChip({ title, meta, reason, onRemove }) {
  return <li className="flex max-w-full min-w-0 items-center gap-2 rounded-md border bg-card py-1 pr-1 pl-2.5">
    <FileText className="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
    <span className="grid min-w-0">
      <span className="truncate text-sm">{title}</span>
      <span className="truncate text-xs text-muted-foreground">{[meta, reason].filter(Boolean).join(" · ")}</span>
    </span>
    <Button type="button" variant="ghost" size="icon-xs" aria-label={`Remove ${title}`} onClick={onRemove}><X /></Button>
  </li>;
}

function LinkedRecords({ links, onAdd, onRemove }) {
  return <section className="grid gap-2" aria-label="Linked records">
    <div className="flex flex-wrap items-center gap-2">
      <h3 className="flex items-center gap-2 text-sm font-medium"><Link2 className="size-4 text-muted-foreground" aria-hidden="true" />Linked records</h3>
      <span className="text-xs text-muted-foreground">The email and its replies show on these records.</span>
      <div className="ml-auto"><LinkRecordButton onAdd={onAdd} /></div>
    </div>
    {links.length
      ? <div className="flex flex-wrap gap-1.5">{links.map((link) => <Badge key={`${link.type}:${link.code}`} variant="secondary" className="h-7 gap-1 rounded-md pr-0.5 pl-2 text-sm font-normal">
        <span className="text-xs text-muted-foreground">{RECORD_TYPE_LABELS[link.type] || humanize(link.type)}</span>{link.code}
        <button type="button" className="grid size-5 place-items-center rounded-sm hover:bg-foreground/10" aria-label={`Unlink ${link.code}`} onClick={() => onRemove(link)}><X className="size-3" /></button>
      </Badge>)}</div>
      : <p className="text-sm text-muted-foreground">Not linked to any record.</p>}
  </section>;
}

function LinkRecordButton({ onAdd }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const { results, isLoading, ready } = useRecordLinkSearch(q, open);

  return <Popover open={open} onOpenChange={(next) => { setOpen(next); if (!next) setQ(""); }}>
    <PopoverTrigger asChild><Button type="button" variant="outline" size="sm"><Plus />Link record</Button></PopoverTrigger>
    <PopoverContent align="end" className="w-80 p-0">
      <Command shouldFilter={false}>
        <CommandInput value={q} onValueChange={setQ} placeholder="PO, shipment, quote code or buyer…" />
        <CommandList>
          {isLoading ? <div className="grid gap-2 p-2">{[0, 1, 2].map((index) => <Skeleton key={index} className="h-8" />)}</div>
            : <CommandEmpty>{ready ? "No record matches." : "Type at least 2 characters."}</CommandEmpty>}
          {results.length > 0 && <CommandGroup>
            {results.map((item) => <CommandItem key={`${item.type}:${item.code}`} value={`${item.type}:${item.code}`} onSelect={() => { onAdd({ type: item.type, code: item.code }); setOpen(false); setQ(""); }}>
              <span className="grid min-w-0">
                <span className="truncate font-medium">{item.code} <span className="font-normal text-muted-foreground">· {RECORD_TYPE_LABELS[item.type] || humanize(item.type)}</span></span>
                {(item.title || item.subtitle) && <span className="truncate text-xs text-muted-foreground">{[item.title, item.subtitle].filter(Boolean).join(" · ")}</span>}
              </span>
            </CommandItem>)}
          </CommandGroup>}
        </CommandList>
      </Command>
    </PopoverContent>
  </Popover>;
}

function AfterSend({ hooks, control }) {
  return <section className="grid gap-2 rounded-lg border bg-muted/30 p-3" aria-label="After sending">
    <h3 className="text-sm font-medium">After sending</h3>
    {hooks.map((hook) => <Controller key={hook.key} control={control} name={`afterSend.${hookField(hook.key)}`} render={({ field }) => <Field orientation="horizontal" className="items-start">
      <Checkbox id={`after-${hook.key}`} checked={Boolean(field.value)} disabled={!hook.available} onCheckedChange={(checked) => field.onChange(checked === true)} />
      <div className="grid gap-0.5">
        <FieldLabel htmlFor={`after-${hook.key}`} className="font-normal">{hook.label}</FieldLabel>
        <FieldDescription>{hook.available ? "Runs only after the mail server accepts the email." : hook.reason || "Not available for this record right now."}</FieldDescription>
      </div>
    </Field>} />)}
  </section>;
}

function ComposeSkeleton() {
  return <div className="grid content-start gap-4 p-4 sm:p-6" aria-busy="true" aria-label="Preparing the email">
    <div className="grid gap-3 rounded-lg border p-3">{[0, 1, 2, 3].map((index) => <div key={index} className="grid grid-cols-[64px_1fr] items-center gap-3"><Skeleton className="h-4 w-10" /><Skeleton className="h-9" /></div>)}</div>
    <Skeleton className="h-64 rounded-md" />
    <div className="flex gap-2"><Skeleton className="h-12 w-48" /><Skeleton className="h-12 w-40" /></div>
  </div>;
}
