import { useEffect, useMemo, useRef, useState } from 'react';
import { money } from '@/lib/format';
import { cn } from '@/lib/utils';

/**
 * Sent vs accepted over time, as 1-, 7- or 30-day rolling totals. Two thin lines on one axis,
 * crosshair tooltip, direct end labels + legend (identity is never colour alone), and a table view.
 * Colours: validated categorical pair (blue / aqua) on the white card surface.
 */
const SERIES = [
    { key: 1, label: 'Sent', color: '#2a78d6' },
    { key: 2, label: 'Accepted', color: '#1baf7a', dash: '5 3' },
] as const;

export type Trend = { days: [string, number, number][]; lead: number; valued: boolean };

export function TrendChart({ trend, currency = 'AUD' }: { trend: Trend; currency?: string }) {
    const [win, setWin] = useState<1 | 7 | 30>(7);
    const [table, setTable] = useState(false);
    const [hover, setHover] = useState<number | null>(null);
    const wrap = useRef<HTMLDivElement>(null);
    const [w, setW] = useState(640);
    useEffect(() => {
        const el = wrap.current;
        if (!el) return;
        const ro = new ResizeObserver(([e]) => setW(Math.max(280, Math.round(e.contentRect.width))));
        ro.observe(el);
        return () => ro.disconnect();
    }, []);

    const points = useMemo(() => {
        const d = trend.days;
        const out: { date: string; s: number; a: number }[] = [];
        for (let i = trend.lead; i < d.length; i++) {
            let s = 0; let a = 0;
            for (let j = Math.max(0, i - win + 1); j <= i; j++) { s += d[j][1]; a += d[j][2]; }
            out.push({ date: d[i][0], s, a });
        }
        return out;
    }, [trend, win]);

    const fmt = (v: number) => (trend.valued ? money(v, currency) : String(v));
    const fmtAxis = (v: number) => (trend.valued ? (v >= 100000000 ? `$${(v / 100000000).toFixed(1)}m` : v >= 100000 ? `$${Math.round(v / 100000)}k` : `$${Math.round(v / 100)}`) : String(v));
    const h = 220; const padL = 44; const padR = 78; const padT = 12; const padB = 26;
    const max = Math.max(1, ...points.map((p) => Math.max(p.s, p.a)));
    const niceMax = nice(max);
    const x = (i: number) => padL + (points.length <= 1 ? 0 : (i / (points.length - 1)) * (w - padL - padR));
    const y = (v: number) => padT + (1 - v / niceMax) * (h - padT - padB);
    const path = (k: 's' | 'a') => points.map((p, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)},${y(p[k]).toFixed(1)}`).join('');
    const ticks = [0, 0.25, 0.5, 0.75, 1].map((t) => t * niceMax);
    const xTicks = points.length ? [0, Math.round((points.length - 1) / 3), Math.round((2 * (points.length - 1)) / 3), points.length - 1].filter((v, i, a) => a.indexOf(v) === i) : [];
    const last = points[points.length - 1];
    const hp = hover !== null ? points[hover] : null;
    const label = (dt: string) => new Date(`${dt}T00:00:00`).toLocaleDateString('en-AU', { day: 'numeric', month: 'short' });

    const onMove = (e: React.MouseEvent<SVGRectElement>) => {
        const r = (e.target as SVGRectElement).getBoundingClientRect();
        const px = e.clientX - r.left;
        const i = Math.round((px / r.width) * (points.length - 1));
        setHover(Math.max(0, Math.min(points.length - 1, i)));
    };

    return (
        <div>
            <div className="mb-3 flex flex-wrap items-center justify-between gap-3">
                <div className="flex items-center gap-4 text-sm" aria-label="Legend">
                    {SERIES.map((s) => (
                        <span key={s.key} className="inline-flex items-center gap-1.5 text-ink-muted">
                            <svg width="18" height="8" aria-hidden><line x1="1" y1="4" x2="17" y2="4" stroke={s.color} strokeWidth="2" strokeDasharray={'dash' in s ? s.dash : undefined} strokeLinecap="round" /></svg>
                            {s.label}
                        </span>
                    ))}
                </div>
                <div className="flex items-center gap-2">
                    <div role="group" aria-label="Rolling total" className="inline-flex rounded-md border border-line p-0.5 text-xs">
                        {([1, 7, 30] as const).map((n) => (
                            <button key={n} type="button" onClick={() => setWin(n)} aria-pressed={win === n} data-testid={`rolling-${n}`}
                                className={cn('rounded px-2.5 py-1', win === n ? 'bg-ink text-white' : 'text-ink-muted hover:bg-stone-100')}>{n === 1 ? 'Daily' : `${n}-day`}</button>
                        ))}
                    </div>
                    <button type="button" className="text-xs text-link underline" onClick={() => setTable((t) => !t)} data-testid="trend-table-toggle">{table ? 'Show chart' : 'Show table'}</button>
                </div>
            </div>
            {table ? (
                <div className="max-h-64 overflow-auto rounded-md border border-line" data-testid="trend-table">
                    <table className="w-full text-sm">
                        <thead className="sticky top-0 bg-page text-left text-xs text-ink-muted"><tr><th className="px-3 py-1.5 font-medium">Day</th><th className="px-3 py-1.5 text-right font-medium">Sent{win > 1 ? ` (${win}-day)` : ''}</th><th className="px-3 py-1.5 text-right font-medium">Accepted{win > 1 ? ` (${win}-day)` : ''}</th></tr></thead>
                        <tbody>{[...points].reverse().map((p) => <tr key={p.date} className="border-t border-line"><td className="px-3 py-1">{label(p.date)}</td><td className="px-3 py-1 text-right tabular-nums">{fmt(p.s)}</td><td className="px-3 py-1 text-right tabular-nums">{fmt(p.a)}</td></tr>)}</tbody>
                    </table>
                </div>
            ) : (
                <div ref={wrap} className="relative" data-testid="trend-chart">
                    <svg width={w} height={h} role="img" aria-label={`Sent and accepted, ${win === 1 ? 'daily' : `${win}-day rolling totals`}`}>
                        {ticks.map((t) => (
                            <g key={t}>
                                <line x1={padL} x2={w - padR} y1={y(t)} y2={y(t)} stroke="#e7e5e4" strokeWidth={1} />
                                <text x={padL - 6} y={y(t)} dy="0.32em" textAnchor="end" className="fill-ink-muted text-[11px] tabular-nums">{fmtAxis(t)}</text>
                            </g>
                        ))}
                        {xTicks.map((i) => <text key={i} x={x(i)} y={h - 6} textAnchor={i === 0 ? 'start' : i === points.length - 1 ? 'end' : 'middle'} className="fill-ink-muted text-[11px]">{label(points[i].date)}</text>)}
                        <path d={path('s')} fill="none" stroke={SERIES[0].color} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round" />
                        <path d={path('a')} fill="none" stroke={SERIES[1].color} strokeWidth={2} strokeDasharray={SERIES[1].dash} strokeLinejoin="round" strokeLinecap="round" />
                        {last && (() => {
                            // Direct end labels, nudged apart when they'd overlap.
                            let ys = y(last.s); let ya = y(last.a);
                            if (Math.abs(ys - ya) < 14) { if (ys <= ya) { ys -= 7; ya += 7; } else { ys += 7; ya -= 7; } }
                            return <>
                                <text x={w - padR + 8} y={ys} dy="0.32em" className="fill-ink text-[11px] font-medium">Sent {fmt(last.s)}</text>
                                <text x={w - padR + 8} y={ya} dy="0.32em" className="fill-ink text-[11px] font-medium">Acc. {fmt(last.a)}</text>
                            </>;
                        })()}
                        {hp && hover !== null && (
                            <g pointerEvents="none">
                                <line x1={x(hover)} x2={x(hover)} y1={padT} y2={h - padB} stroke="#a8a29e" strokeWidth={1} />
                                <circle cx={x(hover)} cy={y(hp.s)} r={4} fill={SERIES[0].color} stroke="#fff" strokeWidth={2} />
                                <circle cx={x(hover)} cy={y(hp.a)} r={4} fill={SERIES[1].color} stroke="#fff" strokeWidth={2} />
                            </g>
                        )}
                        <rect x={padL} y={padT} width={Math.max(1, w - padL - padR)} height={h - padT - padB} fill="transparent" onMouseMove={onMove} onMouseLeave={() => setHover(null)} />
                    </svg>
                    {hp && hover !== null && (
                        <div className="pointer-events-none absolute top-2 z-10 rounded-md border border-line bg-surface px-3 py-2 text-xs shadow-pop"
                            style={{ left: Math.min(Math.max(x(hover) + 12, 0), w - 170) }} role="tooltip">
                            <div className="mb-1 font-medium">{label(hp.date)}{win > 1 ? ` · ${win} days to here` : ''}</div>
                            <div className="flex items-center justify-between gap-6"><span className="inline-flex items-center gap-1.5"><i className="inline-block h-0.5 w-3" style={{ background: SERIES[0].color }} />Sent</span><b className="tabular-nums">{fmt(hp.s)}</b></div>
                            <div className="flex items-center justify-between gap-6"><span className="inline-flex items-center gap-1.5"><i className="inline-block h-0.5 w-3" style={{ background: SERIES[1].color }} />Accepted</span><b className="tabular-nums">{fmt(hp.a)}</b></div>
                        </div>
                    )}
                </div>
            )}
        </div>
    );
}

function nice(v: number): number {
    const p = 10 ** Math.floor(Math.log10(v));
    const n = v / p;
    return (n <= 1 ? 1 : n <= 2 ? 2 : n <= 4 ? 4 : n <= 5 ? 5 : 10) * p;
}
