import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';
import { toast } from 'sonner';
import {
  AlertTriangle,
  CheckCircle2,
  ClipboardCheck,
  Loader2,
  RefreshCw,
  Receipt,
} from 'lucide-react';
import { api } from '@/api';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import BillModal from '@/components/BillModal';
import { cn, errMessage } from '@/lib/utils';
import { makeBillDraft } from '@/lib/billDrafts';
import type { Bill, Category } from '@/types';

const FIELD_LABELS: Record<string, string> = {
  due_day: 'Due day',
  category_id: 'Category',
  minimum_payment: 'Minimum payment',
  autopay_enabled: 'Autopay details',
  interest_rate: 'APR',
};

interface Issue {
  severity: string;
  field: string;
  suggestion?: string;
}

interface HealthBill {
  id: number;
  name: string;
  category_name?: string | null;
  due_day?: number | null;
  active?: number;
  issues: Issue[];
}

interface HealthSummary {
  audited_bills?: number;
  issue_count?: number;
  error_count?: number;
  warning_count?: number;
}

interface HealthData {
  summary?: HealthSummary;
  bills?: HealthBill[];
}

interface ModalState {
  bill?: Bill | null;
  initialBill?: Partial<Bill>;
}

function severityClass(severity: string): string {
  return severity === 'error'
    ? 'border-destructive/25 bg-destructive/10 text-destructive'
    : 'border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300';
}

function severityWeight(severity: string): number {
  return severity === 'error' ? 0 : 1;
}

function issueSummary(issues: Issue[] = []): string {
  const errors = issues.filter(issue => issue.severity === 'error').length;
  const warnings = issues.length - errors;
  if (errors && warnings) return `${errors} error${errors === 1 ? '' : 's'}, ${warnings} warning${warnings === 1 ? '' : 's'}`;
  if (errors) return `${errors} error${errors === 1 ? '' : 's'}`;
  return `${warnings} warning${warnings === 1 ? '' : 's'}`;
}

function StatCard({ label, value, tone = 'default' }: {
  label: string;
  value: ReactNode;
  tone?: 'default' | 'error' | 'warning' | 'ok';
}) {
  return (
    <div className={cn(
      'rounded-xl border px-4 py-3 shadow-sm',
      tone === 'error' && 'border-destructive/20 bg-destructive/10',
      tone === 'warning' && 'border-amber-500/20 bg-amber-500/10',
      tone === 'ok' && 'border-emerald-500/20 bg-emerald-500/10',
      tone === 'default' && 'border-border/70 bg-card/80',
    )}>
      <p className="text-[11px] font-semibold uppercase tracking-normal text-muted-foreground">{label}</p>
      <p className="tracker-number mt-1 text-xl font-bold text-foreground">{value}</p>
    </div>
  );
}

function IssuePill({ issue }: { issue: Issue }) {
  return (
    <span className={cn(
      'inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide',
      severityClass(issue.severity),
    )}>
      {issue.severity}
    </span>
  );
}

function BillIssueCard({ bill, onOpenBill, openingBillId }: {
  bill: HealthBill;
  onOpenBill: (id: number) => void;
  openingBillId: number | null;
}) {
  const sortedIssues = [...bill.issues].sort((a, b) => severityWeight(a.severity) - severityWeight(b.severity));
  const opening = openingBillId === bill.id;

  return (
    <Card className="overflow-hidden">
      <CardHeader className="pb-3">
        <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
          <div className="min-w-0">
            <CardTitle className="flex min-w-0 flex-wrap items-center gap-2 text-base">
              <Receipt className="h-4 w-4 shrink-0 text-primary" />
              <span className="truncate">{bill.name}</span>
              <span className="rounded-full border border-border bg-muted/55 px-2 py-0.5 text-[11px] font-semibold text-muted-foreground">
                {issueSummary(bill.issues)}
              </span>
            </CardTitle>
            <CardDescription className="mt-1">
              {bill.category_name || 'No category'} - {bill.due_day ? `Due day ${bill.due_day}` : 'No due day'}
              {!bill.active && ' - Inactive'}
            </CardDescription>
          </div>
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="shrink-0"
            onClick={() => onOpenBill(bill.id)}
            disabled={!!openingBillId}
          >
            {opening && <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />}
            {opening ? 'Opening...' : 'Open Bill'}
          </Button>
        </div>
      </CardHeader>
      <CardContent className="space-y-3">
        {sortedIssues.map(issue => (
          <div key={`${bill.id}-${issue.field}`} className="rounded-lg border border-border/70 bg-background/65 p-3">
            <div className="flex flex-wrap items-center gap-2">
              <IssuePill issue={issue} />
              <span className="text-sm font-semibold text-foreground">
                {FIELD_LABELS[issue.field] || issue.field}
              </span>
            </div>
            <p className="mt-2 text-sm leading-relaxed text-muted-foreground">{issue.suggestion}</p>
          </div>
        ))}
      </CardContent>
    </Card>
  );
}

