import { Link, router, useForm } from '@inertiajs/react';
import { GitMerge, Plus, Trash2 } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Dialog } from '@/components/ui/dialog';
import { attachPlaces } from '@/lib/places';
import { PageHeader } from '@/components/PageHeader';
import { StatusBadge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardBody, CardHeader } from '@/components/ui/card';
import { Input, Select, Textarea } from '@/components/ui/input';
import { Field, FieldError } from '@/components/ui/label';
import { AnimatedList, Reveal } from '@/components/motion';
import { Table, Td, Th } from '@/components/ui/table';
import AppLayout from '@/layouts/AppLayout';
import { date, money } from '@/lib/format';
import type { QuoteRow } from '@/types';

type Address = { line1: string; line2: string; suburb: string; state: string; postcode: string; country: string };
type ContactData = {
    id: number; first_name: string; last_name: string; client_company_id: number | null;
    emails: { email: string; primary: boolean }[]; phones: { number: string; type: string }[];
    address: Address; tags: string; notes: string; archived: boolean;
};

const BLANK: Omit<ContactData, 'id' | 'archived'> = {
    first_name: '', last_name: '', client_company_id: null, emails: [{ email: '', primary: true }], phones: [],
    address: { line1: '', line2: '', suburb: '', state: '', postcode: '', country: 'AU' }, tags: '', notes: '',
};
let rowSeq = 0;
const rowKey = () => `r${++rowSeq}`;
const STATES = ['', 'NSW', 'VIC', 'QLD', 'WA', 'SA', 'TAS', 'ACT', 'NT'];

type Option = { id: number; name: string; email: string | null; company: string | null };
type EditProps = {
    contact: ContactData | null; clientCompanies: { id: number; name: string }[];
    quotes: (QuoteRow & { can_edit: boolean })[]; canEdit: boolean;
    duplicates: (Option & { reason: string })[]; mergeOptions: Option[]; placesKey: string | null;
};

/** Keyed by contact so the form starts fresh after "create" redirects to the saved contact. */
export default function ContactEditPage(props: EditProps) {
    return <ContactEdit key={props.contact?.id ?? 'new'} {...props} />;
}

