'use client';

import { useMemo, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { Loader2, Plus, Trash2, Upload } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { DatePicker } from '@/components/ui/date-picker';
import { FileInputButton } from '@/components/ui/file-input-button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { formatBerthRange } from '@/lib/templates/berth-range';

type SignatoryRole = 'client' | 'developer' | 'rep' | 'witness' | 'cc';

interface SignatoryRow {
  name: string;
  email: string;
  role: SignatoryRole;
}

const ROLE_LABELS: Record<SignatoryRole, string> = {
  client: 'Client',
  developer: 'Developer',
  rep: 'Rep',
  witness: 'Witness',
  cc: 'CC (no signing)',
};
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';

interface Props {
  open: boolean;
  onOpenChange: (next: boolean) => void;
  interestId: string;
  onSuccess?: () => void;
}

export function ExternalEoiUploadDialog({ open, onOpenChange, interestId, onSuccess }: Props) {
  const qc = useQueryClient();
  const [file, setFile] = useState<File | null>(null);
  const [title, setTitle] = useState('');
  const [signedAt, setSignedAt] = useState(() => new Date().toISOString().slice(0, 10));
  // `null` means "rep hasn't touched the list yet — show the
  // derived-from-interest seed". Once edited (add/remove/change),
  // the explicit array takes over. Avoids a setState-in-effect that
  // the React Compiler bans.
  const [signatoriesOverride, setSignatoriesOverride] = useState<SignatoryRow[] | null>(null);
  const [notes, setNotes] = useState('');

  // Fetched on open to power the default title:
  // "External EOI — <Client> — <berth range> — YYYY-MM-DD". Without
  // this the file lands as just "External EOI - <date>" which is
  // unscannable in any list when a port has multiple deals closing on
  // the same day. Also drives auto-fill on signatory rows tagged
  // role=client.
  const { data: interestData } = useQuery<{
    data: { clientName: string | null; clientPrimaryEmail: string | null };
  }>({
    queryKey: ['interests', interestId],
    queryFn: () =>
      apiFetch<{ data: { clientName: string | null; clientPrimaryEmail: string | null } }>(
        `/api/v1/interests/${interestId}`,
      ),
    enabled: open,
    staleTime: 60_000,
  });

  // Compute the effective signatory list — when the rep hasn't touched
  // anything, seed from the interest's client. Once they edit, the
  // explicit override takes over.
  const signatories: SignatoryRow[] = useMemo(() => {
    if (signatoriesOverride !== null) return signatoriesOverride;
    if (!interestData?.data) return [];
    return [
      {
        name: interestData.data.clientName ?? '',
        email: interestData.data.clientPrimaryEmail ?? '',
        role: 'client' as const,
      },
    ];
  }, [signatoriesOverride, interestData]);
  const { data: berthsData } = useQuery<{ data: Array<{ mooringNumber: string | null }> }>({
    queryKey: ['interests', interestId, 'berths'],
    queryFn: () =>
      apiFetch<{ data: Array<{ mooringNumber: string | null }> }>(
        `/api/v1/interests/${interestId}/berths`,
      ),
    enabled: open,
    staleTime: 60_000,
  });

  const defaultTitle = useMemo(() => {
    const date = signedAt || new Date().toISOString().slice(0, 10);
    const moorings = (berthsData?.data ?? [])
      .map((b) => b.mooringNumber)
      .filter((m): m is string => !!m);
    const berthLabel = moorings.length > 0 ? formatBerthRange(moorings) : null;
    const clientName = interestData?.data?.clientName ?? null;
    const parts = ['External EOI'];
    if (clientName) parts.push(clientName);
    if (berthLabel) parts.push(berthLabel);
    parts.push(date);
    return parts.join(' — ');
  }, [interestData, berthsData, signedAt]);

  const mutation = useMutation<{ data?: { stageChanged?: boolean } }, Error, void>({
    mutationFn: async () => {
      if (!file) throw new Error('No file selected');
      const form = new FormData();
      form.append('file', file);
      const effectiveTitle = title.trim() || defaultTitle;
      if (effectiveTitle) form.append('title', effectiveTitle);
      if (signedAt) form.append('signedAt', signedAt);
      const cleanSignatories = signatories
        .map((s) => ({ name: s.name.trim(), email: s.email.trim(), role: s.role }))
        .filter((s) => s.name && s.email);
      if (cleanSignatories.length > 0) {
        form.append('signatories', JSON.stringify(cleanSignatories));
        // Back-compat for any consumer that still reads signerNames.
        form.append('signerNames', cleanSignatories.map((s) => s.name).join(', '));
      }
      if (notes) form.append('notes', notes);
      const res = await fetch(`/api/v1/interests/${interestId}/external-eoi`, {
        method: 'POST',
        body: form,
        credentials: 'include',
      });
      if (!res.ok) {
        const err = (await res.json().catch(() => ({ error: 'Upload failed' }))) as {
          error?: string;
        };
        throw new Error(err.error ?? 'Upload failed');
      }
      return (await res.json()) as { data?: { stageChanged?: boolean } };
    },
    onSuccess: (response) => {
      const stageChanged = response?.data?.stageChanged === true;
      toast.success(
        stageChanged
          ? 'External EOI uploaded. Stage advanced to EOI Signed.'
          : 'External EOI uploaded. Filed against this deal (stage unchanged).',
      );
      qc.invalidateQueries({ queryKey: ['interests', interestId] });
      qc.invalidateQueries({ queryKey: ['interests'] });
      qc.invalidateQueries({ queryKey: ['documents'] });
      setFile(null);
      setTitle('');
      setSignatoriesOverride(null);
      setNotes('');
      onOpenChange(false);
      onSuccess?.();
    },
    onError: (err: unknown) => {
      toastError(err, 'Upload failed');
    },
  });

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Upload externally-signed EOI</DialogTitle>
          <DialogDescription>
            For EOIs signed outside our signing service (paper, in person, alternate e-sign vendor).
            The uploaded PDF is filed against this interest and the pipeline stage is advanced to
            EOI Signed.
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-3 py-2">
          <div>
            <Label>PDF file *</Label>
            <div className="mt-1">
              <FileInputButton
                accept="application/pdf,.pdf"
                label={file ? file.name : 'Choose PDF'}
                onFilesPicked={(files) => setFile(files[0] ?? null)}
              />
            </div>
          </div>
          <div>
            <Label>Title (optional)</Label>
            <Input
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder={defaultTitle}
              className="mt-1"
            />
            <p className="text-xs text-muted-foreground mt-1">
              Leave blank to use the default shown above.
            </p>
          </div>
          <div>
            <Label>Date signed</Label>
            <div className="mt-1">
              <DatePicker value={signedAt} onChange={setSignedAt} toDate={new Date()} />
            </div>
          </div>
          <div className="space-y-2">
            <Label>Signatories</Label>
            <div className="space-y-2">
              {signatories.map((s, i) => (
                <div key={i} className="flex gap-2 items-center">
                  <Input
                    placeholder="Name"
                    value={s.name}
                    onChange={(e) =>
                      setSignatoriesOverride(
                        signatories.map((row, idx) =>
                          idx === i ? { ...row, name: e.target.value } : row,
                        ),
                      )
                    }
                    className="flex-1 min-w-0"
                  />
                  <Input
                    type="email"
                    placeholder="email@example.com"
                    value={s.email}
                    onChange={(e) =>
                      setSignatoriesOverride(
                        signatories.map((row, idx) =>
                          idx === i ? { ...row, email: e.target.value } : row,
                        ),
                      )
                    }
                    className="flex-[2] min-w-0"
                  />
                  <Select
                    value={s.role}
                    onValueChange={(v) =>
                      setSignatoriesOverride(
                        signatories.map((row, idx) =>
                          idx === i ? { ...row, role: v as SignatoryRole } : row,
                        ),
                      )
                    }
                  >
                    <SelectTrigger className="w-36 shrink-0">
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      {(Object.keys(ROLE_LABELS) as SignatoryRole[]).map((r) => (
                        <SelectItem key={r} value={r}>
                          {ROLE_LABELS[r]}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                  <Button
                    type="button"
                    variant="ghost"
                    size="icon"
                    onClick={() =>
                      setSignatoriesOverride(signatories.filter((_, idx) => idx !== i))
                    }
                    aria-label="Remove signatory"
                    className="shrink-0"
                  >
                    <Trash2 className="size-4" aria-hidden />
                  </Button>
                </div>
              ))}
            </div>
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={() =>
                setSignatoriesOverride([...signatories, { name: '', email: '', role: 'client' }])
              }
            >
              <Plus className="size-4 mr-1.5" aria-hidden />
              Add signatory
            </Button>
            <p className="text-xs text-muted-foreground">
              Recorded against the document for the audit trail and the signed-count badge. CC
              recipients aren&apos;t counted as signatories.
            </p>
          </div>
          <div>
            <Label>Notes (optional)</Label>
            <Textarea
              value={notes}
              onChange={(e) => setNotes(e.target.value)}
              placeholder="Where / how this EOI was signed"
              rows={2}
              className="mt-1"
            />
          </div>
        </div>

        <DialogFooter>
          <Button
            variant="outline"
            onClick={() => onOpenChange(false)}
            disabled={mutation.isPending}
          >
            Cancel
          </Button>
          <Button onClick={() => mutation.mutate()} disabled={!file || mutation.isPending}>
            {mutation.isPending ? (
              <Loader2 className="h-3.5 w-3.5 animate-spin mr-1.5" aria-hidden />
            ) : (
              <Upload className="h-3.5 w-3.5 mr-1.5" aria-hidden />
            )}
            Upload
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
