import { useDeferredValue, useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import type { ColumnDef } from '@tanstack/react-table';
import type { PermissionGrant } from '@pig/core';
import { Building2, ChevronRight, Mail, Pencil, Plus, RefreshCw, Search, UserPlus } from 'lucide-react';
import { Link } from 'react-router-dom';
import { AccountSheet, ContactSheet, type AccountRecord, type ContactRecord, type ContactRow } from '@/components/RecordSheets';
import { DataTable, DataTableColumnHeader } from '@/components/DataTable';
import { SIDE_LABELS, SideBadge } from '@/components/status';
import { Badge, Button, Card, ConfidenceBadge, EmptyState, Input, Skeleton, Stat } from '@/components/ui';
import { PageHeader } from '@/components/ui/page-header';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { get, relativeTime } from '@/lib/api';
import { can } from '@/lib/permissions';
import { usePageTitle } from '@/lib/title';

interface Me { permissions: PermissionGrant[] }
type View = 'accounts' | 'contacts';

export function Accounts() {
  usePageTitle('Accounts');
  const [side, setSide] = useState<'all' | 'supply' | 'demand'>('all');
  const [view, setView] = useState<View>('accounts');
  const [mobileSearch, setMobileSearch] = useState('');
  const deferredSearch = useDeferredValue(mobileSearch.trim().toLocaleLowerCase());
  const [accountSheet, setAccountSheet] = useState<{ open: boolean; record?: AccountRecord }>({ open: false });
  const [contactSheet, setContactSheet] = useState<{ open: boolean; record?: ContactRecord; accountId?: string }>({ open: false });
  const { data: me } = useQuery({ queryKey: ['me'], queryFn: () => get<Me>('/api/me') });
  const accountsQuery = useQuery({ queryKey: ['accounts', side], queryFn: () => get<AccountRecord[]>(`/api/accounts${side === 'all' ? '' : `?side=${side}`}`) });
  const contactsQuery = useQuery({ queryKey: ['contacts', 'table'], queryFn: () => get<ContactRow[]>('/api/contacts') });
  const canDemand = can(me, 'deal:write', 'demand');
  const canSupply = can(me, 'deal:write', 'supply');
  const canAny = canDemand || canSupply;
  const canAccount = (account: AccountRecord) => account.side === 'both' ? canAny : account.side === 'demand' ? canDemand : canSupply;
  const canContact = (row: ContactRow) => row.accountSide === 'both' ? canAny : row.accountSide === 'demand' ? canDemand : row.accountSide === 'supply' ? canSupply : false;
  const visibleAccounts = useMemo(() => !deferredSearch ? accountsQuery.data ?? [] : (accountsQuery.data ?? []).filter((account) => [account.name, account.domain, account.country, account.supplierType, account.customerSegment].some((value) => value?.toLocaleLowerCase().includes(deferredSearch))), [accountsQuery.data, deferredSearch]);
  const visibleContacts = useMemo(() => !deferredSearch ? contactsQuery.data ?? [] : (contactsQuery.data ?? []).filter((row) => [row.contact.fullName, row.contact.email, row.contact.title, row.accountName].some((value) => value?.toLocaleLowerCase().includes(deferredSearch))), [contactsQuery.data, deferredSearch]);

  const accountColumns: ColumnDef<AccountRecord>[] = [
    { id: 'account', accessorFn: (account) => `${account.name} ${account.domain ?? ''}`, header: ({ column }) => <DataTableColumnHeader column={column} title="Account" />, cell: ({ row }) => <div className="min-w-0 max-w-xs"><Link to={`/accounts/${row.original.id}`} className="truncate block font-medium underline-offset-4 hover:text-accent-fg hover:underline">{row.original.name}</Link>{row.original.domain ? <p className="truncate text-xs text-muted">{row.original.domain}</p> : null}</div> },
    { accessorKey: 'side', header: ({ column }) => <DataTableColumnHeader column={column} title="Side" />, cell: ({ row }) => <SideBadge side={row.original.side} /> },
    { id: 'type', accessorFn: (account) => account.supplierType ?? account.customerSegment ?? '', header: ({ column }) => <DataTableColumnHeader column={column} title="Type" />, cell: ({ row }) => { const type = accountType(row.original); return type ? humanise(type) : '—'; } },
    { accessorKey: 'country', header: ({ column }) => <DataTableColumnHeader column={column} title="Country" />, cell: ({ row }) => row.original.country ?? '—' },
    { accessorKey: 'confidence', header: ({ column }) => <DataTableColumnHeader column={column} title="Confidence" />, cell: ({ row }) => <Confidence confidence={row.original.confidence} /> },
    { accessorKey: 'lastActivityAt', header: ({ column }) => <DataTableColumnHeader column={column} title="Last activity" />, cell: ({ row }) => row.original.lastActivityAt ? relativeTime(row.original.lastActivityAt) : '—' },
    { id: 'actions', enableHiding: false, enableSorting: false, header: 'Actions', cell: ({ row }) => <div className="flex justify-end gap-1">{/* The name is a link, but a link that only reveals itself on hover is a drill-down nobody finds; this is the affordance. */}<Button size="icon" variant="ghost" title="Open account" asChild><Link to={`/accounts/${row.original.id}`}><ChevronRight aria-hidden /><span className="sr-only">Open {row.original.name}</span></Link></Button><Button size="icon" variant="ghost" title="Add contact" disabled={!canAccount(row.original)} onClick={() => setContactSheet({ open: true, accountId: row.original.id })}><UserPlus aria-hidden /><span className="sr-only">Add contact to {row.original.name}</span></Button><Button size="icon" variant="ghost" title="Edit account" disabled={!canAccount(row.original)} onClick={() => setAccountSheet({ open: true, record: row.original })}><Pencil aria-hidden /><span className="sr-only">Edit {row.original.name}</span></Button></div> },
  ];
  const contactColumns: ColumnDef<ContactRow>[] = [
    { id: 'contact', accessorFn: (row) => `${row.contact.fullName} ${row.contact.email ?? ''}`, header: ({ column }) => <DataTableColumnHeader column={column} title="Contact" />, cell: ({ row }) => <div className="min-w-0 max-w-xs"><p className="truncate font-medium">{row.original.contact.fullName}</p>{row.original.contact.email ? <p className="truncate text-xs text-muted">{row.original.contact.email}</p> : <p className="text-xs text-muted">No email recorded</p>}</div> },
    { accessorKey: 'accountName', header: ({ column }) => <DataTableColumnHeader column={column} title="Account" />, cell: ({ row }) => row.original.contact.accountId && row.original.accountName ? <Link to={`/accounts/${row.original.contact.accountId}`} className="underline-offset-4 hover:text-accent-fg hover:underline">{row.original.accountName}</Link> : 'Unassigned' },
    { id: 'title', accessorFn: (row) => row.contact.title ?? '', header: ({ column }) => <DataTableColumnHeader column={column} title="Title" />, cell: ({ row }) => row.original.contact.title ?? '—' },
    { id: 'affiliation', accessorFn: (row) => row.contact.affiliation, header: ({ column }) => <DataTableColumnHeader column={column} title="Affiliation" />, cell: ({ row }) => humanise(row.original.contact.affiliation) },
    { id: 'confidence', accessorFn: (row) => row.contact.confidence, header: ({ column }) => <DataTableColumnHeader column={column} title="Confidence" />, cell: ({ row }) => <Confidence confidence={row.original.contact.confidence} /> },
    { id: 'lastActivityAt', accessorFn: (row) => row.contact.lastActivityAt ?? '', header: ({ column }) => <DataTableColumnHeader column={column} title="Last activity" />, cell: ({ row }) => row.original.contact.lastActivityAt ? relativeTime(row.original.contact.lastActivityAt) : '—' },
    { id: 'actions', enableHiding: false, enableSorting: false, header: 'Actions', cell: ({ row }) => <div className="flex justify-end"><Button size="icon" variant="ghost" title="Edit contact" disabled={!canContact(row.original)} onClick={() => setContactSheet({ open: true, record: row.original.contact })}><Pencil aria-hidden /><span className="sr-only">Edit {row.original.contact.fullName}</span></Button></div> },
  ];
  const activeQuery = view === 'accounts' ? accountsQuery : contactsQuery;
  const count = view === 'accounts' ? accountsQuery.data?.length ?? 0 : contactsQuery.data?.length ?? 0;

  return <div className="flex flex-col gap-6">
    <PageHeader
      title="Accounts"
      description="Providers we buy from, customers we sell to, and the people who make each relationship real."
      actions={<><Button variant="outline" disabled={!canAny} onClick={() => setContactSheet({ open: true })}><UserPlus aria-hidden />New contact</Button><Button variant="primary" disabled={!canAny} onClick={() => setAccountSheet({ open: true })}><Plus aria-hidden />New account</Button></>}
    />
    {/*
      No tinted panel around the toolbar: the tabs already carry a border and a
      surface from the primitive, so boxing them added a fourth plane to a
      system that has three, and the box was the only one of its kind in the
      product.

      Two tab bars at opposite ends of the row, because they answer two
      different questions — which records you are looking at, and which half of
      the book they come from. Stacked, they read as one control that has been
      split in two. The filter's labels are the book's own: the Side column
      beside it says "Buy-side", and a filter saying "supply" for the same fact
      is a second vocabulary to learn.
    */}
    <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
      <Tabs value={view} onValueChange={(value) => { setView(value as View); setMobileSearch(''); }}>
        <TabsList className="grid w-full grid-cols-2 sm:w-64"><TabsTrigger value="accounts">Accounts</TabsTrigger><TabsTrigger value="contacts">Contacts</TabsTrigger></TabsList>
      </Tabs>
      <div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-3">
        {view === 'accounts' ? <Tabs value={side} onValueChange={(value) => setSide(value as typeof side)}>
          <TabsList aria-label="Filter by side of the book"><TabsTrigger value="all">All</TabsTrigger><TabsTrigger value="supply">{SIDE_LABELS.supply}</TabsTrigger><TabsTrigger value="demand">{SIDE_LABELS.demand}</TabsTrigger></TabsList>
        </Tabs> : null}
        <p className="text-sm text-muted"><strong className="nums text-fg">{count}</strong> {view}{view === 'accounts' && side !== 'all' ? ` · ${SIDE_LABELS[side].toLocaleLowerCase()}` : ''}</p>
      </div>
    </div>
    <label className="relative md:hidden"><span className="sr-only">Search {view}</span><Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted" aria-hidden /><Input className="h-11 pl-9" value={mobileSearch} onChange={(event) => setMobileSearch(event.target.value)} placeholder={view === 'accounts' ? 'Search account, country or type' : 'Search contact, account or title'} /></label>
    {activeQuery.isLoading ? <Skeleton className="h-64" /> : null}
    {activeQuery.isError ? <Card><EmptyState title={`${view === 'accounts' ? 'Accounts' : 'Contacts'} unavailable`} description={activeQuery.error.message} action={<Button variant="outline" onClick={() => void activeQuery.refetch()}><RefreshCw aria-hidden />Try again</Button>} /></Card> : null}
    {!activeQuery.isLoading && !activeQuery.isError && view === 'accounts' ? <><div className="hidden md:block"><DataTable columns={accountColumns} data={accountsQuery.data ?? []} filterColumn="account" filterPlaceholder="Search account names or domains" empty={<EmptyState icon={<Building2 />} title="No accounts yet" description="Relationship records appear here once a buy- or sell-side account exists." />} /></div><div className="grid gap-3 md:hidden">{visibleAccounts.map((account) => <AccountCard key={account.id} account={account} writable={canAccount(account)} onAddContact={() => setContactSheet({ open: true, accountId: account.id })} onEdit={() => setAccountSheet({ open: true, record: account })} />)}{visibleAccounts.length === 0 ? <Card><EmptyState icon={<Building2 />} title={accountsQuery.data?.length ? 'No accounts match' : `No ${side === 'all' ? '' : `${side} `}accounts`} description={accountsQuery.data?.length ? 'Try a broader search.' : 'No relationship records are available in this view.'} /></Card> : null}</div></> : null}
    {!activeQuery.isLoading && !activeQuery.isError && view === 'contacts' ? <><div className="hidden md:block"><DataTable columns={contactColumns} data={contactsQuery.data ?? []} filterColumn="contact" filterPlaceholder="Search contact names or email" empty={<EmptyState icon={<Mail />} title="No contacts recorded" description="Add a sourced contact to an account when the relationship is known." />} /></div><div className="grid gap-3 md:hidden">{visibleContacts.map((row) => <ContactCard key={row.contact.id} row={row} writable={canContact(row)} onEdit={() => setContactSheet({ open: true, record: row.contact })} />)}{visibleContacts.length === 0 ? <Card><EmptyState icon={<Mail />} title={contactsQuery.data?.length ? 'No contacts match' : 'No contacts recorded'} description={contactsQuery.data?.length ? 'Try a broader search.' : 'Add a sourced contact to an account when the relationship is known.'} /></Card> : null}</div></> : null}
    <AccountSheet open={accountSheet.open} onOpenChange={(open) => setAccountSheet((state) => ({ ...state, open }))} record={accountSheet.record} identity={me} /><ContactSheet open={contactSheet.open} onOpenChange={(open) => setContactSheet((state) => ({ ...state, open }))} record={contactSheet.record} defaultAccountId={contactSheet.accountId} identity={me} />
  </div>;
}

function AccountCard({ account, writable, onAddContact, onEdit }: { account: AccountRecord; writable: boolean; onAddContact(): void; onEdit(): void }) { const type = accountType(account); return <article className="card min-w-0 p-4"><div className="flex items-start justify-between gap-3"><div className="min-w-0">{/* The whole title is the tap target: on a phone a link the width of the text is the difference between opening the record and selecting it. */}<Link to={`/accounts/${account.id}`} className="tap flex items-center gap-1 truncate font-semibold underline-offset-4 hover:underline">{account.name}<ChevronRight className="size-4 shrink-0 text-muted" aria-hidden /></Link><p className="mt-0.5 truncate text-sm text-muted">{account.domain ?? 'No domain recorded'}</p></div><SideBadge side={account.side} /></div><div className="mt-3 grid grid-cols-2 gap-2"><RecordValue label="Relationship" value={type ? humanise(type) : null} /><RecordValue label="Geography" value={account.country} /><RecordValue label="Confidence" value={<Confidence confidence={account.confidence} />} /><RecordValue label="Last activity" value={account.lastActivityAt ? relativeTime(account.lastActivityAt) : null} /></div><div className="mt-3 grid grid-cols-2 gap-2 border-t border-border pt-3"><Button variant="outline" disabled={!writable} onClick={onAddContact}><UserPlus aria-hidden />Add contact</Button><Button variant="ghost" disabled={!writable} onClick={onEdit}><Pencil aria-hidden />Edit account</Button></div></article>; }
function ContactCard({ row, writable, onEdit }: { row: ContactRow; writable: boolean; onEdit(): void }) { return <article className="card min-w-0 p-4"><div className="flex items-start justify-between gap-3"><div className="min-w-0"><p className="truncate font-semibold">{row.contact.fullName}</p><p className="mt-0.5 truncate text-sm text-muted">{row.contact.title ?? 'No title recorded'}</p></div><Badge tone="neutral">{humanise(row.contact.affiliation)}</Badge></div><div className="mt-3 grid grid-cols-2 gap-2"><RecordValue label="Account" value={row.contact.accountId && row.accountName ? <Link className="underline-offset-4 hover:underline" to={`/accounts/${row.contact.accountId}`}>{row.accountName}</Link> : 'Unassigned'} /><RecordValue label="Email" value={row.contact.email} /><RecordValue label="Confidence" value={<Confidence confidence={row.contact.confidence} />} /><RecordValue label="Last activity" value={row.contact.lastActivityAt ? relativeTime(row.contact.lastActivityAt) : null} /></div><Button className="mt-3 w-full border-t border-border" variant="ghost" disabled={!writable} onClick={onEdit}><Pencil aria-hidden />Edit contact</Button></article>; }
/**
 * The phone card's label-and-value tile, which is `Stat` at the inline size —
 * one of the six the audit counted at three numeral scales. The value used to
 * be 12px and truncated, so a long account name or an email address on this
 * card ended mid-word; `Stat` wraps instead, which is the only honest thing to
 * do with a value nobody can widen the column for.
 */
function RecordValue({ label, value }: { label: string; value: React.ReactNode }) { return <Stat label={label} value={value} size="sm" surface="inset" />; }
function Confidence({ confidence }: { confidence: string }) { return confidence === 'confirmed' ? <span className="text-muted">Confirmed</span> : <ConfidenceBadge confidence={confidence} />; }
function accountType(account: AccountRecord) { return account.supplierType ?? account.customerSegment; }
/** `ai` is cased because the segment is `applied_ai_startup`, and "Applied ai
 *  startup" beside a page about GPU capacity reads as a typo. */
function humanise(value: string) { const spaced = value.replaceAll('_', ' ').replace(/\bai\b/g, 'AI'); return spaced.charAt(0).toUpperCase() + spaced.slice(1); }
