import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import {
  ArrowDown,
  ArrowDownUp,
  ArrowUp,
  Building2,
  CheckCircle2,
  ChevronLeft,
  ChevronRight,
  Clock3,
  Landmark,
  Link2,
  RefreshCw,
  Search,
  TrendingDown,
  TrendingUp,
  WalletCards,
} from 'lucide-react';
import { api } from '@/api';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table';
import { cn, fmt, fmtDate } from '@/lib/utils';

const PAGE_SIZE = 50;

const flowOptions = [
  { value: 'all', label: 'All' },
  { value: 'in', label: 'Money in' },
  { value: 'out', label: 'Money out' },
  { value: 'pending', label: 'Pending' },
  { value: 'unmatched', label: 'Needs review' },
  { value: 'matched', label: 'Matched' },
  { value: 'ignored', label: 'Ignored' },
];

const sortOptions = [
  { value: 'date', label: 'Date' },
  { value: 'amount', label: 'Amount' },
  { value: 'merchant', label: 'Merchant' },
  { value: 'account', label: 'Account' },
  { value: 'status', label: 'Status' },
];

function formatCents(value, { signed = false } = {}) {
  const cents = Number(value || 0);
  const amount = fmt(Math.abs(cents) / 100);
  if (!signed || cents === 0) return amount;
  return `${cents > 0 ? '+' : '-'}${amount}`;
}

function formatSyncTime(value) {
  if (!value) return 'Not synced yet';
  const normalized = String(value).includes('T') ? String(value) : String(value).replace(' ', 'T');
  const date = new Date(normalized);
  if (Number.isNaN(date.getTime())) return String(value);
  return new Intl.DateTimeFormat(undefined, {
    month: 'short',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
  }).format(date);
}

function transactionTitle(tx) {
  return tx.payee || tx.description || tx.memo || 'Transaction';
}

function transactionDate(tx) {
  return tx.posted_date || (tx.transacted_at ? tx.transacted_at.slice(0, 10) : null);
}

function accountLabel(account) {
  if (!account) return 'Unknown account';
  return [account.org_name, account.name].filter(Boolean).join(' - ') || account.name || 'Account';
}

function StatusBadge({ tx }) {
  if (tx.pending) {
    return (
      <Badge className="border-amber-300/50 bg-amber-400/15 text-amber-700 dark:text-amber-200">
        <Clock3 className="mr-1 h-3 w-3" />
        Pending
      </Badge>
    );
  }
  if (tx.ignored || tx.match_status === 'ignored') {
    return <Badge variant="outline" className="border-muted-foreground/30 text-muted-foreground">Ignored</Badge>;
  }
  if (tx.match_status === 'matched') {
    return (
      <Badge className="border-emerald-300/50 bg-emerald-400/15 text-emerald-700 dark:text-emerald-200">
        <CheckCircle2 className="mr-1 h-3 w-3" />
        Matched
      </Badge>
    );
  }
  return <Badge className="border-sky-300/50 bg-sky-400/15 text-sky-700 dark:text-sky-200">Review</Badge>;
}

function SummaryTile({ icon: Icon, label, value, tone, detail }) {
  const tones = {
    emerald: 'border-emerald-300/40 bg-emerald-400/10 text-emerald-700 dark:text-emerald-200',
    rose: 'border-rose-300/40 bg-rose-400/10 text-rose-700 dark:text-rose-200',
    sky: 'border-sky-300/40 bg-sky-400/10 text-sky-700 dark:text-sky-200',
    amber: 'border-amber-300/40 bg-amber-400/10 text-amber-700 dark:text-amber-200',
  };

  return (
    <div className="rounded-lg border border-border/70 bg-card/80 p-4 shadow-sm">
      <div className="flex items-center justify-between gap-3">
        <p className="text-sm font-medium text-muted-foreground">{label}</p>
        <span className={cn('flex h-9 w-9 items-center justify-center rounded-full border', tones[tone])}>
          <Icon className="h-4 w-4" />
        </span>
      </div>
      <p className="tracker-number mt-3 text-2xl font-bold text-foreground">{value}</p>
      {detail && <p className="mt-1 text-xs font-medium text-muted-foreground">{detail}</p>}
    </div>
  );
}