export default function HealthPage() {
  const [data, setData] = useState<HealthData | null>(null);
  const [loading, setLoading] = useState(true);
  const [includeInactive, setIncludeInactive] = useState(false);
  const [categories, setCategories] = useState<Category[]>([]);
  const [modal, setModal] = useState<ModalState | null>(null);
  const [openingBillId, setOpeningBillId] = useState<number | null>(null);

  const load = useCallback(async () => {
    setLoading(true);
    try {
      setData(await api.billAudit(includeInactive) as HealthData);
    } catch (err) {
      toast.error(errMessage(err, 'Could not run bill health check.'));
    } finally {
      setLoading(false);
    }
  }, [includeInactive]);

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

  const openBill = useCallback(async (billId: number) => {
    setOpeningBillId(billId);
    try {
      const [bill, cats] = await Promise.all([
        api.bill(billId) as Promise<Bill>,
        (categories.length ? Promise.resolve(categories) : api.categories()) as Promise<Category[]>,
      ]);
      if (!categories.length) setCategories(cats);
      setModal({ bill });
    } catch (err) {
      toast.error(errMessage(err, 'Could not open bill.'));
    } finally {
      setOpeningBillId(null);
    }
  }, [categories]);

  const handleBillSaved = useCallback(() => {
    setModal(null);
    load();
  }, [load]);

  const summary = data?.summary || {};
  const bills = useMemo(() => data?.bills || [], [data]);
  const sortedBills = useMemo(() => [...bills].sort((a, b) => {
    const aErrors = a.issues.filter(issue => issue.severity === 'error').length;
    const bErrors = b.issues.filter(issue => issue.severity === 'error').length;
    if (aErrors !== bErrors) return bErrors - aErrors;
    if (a.issues.length !== b.issues.length) return b.issues.length - a.issues.length;
    return a.name.localeCompare(b.name);
  }), [bills]);
  const hasIssues = sortedBills.length > 0;
  const healthTone = useMemo<'error' | 'warning' | 'ok'>(() => {
    if ((summary.error_count || 0) > 0) return 'error';
    if ((summary.warning_count || 0) > 0) return 'warning';
    return 'ok';
  }, [summary.error_count, summary.warning_count]);

  return (
    <div className="mx-auto w-full max-w-5xl space-y-5">
      <div className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
        <div className="min-w-0">
          <div className="flex items-center gap-2">
            <div className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/70 bg-card shadow-sm">
              <ClipboardCheck className="h-4 w-4 text-primary" />
            </div>
            <div>
              <h1 className="text-2xl font-bold tracking-tight">Bill Health</h1>
              <p className="mt-0.5 text-sm text-muted-foreground">Find setup gaps before they skew tracker and snowball results.</p>
            </div>
          </div>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          <Button
            type="button"
            variant={includeInactive ? 'default' : 'outline'}
            size="sm"
            aria-pressed={includeInactive}
            onClick={() => setIncludeInactive(value => !value)}
          >
            {includeInactive ? 'Including Inactive' : 'Active Bills Only'}
          </Button>
          <Button type="button" variant="outline" size="sm" onClick={load} disabled={loading}>
            {loading ? <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" /> : <RefreshCw className="mr-1.5 h-3.5 w-3.5" />}
            Refresh
          </Button>
        </div>
      </div>

      <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
        <StatCard label="Audited bills" value={summary.audited_bills ?? '-'} />
        <StatCard label="Issues" value={summary.issue_count ?? '-'} tone={healthTone} />
        <StatCard label="Errors" value={summary.error_count ?? '-'} tone={(summary.error_count || 0) > 0 ? 'error' : 'default'} />
        <StatCard label="Warnings" value={summary.warning_count ?? '-'} tone={(summary.warning_count || 0) > 0 ? 'warning' : 'default'} />
      </div>

      {loading ? (
        <Card>
          <CardContent className="flex items-center justify-center gap-2 py-16 text-sm text-muted-foreground">
            <Loader2 className="h-4 w-4 animate-spin" />
            Running health check...
          </CardContent>
        </Card>
      ) : !hasIssues ? (
        <Card className="border-emerald-500/20 bg-emerald-500/5">
          <CardContent className="flex flex-col items-center gap-3 px-4 py-16 text-center">
            <div className="flex h-11 w-11 items-center justify-center rounded-full border border-emerald-500/25 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300">
              <CheckCircle2 className="h-5 w-5" />
            </div>
            <div>
              <p className="font-semibold text-foreground">No bill setup issues found.</p>
              <p className="mt-1 text-sm text-muted-foreground">Your audited bills have the core fields needed for tracking and snowball projections.</p>
            </div>
          </CardContent>
        </Card>
      ) : (
        <div className="space-y-3">
          <div className="flex items-start gap-2 rounded-xl border border-amber-500/20 bg-amber-500/10 px-4 py-3 text-sm text-amber-700 dark:text-amber-300">
            <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
            <p>Fix errors first; warnings are cleanup items that improve confidence and projections.</p>
          </div>
          <div className="grid gap-3">
            {sortedBills.map(bill => (
              <BillIssueCard
                key={bill.id}
                bill={bill}
                onOpenBill={openBill}
                openingBillId={openingBillId}
              />
            ))}
          </div>
        </div>
      )}

      {modal && (
        <BillModal
          key={modal.bill?.id ? `edit-${modal.bill.id}` : `new-${modal.initialBill?.name || 'blank'}`}
          bill={modal.bill}
          initialBill={modal.initialBill}
          categories={categories}
          onClose={() => setModal(null)}
          onSave={handleBillSaved}
          onDuplicate={bill => setModal({ bill: null, initialBill: makeBillDraft(bill, { copy: true, categories }) as Partial<Bill> })}
        />
      )}
    </div>
  );
}
