import { useEffect, useMemo, useState } from 'react';
import { Plus, Trash2, FileText } from 'lucide-react';
import { Drawer } from '@/components/ui/Drawer';
import { Button } from '@/components/ui/Button';
import { Input, Select, Textarea } from '@/components/ui/Input';
import { useClients } from '@/hooks/useClients';
import { useCases } from '@/hooks/useCases';
import { useTimeEntries } from '@/hooks/useTime';
import { useCreateInvoice, type CreateInvoiceInput } from '@/hooks/useInvoices';
import { formatDate, formatHours, formatMoney, planLimitMessage } from '@/lib/format';
import { cn } from '@/lib/cn';

interface ManualItem {
  description: string;
  quantity: string;
  rate: string;
}

type Mode = 'manual' | 'time';

interface Props {
  open: boolean;
  onClose: () => void;
  initialClientId?: string;
  initialCaseId?: string;
  onCreated?: (invoiceId: string) => void;
}

function defaultDueDate(): string {
  const d = new Date();
  d.setDate(d.getDate() + 30);
  return d.toISOString().slice(0, 10);
}

export function CreateInvoiceDrawer({ open, onClose, initialClientId, initialCaseId, onCreated }: Props) {
  const clients = useClients();
  const cases = useCases();
  const create = useCreateInvoice();

  const [mode, setMode] = useState<Mode>(initialCaseId ? 'time' : 'manual');
  const [clientId, setClientId] = useState(initialClientId ?? '');
  const [caseId, setCaseId] = useState(initialCaseId ?? '');
  const [taxRate, setTaxRate] = useState('0');
  const [dueDate, setDueDate] = useState(defaultDueDate());
  const [notes, setNotes] = useState('');
  const [items, setItems] = useState<ManualItem[]>([{ description: '', quantity: '1', rate: '' }]);
  const [selectedTimeIds, setSelectedTimeIds] = useState<Set<string>>(new Set());

  // Reset on open
  useEffect(() => {
    if (!open) return;
    setMode(initialCaseId ? 'time' : 'manual');
    setClientId(initialClientId ?? '');
    setCaseId(initialCaseId ?? '');
    setTaxRate('0');
    setDueDate(defaultDueDate());
    setNotes('');
    setItems([{ description: '', quantity: '1', rate: '' }]);
    setSelectedTimeIds(new Set());
    create.reset();
    // Only re-run on the open transition (and when the initial ids change).
    // `create` is a fresh object each render; depending on it would re-fire the
    // effect every render and wipe user input ("Maximum update depth exceeded").
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, initialClientId, initialCaseId]);

  // When client changes, clear case selection if the case doesn't belong to that client
  useEffect(() => {
    if (!caseId) return;
    const c = cases.data?.items.find((x) => x.id === caseId);
    if (c && c.clientId !== clientId) setCaseId('');
  }, [clientId, caseId, cases.data]);

  // Pull unbilled time entries for the chosen case (or for any case of the client if no case)
  const unbilledTime = useTimeEntries(
    mode === 'time'
      ? caseId
        ? { caseId, invoiced: 'false' }
        : { invoiced: 'false' }
      : { invoiced: 'false' },
  );

  const filteredEntries = useMemo(() => {
    const all = unbilledTime.data?.items ?? [];
    return all.filter((e) => {
      if (!e.billable) return false;
      if (e.endedAt === null) return false; // skip running timer
      if (clientId && e.clientId !== clientId) return false;
      if (caseId && e.caseId !== caseId) return false;
      return true;
    });
  }, [unbilledTime.data, clientId, caseId]);

  function toggleTime(id: string) {
    setSelectedTimeIds((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  // Live totals preview
  const previewSubtotal = useMemo(() => {
    if (mode === 'manual') {
      return items.reduce((acc, it) => acc + (Number(it.quantity) || 0) * (Number(it.rate) || 0), 0);
    }
    return filteredEntries
      .filter((e) => selectedTimeIds.has(e.id))
      .reduce((acc, e) => acc + (Number(e.rate) || 0) * (e.minutes / 60), 0);
  }, [mode, items, filteredEntries, selectedTimeIds]);

  const previewTotal = previewSubtotal * (1 + (Number(taxRate) || 0) / 100);

  const clientCases = useMemo(
    () => (cases.data?.items ?? []).filter((c) => !clientId || c.clientId === clientId),
    [cases.data, clientId],
  );

  async function onSubmit() {
    if (!clientId) return;
    const payload: CreateInvoiceInput = {
      clientId,
      caseId: caseId || null,
      notes: notes.trim() || null,
      taxRate: Number(taxRate) || 0,
      dueAt: dueDate ? new Date(`${dueDate}T00:00:00`).toISOString() : null,
    };
    if (mode === 'manual') {
      payload.items = items
        .filter((it) => it.description.trim() && Number(it.quantity) > 0 && Number(it.rate) >= 0)
        .map((it) => ({
          description: it.description.trim(),
          quantity: Number(it.quantity),
          rate: Number(it.rate),
        }));
    } else {
      payload.timeEntryIds = Array.from(selectedTimeIds);
    }
    if (!payload.items?.length && !payload.timeEntryIds?.length) return;

    const created = await create.mutateAsync(payload);
    onCreated?.(created.id);
    onClose();
  }

  const apiErr = create.error ? planLimitMessage(create.error.code, 'Could not create the invoice.') : null;

  const canSubmit =
    !!clientId &&
    ((mode === 'manual' &&
      items.some((it) => it.description.trim() && Number(it.quantity) > 0 && Number(it.rate) >= 0)) ||
      (mode === 'time' && selectedTimeIds.size > 0));

  return (
    <Drawer
      open={open}
      onClose={onClose}
      title="New invoice"
      description="Create a draft from time entries or build it manually."
      width="max-w-2xl"
      footer={
        <div className="flex items-center justify-between gap-2">
          <p className="text-sm text-ink-600">
            <span className="text-xs text-ink-500">Total</span>{' '}
            <span className="font-semibold text-ink-900">{formatMoney(previewTotal)}</span>
          </p>
          <div className="flex items-center gap-2">
            <Button variant="secondary" onClick={onClose}>
              Cancel
            </Button>
            <Button onClick={onSubmit} disabled={!canSubmit || create.isPending}>
              {create.isPending ? 'Creating…' : 'Create draft'}
            </Button>
          </div>
        </div>
      }
    >
      <div className="space-y-5">
        <div className="grid gap-4 md:grid-cols-2">
          <Select label="Client" value={clientId} onChange={(e) => setClientId(e.target.value)}>
            <option value="">Select a client…</option>
            {clients.data?.items.map((c) => (
              <option key={c.id} value={c.id}>
                {c.name}
              </option>
            ))}
          </Select>
          <Select label="Case (optional)" value={caseId} onChange={(e) => setCaseId(e.target.value)} disabled={!clientId}>
            <option value="">No case</option>
            {clientCases.map((c) => (
              <option key={c.id} value={c.id}>
                {c.title}
              </option>
            ))}
          </Select>
        </div>

        <div className="flex rounded-xl border border-ink-200 p-1 bg-ink-50/50 w-fit">
          <ModeButton active={mode === 'time'} onClick={() => setMode('time')}>From time entries</ModeButton>
          <ModeButton active={mode === 'manual'} onClick={() => setMode('manual')}>Manual</ModeButton>
        </div>

        {mode === 'time' ? (
          <div className="rounded-xl border border-ink-100">
            <div className="border-b border-ink-100 px-4 py-3 flex items-center justify-between">
              <p className="text-sm font-medium text-ink-700">Unbilled time entries</p>
              <p className="text-xs text-ink-500">{selectedTimeIds.size} selected</p>
            </div>
            {!clientId ? (
              <div className="px-6 py-10 text-center text-sm text-ink-500">Select a client first.</div>
            ) : !filteredEntries.length ? (
              <div className="px-6 py-10 text-center text-sm text-ink-500">
                No unbilled, billable time entries{caseId ? ' for this case' : ' for this client'}.
              </div>
            ) : (
              <ul className="max-h-72 overflow-y-auto divide-y divide-ink-100">
                {filteredEntries.map((e) => {
                  const checked = selectedTimeIds.has(e.id);
                  const amount = (Number(e.rate) || 0) * (e.minutes / 60);
                  return (
                    <li key={e.id}>
                      <label
                        className={cn(
                          'flex items-center gap-3 px-4 py-2.5 cursor-pointer hover:bg-ink-50/50 transition',
                          checked && 'bg-brand-50/50',
                        )}
                      >
                        <input
                          type="checkbox"
                          checked={checked}
                          onChange={() => toggleTime(e.id)}
                          className="h-4 w-4 rounded border-ink-300"
                        />
                        <div className="min-w-0 flex-1">
                          <p className="text-sm text-ink-900 truncate">{e.description}</p>
                          <p className="text-xs text-ink-500 truncate">
                            {e.caseTitle} · {formatDate(e.startedAt)}
                          </p>
                        </div>
                        <p className="text-xs text-ink-500 tabular-nums">{formatHours(e.minutes)}</p>
                        <p className="text-sm font-semibold text-ink-900 tabular-nums w-20 text-right">
                          {formatMoney(amount)}
                        </p>
                      </label>
                    </li>
                  );
                })}
              </ul>
            )}
          </div>
        ) : (
          <div className="rounded-xl border border-ink-100">
            <div className="border-b border-ink-100 px-4 py-3 flex items-center justify-between">
              <p className="text-sm font-medium text-ink-700">Line items</p>
              <Button
                size="sm"
                variant="secondary"
                onClick={() => setItems((s) => [...s, { description: '', quantity: '1', rate: '' }])}
              >
                <Plus className="h-3.5 w-3.5" />
                Add line
              </Button>
            </div>
            <ul className="divide-y divide-ink-100">
              {items.map((it, i) => (
                <li key={i} className="grid grid-cols-12 gap-2 px-4 py-3 items-start">
                  <input
                    placeholder="Description"
                    value={it.description}
                    onChange={(e) => setItems((s) => s.map((x, j) => (j === i ? { ...x, description: e.target.value } : x)))}
                    className="col-span-6 rounded-lg border border-ink-200 px-3 py-2 text-sm focus:outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
                  />
                  <input
                    type="number"
                    min="0"
                    step="0.01"
                    placeholder="Qty"
                    value={it.quantity}
                    onChange={(e) => setItems((s) => s.map((x, j) => (j === i ? { ...x, quantity: e.target.value } : x)))}
                    className="col-span-2 rounded-lg border border-ink-200 px-3 py-2 text-sm text-right focus:outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
                  />
                  <input
                    type="number"
                    min="0"
                    step="0.01"
                    placeholder="Rate"
                    value={it.rate}
                    onChange={(e) => setItems((s) => s.map((x, j) => (j === i ? { ...x, rate: e.target.value } : x)))}
                    className="col-span-3 rounded-lg border border-ink-200 px-3 py-2 text-sm text-right focus:outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
                  />
                  <button
                    type="button"
                    onClick={() => setItems((s) => (s.length > 1 ? s.filter((_, j) => j !== i) : s))}
                    className="col-span-1 grid place-items-center text-ink-400 hover:text-rose-600 h-9"
                    aria-label="Remove line"
                  >
                    <Trash2 className="h-4 w-4" />
                  </button>
                </li>
              ))}
            </ul>
          </div>
        )}

        <div className="grid gap-4 md:grid-cols-2">
          <Input
            label="Tax rate (%)"
            type="number"
            min="0"
            max="100"
            step="0.01"
            value={taxRate}
            onChange={(e) => setTaxRate(e.target.value)}
          />
          <Input
            label="Due date"
            type="date"
            value={dueDate}
            onChange={(e) => setDueDate(e.target.value)}
          />
        </div>

        <Textarea
          label="Notes"
          rows={3}
          placeholder="Payment terms, thank-you note, etc."
          value={notes}
          onChange={(e) => setNotes(e.target.value)}
        />

        {apiErr && <p className="rounded-lg bg-rose-50 px-3 py-2 text-sm text-rose-700">{apiErr}</p>}

        <p className="flex items-start gap-2 text-xs text-ink-500">
          <FileText className="h-3.5 w-3.5 mt-0.5 flex-none" />
          The invoice will be created as a draft. You can review and send it from the invoice page.
        </p>
      </div>
    </Drawer>
  );
}

function ModeButton({
  active,
  onClick,
  children,
}: {
  active: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className={cn(
        'rounded-lg px-3 py-1.5 text-xs font-semibold transition',
        active ? 'bg-white text-ink-900 shadow-sm' : 'text-ink-500 hover:text-ink-800',
      )}
    >
      {children}
    </button>
  );
}