function TransactionMobileCard({ tx }) {
  const cents = Number(tx.amount || 0);
  const isCredit = cents > 0;

  return (
    <div className="rounded-lg border border-border/70 bg-card/85 p-4 shadow-sm">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="truncate text-sm font-semibold text-foreground">{transactionTitle(tx)}</p>
          <p className="mt-1 truncate text-xs font-medium text-muted-foreground">
            {[tx.account_org_name, tx.account_name].filter(Boolean).join(' - ') || 'Account'}
          </p>
        </div>
        <p className={cn('tracker-number shrink-0 text-sm font-bold', isCredit ? 'text-emerald-600 dark:text-emerald-300' : 'text-rose-600 dark:text-rose-300')}>
          {formatCents(cents, { signed: true })}
        </p>
      </div>
      <div className="mt-3 flex flex-wrap items-center gap-2">
        <Badge variant="outline" className="text-muted-foreground">{fmtDate(transactionDate(tx))}</Badge>
        <StatusBadge tx={tx} />
        {tx.matched_bill_name && (
          <Badge className="border-indigo-300/50 bg-indigo-400/15 text-indigo-700 dark:text-indigo-200">
            <Link2 className="mr-1 h-3 w-3" />
            {tx.matched_bill_name}
          </Badge>
        )}
      </div>
    </div>
  );
}

