"use client";

import React, { useEffect, useMemo, useRef, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { AppShell } from "../../components/app-shell";

type ApiResponse<T> = {
  data: T;
  meta: { timestamp: string; version: "v1" };
  error: null | { message: string; code?: string };
};

type Summary = {
  total: string;
  count: number;
  income?: string;
  expense?: string;
  net?: string;
};

type CashflowPoint = {
  month: string;
  income: string;
  expense: string;
  net: string;
};

type MerchantInsight = {
  merchant: string;
  total: string;
  count: number;
};

const recentTransactions = [
  {
    date: "Oct 24, 2023",
    description: "Whole Foods Market",
    category: "Groceries",
    account: "Chase Sapphire",
    amount: "-$142.30"
  },
  {
    date: "Oct 23, 2023",
    description: "Apple Subscription",
    category: "Services",
    account: "Apple Card",
    amount: "-$14.99"
  },
  {
    date: "Oct 22, 2023",
    description: "Stripe Payout",
    category: "Income",
    account: "Mercury Business",
    amount: "+$4,200.00"
  },
  {
    date: "Oct 22, 2023",
    description: "Shell Gasoline",
    category: "Transport",
    account: "Chase Sapphire",
    amount: "-$52.12"
  }
];

// ---------- helpers ----------
function formatMonthLabel(yyyyMm: string) {
  const [y, m] = yyyyMm.split("-").map((v) => Number(v));
  const d = new Date(y, (m || 1) - 1, 1);
  return d.toLocaleString(undefined, { month: "short", year: "numeric" });
}

function formatCurrency(n: number) {
  const abs = Math.abs(n);
  const sign = n < 0 ? "-" : "";
  return `${sign}$${abs.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}

function parseMoney(s?: string) {
  return Number.parseFloat(s ?? "0") || 0;
}

function clamp(n: number, min: number, max: number) {
  return Math.max(min, Math.min(max, n));
}

// ---------- tiny UI bits ----------
function Pill({
  label,
  value,
  tone = "neutral"
}: {
  label: string;
  value: string;
  tone?: "neutral" | "good" | "bad";
}) {
  const toneCls =
    tone === "good"
      ? "text-primary bg-primary/10"
      : tone === "bad"
      ? "text-red-500 bg-red-500/10"
      : "text-muted-foreground bg-secondary/40";

  return (
    <span className={`inline-flex items-center gap-2 rounded-full border border-border px-3 py-1 text-xs ${toneCls}`}>
      <span className="text-[10px] uppercase tracking-[0.18em]">{label}</span>
      <span className="font-semibold text-foreground">{value}</span>
    </span>
  );
}

function ChartLoadingSkeleton() {
  // A nicer loading state than a big black rectangle:
  // grid + animated wave + fake bars/line.
  return (
    <div className="relative h-56 w-full overflow-hidden rounded-xl border border-border bg-background/50">
      <div
        className="absolute inset-0"
        style={{
          backgroundImage:
            "linear-gradient(rgba(49, 98, 99, 0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(49, 98, 99, 0.06) 1px, transparent 1px)",
          backgroundSize: "40px 40px"
        }}
      />

      {/* shimmer wave */}
      <motion.div
        className="absolute inset-0"
        initial={{ x: "-60%" }}
        animate={{ x: "160%" }}
        transition={{ duration: 1.3, repeat: Infinity, ease: "linear" }}
        style={{
          background:
            "linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.06) 40%, rgba(255,255,255,0.12) 50%, rgba(255,255,255,0.06) 60%, transparent 100%)"
        }}
      />

      {/* fake bars */}
      <div className="absolute inset-x-4 bottom-4 flex items-end gap-3">
        {Array.from({ length: 10 }).map((_, i) => (
          <motion.div
            key={i}
            className="w-4 rounded-lg bg-secondary/60 border border-border"
            initial={{ height: 10 }}
            animate={{ height: [10, 36, 18, 42, 22, 50, 30, 44][i % 8] }}
            transition={{ duration: 0.8, repeat: Infinity, repeatType: "mirror", delay: i * 0.06 }}
          />
        ))}
      </div>

      {/* fake line */}
      <motion.div
        className="absolute left-6 right-6 top-12 h-[2px] rounded-full bg-primary/40"
        initial={{ scaleX: 0, originX: 0 }}
        animate={{ scaleX: 1 }}
        transition={{ duration: 0.9, repeat: Infinity, repeatType: "mirror" }}
      />
    </div>
  );
}

// ---------- chart ----------
function CashflowChart({
  data,
  loading
}: {
  data: CashflowPoint[];
  loading: boolean;
}) {
  const wrapRef = useRef<HTMLDivElement | null>(null);
  const [activeIndex, setActiveIndex] = useState<number | null>(null);

  const parsed = useMemo(() => {
    return (data ?? []).map((d) => {
      const income = parseMoney(d.income);
      const expense = parseMoney(d.expense);
      const net = parseMoney(d.net);
      return { ...d, income, expense, net, label: formatMonthLabel(d.month) };
    });
  }, [data]);

  const chart = useMemo(() => {
    const W = 640;
    const H = 240;
    const PAD_L = 36;
    const PAD_R = 18;
    const PAD_T = 16;
    const PAD_B = 34;

    const innerW = W - PAD_L - PAD_R;
    const innerH = H - PAD_T - PAD_B;

    if (!parsed.length) {
      return {
        W,
        H,
        PAD_L,
        PAD_R,
        PAD_T,
        PAD_B,
        innerW,
        innerH,
        yZero: PAD_T + innerH / 2,
        points: [] as { x: number; y: number; net: number; income: number; expense: number; label: string; month: string }[],
        lineD: "",
        areaD: "",
        bars: [] as { x: number; w: number; yIncome: number; hIncome: number; yExpense: number; hExpense: number }[],
        ticks: [] as { y: number; value: number }[]
      };
    }

    const nets = parsed.map((d) => d.net);
    const maxAbs = Math.max(1, ...nets.map((v) => Math.abs(v)));
    const yMax = maxAbs;
    const yMin = -maxAbs;

    const xStep = parsed.length === 1 ? 0 : innerW / (parsed.length - 1);

    const yScale = (v: number) => {
      const t = (v - yMax) / (yMin - yMax);
      return PAD_T + t * innerH;
    };

    const yZero = yScale(0);

    const points = parsed.map((d, i) => {
      const x = PAD_L + xStep * i;
      const y = yScale(d.net);
      return { x, y, net: d.net, income: d.income, expense: d.expense, label: d.label, month: d.month };
    });

    const lineD = points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");

    const areaD = [
      `M ${points[0].x} ${yZero}`,
      ...points.map((p) => `L ${p.x} ${p.y}`),
      `L ${points[points.length - 1].x} ${yZero}`,
      "Z"
    ].join(" ");

    const maxBar = Math.max(1, ...parsed.map((d) => Math.max(Math.abs(d.income), Math.abs(d.expense))));
    const barScale = (v: number) => (Math.abs(v) / maxBar) * (innerH * 0.46);

    const barBand = innerW / parsed.length;
    const barW = Math.max(10, Math.min(22, barBand * 0.42));

    const bars = parsed.map((d, i) => {
      const cx = PAD_L + barBand * i + barBand / 2;
      const x = cx - barW / 2;

      const hIncome = barScale(d.income);
      const hExpense = barScale(d.expense);

      const baseY = PAD_T + innerH;
      const yIncome = baseY - hIncome;
      const yExpense = baseY - hExpense;

      return { x, w: barW, yIncome, hIncome, yExpense, hExpense };
    });

    const ticks = [-yMax, 0, yMax].map((v) => ({ value: v, y: yScale(v) }));

    return { W, H, PAD_L, PAD_R, PAD_T, PAD_B, innerW, innerH, yZero, points, lineD, areaD, bars, ticks };
  }, [parsed]);

  const active = activeIndex === null ? null : parsed[activeIndex];

  function pickNearestIndex(clientX: number) {
    if (!wrapRef.current || !chart.points.length) return null;
    const rect = wrapRef.current.getBoundingClientRect();
    const x = ((clientX - rect.left) / rect.width) * chart.W;

    let best = 0;
    let bestDist = Infinity;
    for (let i = 0; i < chart.points.length; i++) {
      const d = Math.abs(chart.points[i].x - x);
      if (d < bestDist) {
        bestDist = d;
        best = i;
      }
    }
    return best;
  }

  if (loading) return <ChartLoadingSkeleton />;

  if (!parsed.length) {
    return (
      <div className="mt-4 rounded-xl border border-border p-4 bg-background/50">
        <div className="h-56 grid place-items-center text-sm text-muted-foreground">No cash flow data available</div>
      </div>
    );
  }

  return (
    <div
      ref={wrapRef}
      className="mt-4 rounded-xl border border-border p-4 bg-background/50 relative overflow-hidden"
      style={{
        backgroundImage:
          "linear-gradient(rgba(49, 98, 99, 0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(49, 98, 99, 0.06) 1px, transparent 1px)",
        backgroundSize: "40px 40px"
      }}
      onMouseLeave={() => setActiveIndex(null)}
      onMouseMove={(e) => setActiveIndex(pickNearestIndex(e.clientX))}
    >
      {/* subtle moving glow */}
      <motion.div
        className="pointer-events-none absolute -inset-24 opacity-40"
        animate={{ rotate: 360 }}
        transition={{ duration: 18, repeat: Infinity, ease: "linear" }}
        style={{
          background:
            "radial-gradient(circle at 30% 20%, rgba(55,121,185,0.14), transparent 45%), radial-gradient(circle at 70% 60%, rgba(49,98,99,0.12), transparent 50%)"
        }}
      />

      <svg viewBox={`0 0 ${chart.W} ${chart.H}`} className="relative h-56 w-full">
        <defs>
          <linearGradient id="netFill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--primary)" stopOpacity="0.25" />
            <stop offset="100%" stopColor="var(--primary)" stopOpacity="0.03" />
          </linearGradient>

          <linearGradient id="barsIncome" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--primary)" stopOpacity="0.55" />
            <stop offset="100%" stopColor="var(--primary)" stopOpacity="0.12" />
          </linearGradient>

          <linearGradient id="barsExpense" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="rgb(239 68 68)" stopOpacity="0.55" />
            <stop offset="100%" stopColor="rgb(239 68 68)" stopOpacity="0.12" />
          </linearGradient>
        </defs>

        {/* ticks + baseline pulse */}
        {chart.ticks.map((t) => (
          <g key={t.value}>
            <line
              x1={chart.PAD_L}
              x2={chart.W - chart.PAD_R}
              y1={t.y}
              y2={t.y}
              stroke="currentColor"
              strokeOpacity={t.value === 0 ? 0.18 : 0.08}
            />
            <text x={6} y={t.y + 4} fill="currentColor" className="text-[10px] text-muted-foreground">
              {formatCurrency(t.value)}
            </text>
          </g>
        ))}

        <motion.line
          x1={chart.PAD_L}
          x2={chart.W - chart.PAD_R}
          y1={chart.yZero}
          y2={chart.yZero}
          stroke="currentColor"
          strokeOpacity="0.18"
          strokeDasharray="6 6"
          animate={{ strokeOpacity: [0.10, 0.22, 0.10] }}
          transition={{ duration: 2.2, repeat: Infinity }}
        />

        {/* bars */}
        {chart.bars.map((b, i) => (
          <g key={i}>
            <motion.rect
              x={b.x - 6}
              y={b.yExpense}
              width={b.w}
              height={b.hExpense}
              rx="7"
              fill="url(#barsExpense)"
              initial={{ height: 0, y: chart.PAD_T + chart.innerH }}
              animate={{ height: b.hExpense, y: b.yExpense }}
              transition={{ duration: 0.6, delay: i * 0.05, ease: [0.2, 0.8, 0.2, 1] }}
            />
            <motion.rect
              x={b.x + 6}
              y={b.yIncome}
              width={b.w}
              height={b.hIncome}
              rx="7"
              fill="url(#barsIncome)"
              initial={{ height: 0, y: chart.PAD_T + chart.innerH }}
              animate={{ height: b.hIncome, y: b.yIncome }}
              transition={{ duration: 0.6, delay: i * 0.05 + 0.06, ease: [0.2, 0.8, 0.2, 1] }}
            />
          </g>
        ))}

        {/* area */}
        <motion.path
          d={chart.areaD}
          fill="url(#netFill)"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: 0.4 }}
        />

        {/* net line */}
        <motion.path
          d={chart.lineD}
          fill="none"
          stroke="var(--primary)"
          strokeWidth="3.2"
          strokeLinecap="round"
          strokeLinejoin="round"
          initial={{ pathLength: 0 }}
          animate={{ pathLength: 1 }}
          transition={{ duration: 1.05, ease: [0.2, 0.8, 0.2, 1] }}
        />

        {/* points */}
        {chart.points.map((p, i) => {
          const isActive = i === activeIndex;
          return (
            <g key={i}>
              {isActive ? <circle cx={p.x} cy={p.y} r="14" fill="var(--primary)" opacity="0.10" /> : null}
              <motion.circle
                cx={p.x}
                cy={p.y}
                r={isActive ? 6 : 4}
                fill="var(--primary)"
                initial={{ scale: 0 }}
                animate={{ scale: 1 }}
                transition={{ duration: 0.28, delay: i * 0.03 }}
                opacity={isActive ? 1 : 0.75}
              />
            </g>
          );
        })}

        {/* hover crosshair */}
        {activeIndex !== null && chart.points[activeIndex] ? (
          <g>
            <line
              x1={chart.points[activeIndex].x}
              x2={chart.points[activeIndex].x}
              y1={chart.PAD_T}
              y2={chart.PAD_T + chart.innerH}
              stroke="currentColor"
              strokeOpacity="0.14"
              strokeDasharray="4 4"
            />
          </g>
        ) : null}

        {/* x labels */}
        {chart.points.map((p, i) => (
          <text
            key={i}
            x={p.x}
            y={chart.H - 10}
            textAnchor="middle"
            fill="currentColor"
            className="text-[10px] text-muted-foreground"
            opacity={i % 2 === 0 ? 0.95 : 0.55}
          >
            {(parsed[i]?.month ?? "").slice(2)} {/* "25-08" vibe */}
          </text>
        ))}
      </svg>

      {/* Tooltip */}
      <AnimatePresence>
        {active && activeIndex !== null ? (
          <motion.div
            key="tooltip"
            initial={{ opacity: 0, y: 8, scale: 0.98 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, y: 8, scale: 0.98 }}
            transition={{ duration: 0.16 }}
            className="absolute top-3 right-3 rounded-xl border border-border bg-background/92 backdrop-blur px-3 py-2 shadow-sm"
          >
            <div className="text-xs font-semibold text-foreground">{active.label}</div>
            <div className="mt-1 grid gap-1 text-[11px] text-muted-foreground">
              <div className="flex items-center justify-between gap-6">
                <span>Income</span>
                <span className="font-semibold text-foreground">{formatCurrency(active.income)}</span>
              </div>
              <div className="flex items-center justify-between gap-6">
                <span>Expense</span>
                <span className="font-semibold text-foreground">{formatCurrency(active.expense)}</span>
              </div>
              <div className="h-px bg-border my-1" />
              <div className="flex items-center justify-between gap-6">
                <span>Net</span>
                <span className={`font-bold ${active.net < 0 ? "text-red-500" : "text-primary"}`}>
                  {formatCurrency(active.net)}
                </span>
              </div>
            </div>
          </motion.div>
        ) : null}
      </AnimatePresence>

      {/* Legend */}
      <div className="mt-2 flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground">
        <span className="inline-flex items-center gap-2 rounded-full border border-border bg-secondary/40 px-2 py-1">
          <span className="h-2 w-2 rounded-full bg-primary" />
          Net (line)
        </span>
        <span className="inline-flex items-center gap-2 rounded-full border border-border bg-secondary/40 px-2 py-1">
          <span className="h-2 w-2 rounded-full bg-red-500" />
          Expense (bar)
        </span>
        <span className="inline-flex items-center gap-2 rounded-full border border-border bg-secondary/40 px-2 py-1">
          <span className="h-2 w-2 rounded-full bg-primary/70" />
          Income (bar)
        </span>
      </div>
    </div>
  );
}

// ---------- page ----------
export default function AppHomePage() {
  const [summary, setSummary] = useState<Summary | null>(null);
  const [cashflow, setCashflow] = useState<CashflowPoint[]>([]);
  const [merchants, setMerchants] = useState<MerchantInsight[]>([]);
  const [accountCount, setAccountCount] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const userId = localStorage.getItem("ledgerone_user_id");
    if (!userId) {
      setLoading(false);
      return;
    }
    const query = `?user_id=${encodeURIComponent(userId)}`;

    Promise.all([
      fetch(`/api/transactions/summary${query}`).then((res) => res.json() as Promise<ApiResponse<Summary>>),
      fetch(`/api/transactions/cashflow${query}&months=6`).then((res) => res.json() as Promise<ApiResponse<CashflowPoint[]>>),
      fetch(`/api/transactions/merchants${query}&limit=5`).then((res) => res.json() as Promise<ApiResponse<MerchantInsight[]>>),
      fetch(`/api/accounts${query}`).then((res) => res.json() as Promise<ApiResponse<{ id: string }[]>>)
    ])
      .then(([summaryRes, cashflowRes, merchantsRes, accountsRes]) => {
        if (!summaryRes.error) setSummary(summaryRes.data);
        if (!cashflowRes.error) setCashflow(cashflowRes.data);
        if (!merchantsRes.error) setMerchants(merchantsRes.data);
        if (!accountsRes.error) setAccountCount(accountsRes.data.length);
      })
      .catch(() => undefined)
      .finally(() => setLoading(false));
  }, []);

  const netNow = useMemo(() => parseMoney(summary?.net), [summary?.net]);
  const incomeNow = useMemo(() => parseMoney(summary?.income), [summary?.income]);
  const expenseNow = useMemo(() => parseMoney(summary?.expense), [summary?.expense]);

  const netTone: "good" | "bad" | "neutral" = netNow > 0 ? "good" : netNow < 0 ? "bad" : "neutral";

  const bestWorst = useMemo(() => {
    if (!cashflow.length) return null;
    const parsed = cashflow.map((c) => ({ month: c.month, net: parseMoney(c.net) }));
    const best = parsed.reduce((a, b) => (b.net > a.net ? b : a), parsed[0]);
    const worst = parsed.reduce((a, b) => (b.net < a.net ? b : a), parsed[0]);
    return { best, worst };
  }, [cashflow]);

  return (
    <AppShell title="Dashboard" subtitle="Performance overview and live account health.">
      {/* top filters row (slightly nicer motion) */}
      <motion.section
        className="flex flex-wrap items-center gap-3 text-xs text-muted-foreground"
        initial={{ opacity: 0, y: 8 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.35 }}
      >
        <span className="flex items-center gap-2 px-4 py-2 rounded-full bg-secondary/50 border border-border font-medium">
          <span className="h-2 w-2 rounded-full bg-primary" />
          Last 30 days
        </span>
        <span className="flex items-center gap-2 px-4 py-2 rounded-full bg-secondary/50 border border-border font-medium">
          <span className="h-2 w-2 rounded-full bg-primary" />
          All categories
        </span>
        <span className="flex items-center gap-2 px-4 py-2 rounded-full bg-secondary/50 border border-border font-medium">
          <span className="h-2 w-2 rounded-full bg-primary" />
          Combined accounts
        </span>

        <div className="ml-auto hidden lg:flex items-center gap-2">
          <Pill label="Sync" value={loading ? "Updating…" : "Live"} tone="neutral" />
          {bestWorst ? (
            <Pill label="Worst" value={`${bestWorst.worst.month}: ${formatCurrency(bestWorst.worst.net)}`} tone="bad" />
          ) : null}
        </div>
      </motion.section>

      {/* KPI cards */}
      <section className="mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
        {[
          {
            title: "Income (30d)",
            value: loading ? "—" : formatCurrency(incomeNow),
            sub: "Deposits and credits",
            badge: "Inflow",
            badgeCls: "text-primary bg-primary/10"
          },
          {
            title: "Expenses (30d)",
            value: loading ? "—" : formatCurrency(expenseNow),
            sub: "Bills and spending",
            badge: "Outflow",
            badgeCls: "text-red-500 bg-red-500/10"
          },
          {
            title: "Net cash flow",
            value: loading ? "—" : formatCurrency(netNow),
            sub: summary ? `${summary.count} transactions` : "No data yet",
            badge: "30d",
            badgeCls: netTone === "bad" ? "text-red-500 bg-red-500/10" : "text-primary bg-primary/10"
          },
          {
            title: "Active accounts",
            value: loading ? "—" : `${accountCount ?? 0}`,
            sub: "Bank + card connections",
            badge: "Linked",
            badgeCls: "text-primary bg-primary/10"
          }
        ].map((card, idx) => (
          <motion.div
            key={card.title}
            className="glass-panel p-5 rounded-2xl shadow-sm"
            initial={{ opacity: 0, y: 10 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.35, delay: idx * 0.05 }}
          >
            <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">{card.title}</p>
            <div className="mt-3 flex items-baseline justify-between">
              <p className="text-2xl font-bold text-foreground">{card.value}</p>
              <span className={`text-xs font-medium px-2 py-1 rounded-full ${card.badgeCls}`}>{card.badge}</span>
            </div>
            <p className="mt-2 text-xs text-muted-foreground">{card.sub}</p>
          </motion.div>
        ))}
      </section>

      {/* main grid */}
      <section className="mt-6 grid gap-4 xl:grid-cols-[2fr_1fr]">
        <motion.div
          className="glass-panel p-6 rounded-2xl shadow-sm"
          initial={{ opacity: 0, y: 12 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.35, delay: 0.05 }}
        >
          <div className="flex items-center justify-between">
            <div>
              <p className="text-sm font-bold text-foreground">Monthly Cash-Flow</p>
              <p className="text-xs text-muted-foreground">Net flow over last 6 months</p>
            </div>

            <div className="hidden md:flex items-center gap-2">
              <Pill label="Net" value={loading ? "—" : formatCurrency(netNow)} tone={netTone} />
              <Pill label="Window" value="6M" tone="neutral" />
            </div>
          </div>

          <CashflowChart data={cashflow} loading={loading} />

          <div className="mt-4 grid gap-2 text-xs text-muted-foreground md:grid-cols-3">
            {(cashflow ?? []).map((item) => {
              const net = parseMoney(item.net);
              return (
                <div key={item.month} className="flex items-center justify-between rounded-lg border border-border bg-secondary/20 px-3 py-2">
                  <span className="font-medium">{formatMonthLabel(item.month)}</span>
                  <span className={`font-bold ${net < 0 ? "text-red-500" : "text-foreground"}`}>
                    {formatCurrency(net)}
                  </span>
                </div>
              );
            })}
          </div>
        </motion.div>

        <motion.div
          className="glass-panel p-6 rounded-2xl shadow-sm"
          initial={{ opacity: 0, y: 12 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.35, delay: 0.1 }}
        >
          <div className="flex items-center justify-between">
            <div>
              <p className="text-sm font-bold text-foreground">Top Merchants</p>
              <p className="text-xs text-muted-foreground">Highest spend by merchant</p>
            </div>
          </div>

          <div className="mt-6 space-y-4 text-xs">
            {loading ? (
              <div className="space-y-3">
                {Array.from({ length: 5 }).map((_, i) => (
                  <div key={i} className="rounded-xl border border-border bg-secondary/20 p-3">
                    <div className="h-3 w-2/3 bg-secondary/60 rounded animate-pulse" />
                    <div className="mt-2 h-3 w-1/2 bg-secondary/60 rounded animate-pulse" />
                  </div>
                ))}
              </div>
            ) : merchants.length ? (
              merchants.map((merchant, i) => (
                <motion.div
                  key={merchant.merchant}
                  initial={{ opacity: 0, y: 6 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ duration: 0.25, delay: i * 0.04 }}
                  className="flex items-center justify-between border-b border-border pb-2 last:border-0 last:pb-0"
                >
                  <div>
                    <p className="text-sm font-medium text-foreground">{merchant.merchant}</p>
                    <p className="text-xs text-muted-foreground">
                      {merchant.count} transaction{merchant.count === 1 ? "" : "s"}
                    </p>
                  </div>
                  <span className="text-sm font-bold text-foreground">${merchant.total}</span>
                </motion.div>
              ))
            ) : (
              <p className="text-sm text-muted-foreground">No merchant insights yet.</p>
            )}
          </div>
        </motion.div>
      </section>

      {/* table */}
      <motion.section
        className="mt-6 glass-panel p-6 rounded-2xl shadow-sm"
        initial={{ opacity: 0, y: 12 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.35, delay: 0.15 }}
      >
        <div className="flex items-center justify-between">
          <h2 className="text-sm font-bold text-foreground">Recent Transactions</h2>
          <span className="text-xs font-medium text-primary cursor-pointer hover:underline">View all records</span>
        </div>

        <div className="mt-4 overflow-x-auto">
          <table className="w-full text-left text-xs text-muted-foreground">
            <thead className="text-[0.65rem] uppercase tracking-[0.2em] text-muted-foreground font-semibold">
              <tr className="border-b border-border">
                <th className="pb-3 pl-2">Date</th>
                <th className="pb-3">Description</th>
                <th className="pb-3">Category</th>
                <th className="pb-3">Account</th>
                <th className="pb-3 pr-2 text-right">Amount</th>
              </tr>
            </thead>
            <tbody>
              {recentTransactions.map((tx) => (
                <tr key={`${tx.date}-${tx.description}`} className="border-b border-border hover:bg-secondary/30 transition-colors">
                  <td className="py-3 pl-2 font-medium">{tx.date}</td>
                  <td className="py-3 text-foreground font-medium">{tx.description}</td>
                  <td className="py-3">
                    <span className="inline-flex items-center rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-foreground">
                      {tx.category}
                    </span>
                  </td>
                  <td className="py-3">{tx.account}</td>
                  <td className={`py-3 pr-2 text-right font-bold ${tx.amount.startsWith("+") ? "text-primary" : "text-foreground"}`}>
                    {tx.amount}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </motion.section>
    </AppShell>
  );
}