function ContactEdit({ contact, clientCompanies, quotes, canEdit, duplicates, mergeOptions, placesKey }: EditProps) {
    const [merging, setMerging] = useState<number | null | 'pick'>(null);
    const line1 = useRef<HTMLInputElement>(null);
    const isNew = contact === null;
    // _k gives each email / phone row a stable identity so add / remove animations track the right row.
    const form = useForm({
        ...(contact ?? BLANK),
        emails: (contact?.emails.length ? contact.emails : BLANK.emails).map((e) => ({ ...e, _k: rowKey() })),
        phones: (contact?.phones ?? []).map((p) => ({ ...p, _k: rowKey() })),
        new_company_name: '',
    });
    const d = form.data;
    const err = (k: string) => (form.errors as Record<string, string>)[k];
    const name = isNew ? 'New contact' : `${contact.first_name} ${contact.last_name}`.trim();
    const submit = (e: React.FormEvent) => {
        e.preventDefault();
        if (isNew) form.post('/contacts');
        else form.put(`/contacts/${contact.id}`, { preserveScroll: true });
    };
    const addressRef = useRef(d.address);
    addressRef.current = d.address;
    useEffect(() => {
        if (!placesKey || !line1.current || !canEdit) return;
        return attachPlaces(line1.current, placesKey, (a) => form.setData('address', { ...addressRef.current, ...a, line2: addressRef.current.line2 }));
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [placesKey, canEdit]);
    const byCompany = quotes.reduce<Record<string, typeof quotes>>((acc, q) => {
        const k = q.company?.name ?? 'Other';
        (acc[k] ??= []).push(q);
        return acc;
    }, {});

    return (
        <AppLayout title={name}>
            <PageHeader title={name} breadcrumbs={[{ label: 'Contacts', href: '/contacts' }, { label: name }]} description={contact?.archived ? 'Archived' : 'Shared across all three companies'}
                actions={<>
                    <Button variant="secondary" asChild><Link href="/contacts">Back to contacts</Link></Button>
                    {!isNew && canEdit && (
                        <>
                            <Button variant="secondary" asChild><Link href={`/quotes/create?contact_id=${contact.id}`}>New quote</Link></Button>
                            {!contact.archived && <Button variant="secondary" onClick={() => setMerging('pick')} data-testid="merge-contact"><GitMerge className="h-4 w-4" /> Merge…</Button>}
                            <Button variant="secondary" onClick={() => router.post(`/contacts/${contact.id}/archive`, {}, { preserveScroll: true })}>
                                {contact.archived ? 'Restore' : 'Archive'}
                            </Button>
                        </>
                    )}
                </>} />

            <div className="grid gap-6 lg:grid-cols-3">
                <form onSubmit={submit} className="space-y-6 lg:col-span-2" noValidate>
                    <fieldset disabled={!canEdit} className="space-y-6">
                        <Card>
                            <CardHeader title="Details" description={canEdit ? undefined : 'You have read-only access to contacts.'} />
                            <CardBody className="grid gap-4 sm:grid-cols-2">
                                <Field label="First name" htmlFor="first_name" error={err('first_name')}>
                                    <Input id="first_name" value={d.first_name} onChange={(e) => form.setData('first_name', e.target.value)} />
                                </Field>
                                <Field label="Last name" htmlFor="last_name" error={err('last_name')}>
                                    <Input id="last_name" value={d.last_name} onChange={(e) => form.setData('last_name', e.target.value)} />
                                </Field>
                                <Field label="Company" htmlFor="client_company_id" error={err('client_company_id')}>
                                    <Select id="client_company_id" value={d.new_company_name ? '__new' : d.client_company_id ?? ''}
                                        onChange={(e) => {
                                            if (e.target.value === '__new') form.setData({ ...d, client_company_id: null, new_company_name: ' ' });
                                            else form.setData({ ...d, client_company_id: Number(e.target.value) || null, new_company_name: '' });
                                        }}>
                                        <option value="">No company</option>
                                        {clientCompanies.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
                                        <option value="__new">+ Add a new company…</option>
                                    </Select>
                                </Field>
                                <Reveal show={d.new_company_name !== ''}>
                                    <Field label="New company name" htmlFor="new_company_name" error={err('new_company_name')}>
                                        <Input id="new_company_name" autoFocus value={d.new_company_name.trimStart()} onChange={(e) => form.setData('new_company_name', e.target.value || ' ')} />
                                    </Field>
                                </Reveal>
                                <Field label="Tags" htmlFor="tags" hint="Separate with commas." className="sm:col-span-2">
                                    <Input id="tags" value={d.tags} onChange={(e) => form.setData('tags', e.target.value)} />
                                </Field>
                            </CardBody>
                        </Card>

                        <Card>
                            <CardHeader title="Email & phone" />
                            <CardBody className="space-y-4">
                                <AnimatedList>
                                {d.emails.map((em, i) => (
                                    <div key={em._k} className="pb-3">
                                        <div className="flex items-center gap-2">
                                            <Input aria-label={`Email ${i + 1}`} type="email" value={em.email} placeholder="name@example.com"
                                                onChange={(e) => form.setData('emails', d.emails.map((x, j) => (j === i ? { ...x, email: e.target.value } : x)))} />
                                            <label className="flex shrink-0 items-center gap-1.5 text-sm">
                                                <input type="radio" name="primary-email" checked={em.primary} className="accent-[var(--color-link)]"
                                                    onChange={() => form.setData('emails', d.emails.map((x, j) => ({ ...x, primary: j === i })))} /> Primary
                                            </label>
                                            <button type="button" aria-label="Remove email" className="rounded p-1.5 text-ink-muted hover:bg-stone-100"
                                                onClick={() => form.setData('emails', d.emails.filter((_, j) => j !== i))}><Trash2 className="h-4 w-4" /></button>
                                        </div>
                                        <FieldError message={err(`emails.${i}.email`)} />
                                    </div>
                                ))}
                                </AnimatedList>
                                <Button type="button" size="sm" variant="secondary" onClick={() => form.setData('emails', [...d.emails, { email: '', primary: d.emails.length === 0, _k: rowKey() }])}>
                                    <Plus className="h-4 w-4" /> Add email
                                </Button>
                                <div className="border-t border-line pt-4" />
                                <AnimatedList>
                                {d.phones.map((ph, i) => (
                                    <div key={ph._k} className="pb-3">
                                        <div className="flex items-center gap-2">
                                            <Input aria-label={`Phone ${i + 1}`} value={ph.number} placeholder="04xx xxx xxx"
                                                onChange={(e) => form.setData('phones', d.phones.map((x, j) => (j === i ? { ...x, number: e.target.value } : x)))} />
                                            <Select aria-label={`Phone ${i + 1} type`} className="w-28" value={ph.type}
                                                onChange={(e) => form.setData('phones', d.phones.map((x, j) => (j === i ? { ...x, type: e.target.value } : x)))}>
                                                <option value="mobile">Mobile</option><option value="work">Work</option><option value="home">Home</option><option value="other">Other</option>
                                            </Select>
                                            <button type="button" aria-label="Remove phone" className="rounded p-1.5 text-ink-muted hover:bg-stone-100"
                                                onClick={() => form.setData('phones', d.phones.filter((_, j) => j !== i))}><Trash2 className="h-4 w-4" /></button>
                                        </div>
                                        <FieldError message={err(`phones.${i}.number`)} />
                                    </div>
                                ))}
                                </AnimatedList>
                                <Button type="button" size="sm" variant="secondary" onClick={() => form.setData('phones', [...d.phones, { number: '', type: 'mobile', _k: rowKey() }])}>
                                    <Plus className="h-4 w-4" /> Add phone
                                </Button>
                            </CardBody>
                        </Card>

                        <Card>
                            <CardHeader title="Address" />
                            <CardBody className="grid gap-4 sm:grid-cols-2">
                                <Field label="Address" htmlFor="line1" hint={placesKey ? 'Start typing to search addresses.' : undefined}><Input ref={line1} id="line1" autoComplete="off" value={d.address.line1} onChange={(e) => form.setData('address', { ...d.address, line1: e.target.value })} /></Field>
                                <Field label="Address line 2" htmlFor="line2"><Input id="line2" value={d.address.line2} onChange={(e) => form.setData('address', { ...d.address, line2: e.target.value })} /></Field>
                                <Field label="Suburb" htmlFor="suburb"><Input id="suburb" value={d.address.suburb} onChange={(e) => form.setData('address', { ...d.address, suburb: e.target.value })} /></Field>
                                <div className="grid grid-cols-2 gap-4">
                                    <Field label="State" htmlFor="state">
                                        <Select id="state" value={d.address.state} onChange={(e) => form.setData('address', { ...d.address, state: e.target.value })}>
                                            {STATES.map((s) => <option key={s} value={s}>{s || '—'}</option>)}
                                        </Select>
                                    </Field>
                                    <Field label="Postcode" htmlFor="postcode"><Input id="postcode" inputMode="numeric" value={d.address.postcode} onChange={(e) => form.setData('address', { ...d.address, postcode: e.target.value })} /></Field>
                                </div>
                            </CardBody>
                        </Card>

                        <Card>
                            <CardHeader title="Notes" description="Staff only." />
                            <CardBody><Textarea aria-label="Notes" rows={4} value={d.notes} onChange={(e) => form.setData('notes', e.target.value)} /></CardBody>
                        </Card>
                    </fieldset>
                    {canEdit && <Button type="submit" disabled={form.processing} data-testid="save-contact">{isNew ? 'Create contact' : 'Save contact'}</Button>}
                </form>

                {!isNew && (
                    <aside className="space-y-6">
                        {duplicates.length > 0 && canEdit && !contact.archived && (
                            <Card data-testid="duplicates">
                                <CardHeader title="Possible duplicates" description="Merge them into this contact to keep one record." />
                                <CardBody className="space-y-3">
                                    {duplicates.map((c) => (
                                        <div key={c.id} className="flex items-center justify-between gap-3 text-sm">
                                            <div className="min-w-0">
                                                <Link href={`/contacts/${c.id}/edit`} className="link font-medium">{c.name}</Link>
                                                <div className="truncate text-xs text-ink-muted">{c.reason}{c.email ? ` · ${c.email}` : ''}{c.company ? ` · ${c.company}` : ''}</div>
                                            </div>
                                            <Button size="sm" variant="secondary" onClick={() => setMerging(c.id)}>Merge</Button>
                                        </div>
                                    ))}
                                </CardBody>
                            </Card>
                        )}
                        <Card>
                            <CardHeader title="Quotes by company" />
                            {quotes.length === 0 && <CardBody><p className="text-sm text-ink-muted">No quotes for this contact yet.</p></CardBody>}
                            {Object.entries(byCompany).map(([company, rows]) => (
                                <div key={company}>
                                    <div className="border-b border-line bg-stone-50 px-5 py-2 text-xs font-semibold uppercase tracking-wide text-ink-muted">{company}</div>
                                    <Table>
                                        <thead className="sr-only"><tr><Th>Number</Th><Th>Status</Th><Th>Date</Th><Th>Total</Th></tr></thead>
                                        <tbody>
                                            {rows.map((q) => (
                                                <tr key={q.id}>
                                                    <Td className="font-medium">{q.href ? <Link href={q.href} className="link">{q.number}</Link> : q.number}</Td>
                                                    <Td><StatusBadge status={q.status} /></Td>
                                                    <Td className="whitespace-nowrap">{date(q.quote_date)}</Td>
                                                    <Td className="text-right tabular-nums">{q.total_cents === null ? '—' : money(q.total_cents, q.currency)}</Td>
                                                </tr>
                                            ))}
                                        </tbody>
                                    </Table>
                                </div>
                            ))}
                        </Card>
                    </aside>
                )}
            </div>
            {!isNew && merging !== null && (
                <MergeDialog contact={{ id: contact.id, name }} options={mergeOptions} preset={merging === 'pick' ? null : merging} onClose={() => setMerging(null)} />
            )}
        </AppLayout>
    );
}

/** Pick the duplicate to fold into this contact, then confirm. */
function MergeDialog({ contact, options, preset, onClose }: { contact: { id: number; name: string }; options: Option[]; preset: number | null; onClose: () => void }) {
    const [q, setQ] = useState('');
    const [other, setOther] = useState<number | null>(preset);
    const [busy, setBusy] = useState(false);
    const matches = useMemo(() => {
        const t = q.trim().toLowerCase();
        return (t ? options.filter((o) => [o.name, o.email, o.company].some((v) => v?.toLowerCase().includes(t))) : options).slice(0, 30);
    }, [q, options]);
    const picked = options.find((o) => o.id === other);
    return (
        <Dialog open onOpenChange={(o) => !o && onClose()} title={`Merge into ${contact.name}`}
            description="The other contact's quotes (in every company), emails, phones and tags move to this contact, blank fields are filled in, and the other contact is archived.">
            <div className="space-y-3">
                {!picked ? <>
                    <Input aria-label="Find contact to merge" placeholder="Search name, email or company" value={q} onChange={(e) => setQ(e.target.value)} autoFocus />
                    <ul className="max-h-64 divide-y divide-line overflow-auto rounded-md border border-line" role="listbox" aria-label="Contacts">
                        {matches.map((o) => (
                            <li key={o.id}>
                                <button type="button" role="option" aria-selected={false} onClick={() => setOther(o.id)} className="block w-full px-3 py-2 text-left text-sm hover:bg-accent-soft">
                                    <span className="font-medium">{o.name}</span>
                                    <span className="block truncate text-xs text-ink-muted">{[o.email, o.company].filter(Boolean).join(' · ') || '—'}</span>
                                </button>
                            </li>
                        ))}
                        {matches.length === 0 && <li className="px-3 py-3 text-sm text-ink-muted">No contacts match.</li>}
                    </ul>
                </> : (
                    <div className="rounded-md border border-line bg-stone-50 p-3 text-sm">
                        <p><strong>{picked.name}</strong>{picked.email ? ` (${picked.email})` : ''} will be merged into <strong>{contact.name}</strong>. This can't be undone automatically.</p>
                        <button type="button" className="mt-1 text-xs text-link underline" onClick={() => setOther(null)}>Choose someone else</button>
                    </div>
                )}
                <div className="flex justify-end gap-2">
                    <Button variant="secondary" onClick={onClose}>Cancel</Button>
                    <Button disabled={!picked || busy} data-testid="confirm-merge" onClick={() => {
                        setBusy(true);
                        router.post(`/contacts/${contact.id}/merge`, { other_id: other }, { preserveScroll: true, onFinish: () => setBusy(false), onSuccess: onClose });
                    }}>{busy ? 'Merging…' : 'Merge contacts'}</Button>
                </div>
            </div>
        </Dialog>
    );
}