export default function BankTransactionsPage() {
  const [ledger, setLedger] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [search, setSearch] = useState('');
  const [query, setQuery] = useState('');
  const [accountId, setAccountId] = useState('all');
  const [flow, setFlow] = useState('all');
  const [sortBy, setSortBy] = useState('date');
  const [sortDir, setSortDir] = useState('desc');
  const [page, setPage] = useState(1);
  // Monotonic request id: a response only lands if it belongs to the newest
  // request, so a slow Refresh can never overwrite fresher filter results.
  const requestSeq = useRef(0);

  useEffect(() => {
    const id = window.setTimeout(() => {
      setQuery(search.trim());
      setPage(1);
    }, 250);
    return () => window.clearTimeout(id);
  }, [search]);

  useEffect(() => {
    setPage(1);
  }, [accountId, flow, sortBy, sortDir]);

  // Single fetch path — used by both the load effect and the Refresh button.
  const loadLedger = useCallback(async () => {
    const seq = ++requestSeq.current;
    setLoading(true);
    setError('');
    try {
      const data = await api.bankTransactionsLedger({
        limit: PAGE_SIZE,
        offset: (page - 1) * PAGE_SIZE,
        q: query,
        account_id: accountId === 'all' ? undefined : accountId,
        flow,
        sort_by: sortBy,
        sort_dir: sortDir,
      });
      if (seq === requestSeq.current) setLedger(data);
    } catch (err) {
      if (seq === requestSeq.current) setError(err.message || 'Unable to load bank transactions');
    } finally {
      if (seq === requestSeq.current) setLoading(false);
    }
  }, [accountId, flow, page, query, sortBy, sortDir]);

  useEffect(() => { loadLedger(); }, [loadLedger]);

  const accounts = ledger?.accounts || [];
  const transactions = ledger?.transactions || [];
  const summary = ledger?.summary || {};
  const total = Number(ledger?.total || 0);
  const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));

  // If a filter shrinks the result set below the current page, snap back to
  // the last real page instead of stranding the user on an empty one.
  useEffect(() => {
    if (!loading && page > totalPages) setPage(totalPages);
  }, [loading, page, totalPages]);
  const latestSync = useMemo(() => {
    const times = (ledger?.sources || []).map(source => source.last_sync_at).filter(Boolean).sort();
    return times[times.length - 1] || null;
  }, [ledger?.sources]);
  const connected = Boolean(ledger?.enabled && ledger?.has_connections);

  // A failed request must never masquerade as "not connected" — without this,
  // a transient API error told users with working bank sync to go connect it.
  if (!loading && error && !ledger) {
    return (
      <div className="space-y-6">
        <section className="rounded-lg border border-rose-300/40 bg-rose-400/10 p-6 shadow-sm">
          <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <h1 className="text-xl font-bold text-foreground">Bank Transactions</h1>
              <p className="mt-1 text-sm font-medium text-rose-700 dark:text-rose-200">{error}</p>
            </div>
            <Button type="button" variant="outline" onClick={loadLedger}>
              <RefreshCw className="h-4 w-4" />
              Try again
            </Button>
          </div>
        </section>
      </div>
    );
  }

  if (!loading && !connected) {
    return (
      <div className="space-y-6">
        <section className="rounded-lg border border-border/70 bg-card/85 p-6 shadow-sm">
          <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <div className="flex items-center gap-2">
                <span className="flex h-10 w-10 items-center justify-center rounded-full border border-sky-300/40 bg-sky-400/10 text-sky-700 dark:text-sky-200">
                  <Landmark className="h-5 w-5" />
                </span>
                <div>
                  <h1 className="text-2xl font-bold tracking-normal text-foreground">Bank Transactions</h1>
                  <p className="text-sm font-medium text-muted-foreground">SimpleFIN bridge required</p>
                </div>
              </div>
            </div>
            <Button asChild>
              <Link to="/data">
                <WalletCards className="h-4 w-4" />
                Open Data
              </Link>
            </Button>
          </div>
        </section>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      <section className="rounded-lg border border-border/70 bg-[linear-gradient(135deg,oklch(var(--card))_0%,oklch(var(--muted)/0.72)_58%,oklch(var(--primary)/0.10)_100%)] p-5 shadow-sm">
        <div className="flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
          <div className="min-w-0">
            <div className="flex flex-wrap items-center gap-3">
              <span className="flex h-11 w-11 items-center justify-center rounded-full border border-sky-300/40 bg-sky-400/10 text-sky-700 dark:text-sky-200">
                <Landmark className="h-5 w-5" />
              </span>
              <div>
                <h1 className="text-2xl font-bold tracking-normal text-foreground sm:text-3xl">Bank Transactions</h1>
                <p className="mt-1 text-sm font-medium text-muted-foreground">
                  {accounts.length} monitored {accounts.length === 1 ? 'account' : 'accounts'} synced through SimpleFIN
                </p>
              </div>
              <Badge className={cn(
                connected
                  ? 'border-emerald-300/50 bg-emerald-400/15 text-emerald-700 dark:text-emerald-200'
                  : 'border-sky-300/50 bg-sky-400/15 text-sky-700 dark:text-sky-200',
              )}>
                {connected ? 'Connected' : 'Loading'}
              </Badge>
            </div>
          </div>
          <div className="flex flex-col gap-2 sm:flex-row sm:items-center">
            <div className="rounded-lg border border-border/70 bg-card/75 px-3 py-2 text-sm shadow-sm">
              <span className="font-medium text-muted-foreground">Last sync </span>
              <span className="font-semibold text-foreground">{formatSyncTime(latestSync)}</span>
            </div>
            <Button type="button" variant="outline" onClick={loadLedger} disabled={loading}>
              <RefreshCw className={cn('h-4 w-4', loading && 'animate-spin')} />
              Refresh
            </Button>
          </div>
        </div>
      </section>

      {error && (
        <div className="rounded-lg border border-rose-300/40 bg-rose-400/10 px-4 py-3 text-sm font-medium text-rose-700 dark:text-rose-200">
          {error}
        </div>
      )}

      <section className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
        <SummaryTile
          icon={TrendingUp}
          label="Money in"
          value={formatCents(summary.inflow)}
          tone="emerald"
          detail={`${Number(summary.matched || 0)} matched`}
        />
        <SummaryTile
          icon={TrendingDown}
          label="Money out"
          value={formatCents(summary.outflow)}
          tone="rose"
          detail={`${Number(summary.unmatched || 0)} need review`}
        />
        <SummaryTile
          icon={ArrowDownUp}
          label="Net flow"
          value={formatCents(summary.net, { signed: true })}
          tone="sky"
          detail={summary.latest_date ? `Latest ${fmtDate(summary.latest_date)}` : 'No posted activity'}
        />
        <SummaryTile
          icon={Clock3}
          label="Pending"
          value={String(Number(summary.pending || 0))}
          tone="amber"
          detail={`${Number(summary.total || 0)} total transactions`}
        />
      </section>

      {accounts.length > 0 && (
        <section className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
          {accounts.map(account => (
            <div key={account.id} className="rounded-lg border border-border/70 bg-card/85 p-4 shadow-sm">
              <div className="flex items-start justify-between gap-3">
                <div className="min-w-0">
                  <p className="truncate text-sm font-semibold text-foreground">{accountLabel(account)}</p>
                  <p className="mt-1 truncate text-xs font-medium text-muted-foreground">
                    {[account.account_type, account.currency].filter(Boolean).join(' - ') || 'Bank account'}
                  </p>
                </div>
                <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-indigo-300/40 bg-indigo-400/10 text-indigo-700 dark:text-indigo-200">
                  <Building2 className="h-4 w-4" />
                </span>
              </div>
              <div className="mt-4 grid grid-cols-2 gap-3">
                <div>
                  <p className="text-xs font-medium text-muted-foreground">Balance</p>
                  <p className="tracker-number mt-1 text-lg font-bold text-foreground">{formatCents(account.balance)}</p>
                </div>
                <div>
                  <p className="text-xs font-medium text-muted-foreground">Available</p>
                  <p className="tracker-number mt-1 text-lg font-bold text-emerald-600 dark:text-emerald-300">
                    {account.available_balance == null ? '—' : formatCents(account.available_balance)}
                  </p>
                </div>
              </div>
              <div className="mt-3 flex items-center justify-between text-xs font-medium text-muted-foreground">
                <span>{Number(account.transaction_count || 0)} transactions</span>
                <span>{account.last_transaction_date ? fmtDate(account.last_transaction_date) : 'No activity'}</span>
              </div>
            </div>
          ))}
        </section>
      )}

      <section className="space-y-4">
        <div className="grid gap-3 rounded-lg border border-border/70 bg-card/85 p-3 shadow-sm lg:grid-cols-[minmax(18rem,1fr)_12rem_12rem_10rem_auto]">
          <div className="relative">
            <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              value={search}
              onChange={event => setSearch(event.target.value)}
              placeholder="Search merchant, memo, category"
              className="pl-9"
            />
          </div>

          <Select value={accountId} onValueChange={setAccountId}>
            <SelectTrigger>
              <SelectValue placeholder="Account" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All accounts</SelectItem>
              {accounts.map(account => (
                <SelectItem key={account.id} value={String(account.id)}>{accountLabel(account)}</SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Select value={flow} onValueChange={setFlow}>
            <SelectTrigger>
              <SelectValue placeholder="Type" />
            </SelectTrigger>
            <SelectContent>
              {flowOptions.map(option => (
                <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Select value={sortBy} onValueChange={setSortBy}>
            <SelectTrigger>
              <SelectValue placeholder="Sort" />
            </SelectTrigger>
            <SelectContent>
              {sortOptions.map(option => (
                <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Button
            type="button"
            variant="outline"
            onClick={() => setSortDir(value => (value === 'asc' ? 'desc' : 'asc'))}
            title={`Sort ${sortDir === 'asc' ? 'ascending' : 'descending'}`}
          >
            {sortDir === 'asc' ? <ArrowUp className="h-4 w-4" /> : <ArrowDown className="h-4 w-4" />}
            {sortDir === 'asc' ? 'Ascending' : 'Descending'}
          </Button>
        </div>

        <div className="hidden overflow-hidden rounded-lg border border-border/70 bg-card/85 shadow-sm lg:block">
          <Table>
            <TableHeader>
              <TableRow className="bg-muted/35 hover:bg-muted/35">
                <TableHead className="w-32">Date</TableHead>
                <TableHead>Merchant</TableHead>
                <TableHead>Account</TableHead>
                <TableHead>Status</TableHead>
                <TableHead className="text-right">Amount</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {loading && transactions.length === 0 && (
                <TableRow>
                  <TableCell colSpan={5} className="py-10 text-center text-muted-foreground">Loading transactions...</TableCell>
                </TableRow>
              )}
              {!loading && transactions.length === 0 && (
                <TableRow>
                  <TableCell colSpan={5} className="py-10 text-center text-muted-foreground">No transactions found.</TableCell>
                </TableRow>
              )}
              {transactions.map(tx => {
                const cents = Number(tx.amount || 0);
                const isCredit = cents > 0;
                return (
                  <TableRow key={tx.id}>
                    <TableCell className="text-muted-foreground">{fmtDate(transactionDate(tx))}</TableCell>
                    <TableCell>
                      <div className="min-w-0">
                        <p className="truncate font-semibold text-foreground">{transactionTitle(tx)}</p>
                        <p className="truncate text-xs font-medium text-muted-foreground">
                          {[tx.memo, tx.category].filter(Boolean).join(' - ') || tx.description || 'SimpleFIN'}
                        </p>
                      </div>
                    </TableCell>
                    <TableCell>
                      <div className="min-w-0">
                        <p className="truncate text-sm font-semibold text-foreground">
                          {[tx.account_org_name, tx.account_name].filter(Boolean).join(' - ') || 'Account'}
                        </p>
                        <p className="truncate text-xs font-medium text-muted-foreground">{tx.account_type || tx.currency || 'Bank'}</p>
                      </div>
                    </TableCell>
                    <TableCell>
                      <div className="flex flex-wrap gap-2">
                        <StatusBadge tx={tx} />
                        {tx.matched_bill_name && (
                          <Badge className="border-indigo-300/50 bg-indigo-400/15 text-indigo-700 dark:text-indigo-200">
                            <Link2 className="mr-1 h-3 w-3" />
                            {tx.matched_bill_name}
                          </Badge>
                        )}
                      </div>
                    </TableCell>
                    <TableCell className={cn('tracker-number text-right font-bold', isCredit ? 'text-emerald-600 dark:text-emerald-300' : 'text-rose-600 dark:text-rose-300')}>
                      {formatCents(cents, { signed: true })}
                    </TableCell>
                  </TableRow>
                );
              })}
            </TableBody>
          </Table>
        </div>

        <div className="grid gap-3 lg:hidden">
          {loading && transactions.length === 0 && (
            <div className="rounded-lg border border-border/70 bg-card/85 p-8 text-center text-sm font-medium text-muted-foreground">
              Loading transactions...
            </div>
          )}
          {!loading && transactions.length === 0 && (
            <div className="rounded-lg border border-border/70 bg-card/85 p-8 text-center text-sm font-medium text-muted-foreground">
              No transactions found.
            </div>
          )}
          {transactions.map(tx => <TransactionMobileCard key={tx.id} tx={tx} />)}
        </div>

        <div className="flex flex-col gap-3 rounded-lg border border-border/70 bg-card/85 px-4 py-3 shadow-sm sm:flex-row sm:items-center sm:justify-between">
          <p className="text-sm font-medium text-muted-foreground">
            {total === 0
              ? '0 transactions'
              : `${(page - 1) * PAGE_SIZE + 1}-${Math.min(page * PAGE_SIZE, total)} of ${total} transactions`}
          </p>
          <div className="flex items-center gap-2">
            <Button
              type="button"
              variant="outline"
              disabled={page <= 1 || loading}
              onClick={() => setPage(value => Math.max(1, value - 1))}
            >
              <ChevronLeft className="h-4 w-4" />
              Previous
            </Button>
            <span className="min-w-20 text-center text-sm font-semibold text-foreground">
              {page} / {totalPages}
            </span>
            <Button
              type="button"
              variant="outline"
              disabled={page >= totalPages || loading}
              onClick={() => setPage(value => Math.min(totalPages, value + 1))}
            >
              Next
              <ChevronRight className="h-4 w-4" />
            </Button>
          </div>
        </div>
      </section>
    </div>
  );
}
