// Portfolio chart (multi-line, toggleable series, time-range filter) +
// Trades table — ported from
// design/design_handoff_nifty_bull/prototype/chart-trades.jsx and
// adapted for the real BacktestSummary shape:
//
//   curve[].date         (ISO yyyy-mm-dd, parsed to Date here)
//   curve[].deployedCash
//   curve[].equityValue
//   curve[].cashOnHold
//   curve[].total
//
//   trades[].symbol
//   trades[].action  ("BUY"|"SELL")
//   trades[].price
//   trades[].quantity
//   trades[].tradeTime (ISO datetime)
//   trades[].pnl  (number|null — only on closing SELLs)

function PortfolioChart({ curve, benchmarkCurve, benchmarkLabel }) {
    // Default to just the two headline series — Total Portfolio Value
    // and Benchmark. The other three (Equity Value, Cash Deployed,
    // Cash on Hold) are useful for digging into the portfolio
    // composition but visually noisy by default; user opts in via the
    // legend pills.
    const [series, setSeries] = React.useState({
        total: true, equityValue: false, deployedCash: false, cashOnHold: false, benchmark: true,
    });
    const [range, setRange] = React.useState('ALL');
    const [hover, setHover] = React.useState(null);
    const svgRef = React.useRef(null);
    const [width, setWidth] = React.useState(900);
    const height = 340;

    // Normalise curve entries so `date` is always a Date instance.
    // Also splice the benchmark's total at each date in by ISO date
    // string so a single hover shows both the strategy AND the
    // benchmark on the same vertical rule.
    const benchmarkByDate = React.useMemo(() => {
        const m = new Map();
        for (const p of (benchmarkCurve || [])) {
            const k = (p.date instanceof Date ? p.date.toISOString() : p.date).slice(0, 10);
            m.set(k, p.total);
        }
        return m;
    }, [benchmarkCurve]);

    const normalised = React.useMemo(() => (curve || []).map((p) => {
        const dateObj = p.date instanceof Date ? p.date : new Date(p.date);
        const key = dateObj.toISOString().slice(0, 10);
        return {
            ...p,
            date: dateObj,
            benchmark: benchmarkByDate.get(key),
        };
    }), [curve, benchmarkByDate]);

    React.useEffect(() => {
        const ro = new ResizeObserver((entries) => {
            for (const e of entries) setWidth(Math.max(400, e.contentRect.width));
        });
        if (svgRef.current?.parentElement) ro.observe(svgRef.current.parentElement);
        return () => ro.disconnect();
    }, []);

    const ranges = ['1M', '3M', '6M', 'YTD', '1Y', 'ALL'];
    const filteredCurve = React.useMemo(() => {
        if (normalised.length === 0) return [];
        if (range === 'ALL') return normalised;
        const end = normalised[normalised.length - 1].date;
        let start;
        if (range === '1M') start = new Date(end - 30 * 864e5);
        else if (range === '3M') start = new Date(end - 90 * 864e5);
        else if (range === '6M') start = new Date(end - 180 * 864e5);
        else if (range === '1Y') start = new Date(end - 365 * 864e5);
        else if (range === 'YTD') start = new Date(end.getFullYear(), 0, 1);
        return normalised.filter((p) => p.date >= start);
    }, [normalised, range]);

    const padding = { top: 20, right: 24, bottom: 32, left: 64 };
    const w = width - padding.left - padding.right;
    const h = height - padding.top - padding.bottom;

    // Include benchmark in the y-axis range when it's visible —
    // otherwise a 0-trade backtest where the strategy total stays
    // flat at initial cash causes the benchmark line to draw above
    // the top tick mark, outside the SVG plotting area.
    const visibleKeys = ['total', 'equityValue', 'deployedCash', 'cashOnHold'].filter((k) => series[k]);
    const allValues = filteredCurve.flatMap((p) => {
        const vs = visibleKeys.map((k) => p[k] || 0);
        if (series.benchmark && p.benchmark != null) vs.push(p.benchmark);
        return vs;
    });
    const maxY = Math.max(1, ...allValues);
    const minY = 0;

    const xAt = (i) => padding.left + (i / Math.max(1, filteredCurve.length - 1)) * w;
    const yAt = (v) => padding.top + h - (v - minY) / Math.max(1, (maxY - minY)) * h;

    const pathFor = (key) => {
        if (!series[key] || filteredCurve.length === 0) return '';
        return filteredCurve.map((p, i) => `${i === 0 ? 'M' : 'L'} ${xAt(i).toFixed(1)} ${yAt(p[key] || 0).toFixed(1)}`).join(' ');
    };

    const benchmarkColor = 'oklch(0.66 0.16 50)'; // warm orange — distinct from chart-1..4
    const seriesDefs = [
        {
            key: 'total', label: 'Total portfolio value',
            color: 'var(--chart-4)', strokeWidth: 2,
            hint: (<><strong>Total Portfolio Value</strong> — Cash on Hold + Equity Value.
                The headline number: what the account would be worth if you sold everything
                at the day's close.</>),
        },
        {
            key: 'benchmark', label: `Benchmark: ${benchmarkLabel || 'index'}`,
            color: benchmarkColor, strokeWidth: 1.75, dash: '6 4',
            hint: (<><strong>Benchmark</strong> — total value if the same lumpsum/SIP cash
                schedule had bought the {benchmarkLabel || 'chosen index'} instead of going
                through the strategy. Each tranche purchases fractional units of the index
                at that day's close and is held to the end. The line is the daily
                mark-to-market of those units. Lets you tell whether the strategy is
                actually beating the index it's drawing stocks from.</>),
        },
        {
            key: 'equityValue', label: 'Equity value',
            color: 'var(--chart-2)', strokeWidth: 1.75,
            hint: (<><strong>Equity Value</strong> — mark-to-market value of stocks you
                currently hold (sum of qty × current price for every open lot). Compare
                against Cash Deployed: if Equity Value &gt; Cash Deployed, your holdings
                are in unrealised profit; reverse means unrealised loss.</>),
        },
        {
            key: 'deployedCash', label: 'Cash deployed',
            color: 'var(--chart-1)', strokeWidth: 1.75,
            hint: (<><strong>Cash Deployed</strong> — the cost basis of stocks you currently
                hold (sum of qty × purchase price for every open lot). <em>Can rise above the
                initial capital</em> if the strategy rolls profits forward — sells holdings
                at a gain and re-invests the larger proceeds in new positions. It's
                the cumulative <em>cost</em> of what you're holding right now, not how
                much fresh cash you've ever put in.</>),
        },
        {
            key: 'cashOnHold', label: 'Cash on hold',
            color: 'var(--chart-3)', strokeWidth: 1.75, dash: '3 3',
            hint: (<><strong>Cash on Hold</strong> — un-deployed cash sitting in your account.
                Lumpsum starts at the full capital and shrinks as the strategy buys; SIP
                starts at zero and refills with one tranche on the 1st of each month.</>),
        },
    ];

    const yTicks = 4;
    const ticks = Array.from({ length: yTicks + 1 }, (_, i) => minY + (maxY - minY) * (i / yTicks));

    const xTickCount = Math.min(6, filteredCurve.length);
    const xTicks = filteredCurve.length > 0
        ? Array.from({ length: xTickCount }, (_, i) => Math.floor(i * (filteredCurve.length - 1) / Math.max(1, (xTickCount - 1))))
        : [];

    const handleMove = (e) => {
        if (!svgRef.current || filteredCurve.length === 0) return;
        const rect = svgRef.current.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const idx = Math.max(0, Math.min(filteredCurve.length - 1, Math.round((x - padding.left) / w * (filteredCurve.length - 1))));
        setHover({ idx, point: filteredCurve[idx] });
    };

    const lastTotal = filteredCurve[filteredCurve.length - 1]?.total || 0;
    const firstTotal = filteredCurve[0]?.total || 0;
    const headlineDelta = firstTotal > 0 ? ((lastTotal / firstTotal - 1) * 100) : 0;

    return (
        <div className="card" style={{ padding: 0 }}>
            <div style={{ padding: '20px 24px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
                <div>
                    <div style={{ fontSize: 11.5, color: 'var(--text-3)', letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>
                        Portfolio timeline
                    </div>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
                        <div className="mono" style={{ fontSize: 28, fontWeight: 600, letterSpacing: '-0.02em' }}>
                            {UI.fmtINR(lastTotal, { compact: true })}
                        </div>
                        <span className={`pill ${headlineDelta >= 0 ? 'up' : 'down'}`} style={{ fontSize: 11.5 }}>
                            {headlineDelta >= 0 ? '+' : ''}{headlineDelta.toFixed(2)}%
                        </span>
                    </div>
                </div>
                <div className="seg">
                    {ranges.map((r) => (
                        <button key={r} className={r === range ? 'active' : ''} onClick={() => setRange(r)}>{r}</button>
                    ))}
                </div>
            </div>

            <div style={{ position: 'relative', padding: '0 8px' }}>
                <svg ref={svgRef} width="100%" height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: 'block', overflow: 'visible' }}
                    onMouseMove={handleMove}
                    onMouseLeave={() => setHover(null)}
                >
                    {ticks.map((t, i) => (
                        <g key={i}>
                            <line x1={padding.left} x2={width - padding.right} y1={yAt(t)} y2={yAt(t)}
                                stroke="var(--border)" strokeDasharray={i === 0 ? '' : '2 3'} />
                            <text x={padding.left - 10} y={yAt(t) + 4} textAnchor="end"
                                style={{ fontSize: 10.5, fill: 'var(--text-3)', fontFamily: 'var(--font-mono)' }}>
                                {UI.fmtINR(t, { compact: true })}
                            </text>
                        </g>
                    ))}

                    {xTicks.map((i) => (
                        <text key={i} x={xAt(i)} y={height - padding.bottom + 18} textAnchor="middle"
                            style={{ fontSize: 10.5, fill: 'var(--text-3)', fontFamily: 'var(--font-sans)' }}>
                            {UI.fmtDate(filteredCurve[i].date, true)}
                        </text>
                    ))}

                    {seriesDefs.map((def) => series[def.key] && (
                        <path key={def.key} d={pathFor(def.key)} fill="none"
                            stroke={def.color} strokeWidth={def.strokeWidth}
                            strokeDasharray={def.dash} strokeLinecap="round" strokeLinejoin="round" />
                    ))}

                    {hover && (
                        <g>
                            <line x1={xAt(hover.idx)} x2={xAt(hover.idx)} y1={padding.top} y2={height - padding.bottom}
                                stroke="var(--text-3)" strokeDasharray="3 3" opacity="0.5" />
                            {seriesDefs.map((def) => series[def.key] && (
                                <circle key={def.key} cx={xAt(hover.idx)} cy={yAt(hover.point[def.key] || 0)}
                                    r="3.5" fill="white" stroke={def.color} strokeWidth="2" />
                            ))}
                        </g>
                    )}
                </svg>

                {hover && (
                    <div style={{
                        position: 'absolute', pointerEvents: 'none',
                        left: Math.min(width - 220, Math.max(8, xAt(hover.idx) + 12)),
                        top: 8,
                        background: 'var(--surface)', border: '1px solid var(--border)',
                        borderRadius: 10, padding: '10px 12px',
                        boxShadow: 'var(--shadow-md)',
                        minWidth: 200,
                    }}>
                        <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginBottom: 8 }}>
                            {UI.fmtDate(hover.point.date)}
                        </div>
                        {seriesDefs.filter((d) => series[d.key]).map((d) => (
                            <div key={d.key} style={{ display: 'flex', justifyContent: 'space-between', gap: 14, alignItems: 'center', marginTop: 4 }}>
                                <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--text-2)' }}>
                                    <span style={{ width: 8, height: 8, borderRadius: 2, background: d.color }} />
                                    {d.label}
                                </div>
                                <div className="mono" style={{ fontSize: 12, fontWeight: 600 }}>
                                    {UI.fmtINR(hover.point[d.key], { compact: true })}
                                </div>
                            </div>
                        ))}
                    </div>
                )}
            </div>

            <div style={{ padding: '10px 24px 22px', display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                {seriesDefs.map((def) => {
                    const on = series[def.key];
                    return (
                        <button key={def.key}
                            onClick={() => setSeries((s) => ({ ...s, [def.key]: !s[def.key] }))}
                            style={{
                                display: 'flex', alignItems: 'center', gap: 8,
                                padding: '8px 14px',
                                background: on ? 'var(--surface)' : 'transparent',
                                border: `1px solid ${on ? 'var(--border-strong)' : 'var(--border)'}`,
                                borderRadius: 999,
                                cursor: 'pointer',
                                font: '500 12px/1 var(--font-sans)',
                                color: on ? 'var(--text)' : 'var(--text-3)',
                                transition: 'all 180ms ease',
                                opacity: on ? 1 : 0.55,
                                boxShadow: on ? 'var(--shadow-selected-neutral)' : 'none',
                                transform: on ? 'translateY(-0.5px)' : 'none',
                            }}
                        >
                            <span style={{
                                width: 10, height: 10, borderRadius: 2,
                                background: on ? def.color : 'transparent',
                                border: `1.5px solid ${def.color}`,
                            }} />
                            {def.label}
                            {def.hint && (
                                <span onClick={(e) => e.stopPropagation()}
                                    style={{ display: 'inline-flex', marginLeft: 2 }}>
                                    <UI.Hint width={300}>{def.hint}</UI.Hint>
                                </span>
                            )}
                        </button>
                    );
                })}
            </div>
        </div>
    );
}

// ---------- Trades Table ----------
function TradesTable({ trades, windowStart, windowEnd }) {
    const [query, setQuery] = React.useState('');
    const [side, setSide] = React.useState('ALL');
    // Pre-populate the date filter with the backtest window so the
    // user immediately sees what range the trades span instead of
    // empty inputs that imply "no filter applied".
    const [startDate, setStartDate] = React.useState(windowStart || '');
    const [endDate, setEndDate] = React.useState(windowEnd || '');
    const [sortBy, setSortBy] = React.useState({ key: 'date', dir: 'desc' });

    // Trades from the API arrive with `tradeTime` (ISO string),
    // `action` ("BUY"|"SELL"), and (when known) companyName + isin +
    // upstoxUrl. Coerce to a uniform shape once.
    const normalised = React.useMemo(() => (trades || []).map((t, i) => ({
        id: t.id ?? `T${i}`,
        symbol: t.symbol,
        companyName: t.companyName,
        upstoxUrl: t.upstoxUrl,
        side: t.action,
        date: t.tradeTime instanceof Date ? t.tradeTime : new Date(t.tradeTime),
        qty: t.quantity,
        price: typeof t.price === 'number' ? t.price : Number(t.price),
        value: (typeof t.price === 'number' ? t.price : Number(t.price)) * t.quantity,
        pnl: t.pnl == null ? null : Number(t.pnl),
        note: t.strategyNote,
    })), [trades]);

    const filtered = React.useMemo(() => {
        return normalised.filter((t) => {
            if (side !== 'ALL' && t.side !== side) return false;
            if (query) {
                const q = query.toLowerCase();
                const hay = `${t.symbol || ''} ${t.companyName || ''}`.toLowerCase();
                if (!hay.includes(q)) return false;
            }
            if (startDate && t.date < new Date(startDate)) return false;
            if (endDate && t.date > new Date(endDate + 'T23:59:59')) return false;
            return true;
        }).sort((a, b) => {
            const k = sortBy.key;
            const dir = sortBy.dir === 'asc' ? 1 : -1;
            if (k === 'date') return (a.date - b.date) * dir;
            if (k === 'stock') return a.symbol.localeCompare(b.symbol) * dir;
            if (k === 'side') return a.side.localeCompare(b.side) * dir;
            if (k === 'value') return (a.value - b.value) * dir;
            if (k === 'pnl') return ((a.pnl ?? 0) - (b.pnl ?? 0)) * dir;
            return 0;
        });
    }, [normalised, query, side, startDate, endDate, sortBy]);

    const toggleSort = (key) => {
        setSortBy((prev) => prev.key === key
            ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' }
            : { key, dir: 'desc' });
    };

    const Th = ({ col, label, align = 'left', hint }) => (
        <th style={{ textAlign: align, cursor: 'pointer', userSelect: 'none' }} onClick={() => toggleSort(col)}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
                {label}
                {sortBy.key === col && (
                    <span style={{ color: 'var(--text-2)', fontSize: 9 }}>
                        {sortBy.dir === 'asc' ? '▲' : '▼'}
                    </span>
                )}
                {hint && (
                    <span onClick={(e) => e.stopPropagation()}>
                        <UI.Hint width={300}>{hint}</UI.Hint>
                    </span>
                )}
            </span>
        </th>
    );

    return (
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
            <div style={{ padding: '18px 20px 14px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
                <div>
                    <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em' }}>Trades executed</div>
                    <div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 3 }}>
                        {filtered.length} of {normalised.length} trades
                    </div>
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
                    <div className="seg">
                        {['ALL', 'BUY', 'SELL'].map((s) => (
                            <button key={s} className={s === side ? 'active' : ''} onClick={() => setSide(s)}>
                                {s === 'ALL' ? 'All' : s === 'BUY' ? 'Buys' : 'Sells'}
                            </button>
                        ))}
                    </div>
                    <div style={{ position: 'relative' }}>
                        <span style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-3)', display: 'flex' }}>
                            <UI.Icon.Search />
                        </span>
                        <input className="input"
                            style={{ paddingLeft: 32, width: 180, padding: '8px 10px 8px 32px', fontSize: 13 }}
                            placeholder="Search ticker…"
                            value={query}
                            onChange={(e) => setQuery(e.target.value)} />
                    </div>
                    <input className="input" type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)}
                        style={{ width: 140, padding: '8px 10px', fontSize: 13 }} />
                    <span style={{ color: 'var(--text-3)', fontSize: 12 }}>to</span>
                    <input className="input" type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)}
                        style={{ width: 140, padding: '8px 10px', fontSize: 13 }} />
                    {(query || side !== 'ALL' || startDate || endDate) && (
                        <button className="btn sm ghost" onClick={() => { setQuery(''); setSide('ALL'); setStartDate(''); setEndDate(''); }}>
                            Clear
                        </button>
                    )}
                </div>
            </div>

            <div style={{ maxHeight: 460, overflow: 'auto', borderTop: '1px solid var(--border)' }}>
                <table className="table">
                    <thead>
                        <tr>
                            <Th col="stock" label="Stock" />
                            <Th col="date" label="Date executed" />
                            <Th col="side" label="Order type" />
                            <Th col="value" label="Qty × Price" align="right" />
                            <th style={{ textAlign: 'right' }}>Value</th>
                            <Th col="pnl" label="P&amp;L" align="right"
                                hint={<>
                                    <strong>Realised P&amp;L</strong> — sells only.
                                    Each sell is FIFO-matched against the earliest open
                                    buys for the same symbol; matched lots contribute{' '}
                                    <code>(sellPrice − buyPrice) × qty</code>. A single
                                    sell can span multiple earlier buys at different
                                    prices, in which case the row's P&amp;L is the
                                    sum across those matched lots. Buys show
                                    {' '}<code>—</code> because they're opening trades
                                    only. Excludes brokerage / STT / GST — those costs
                                    are already debited from cash on hold by the paper
                                    adapter.
                                </>} />
                        </tr>
                    </thead>
                    <tbody>
                        {filtered.length === 0 ? (
                            <tr><td colSpan="6" className="empty">No trades match your filters.</td></tr>
                        ) : filtered.slice(0, 200).map((t) => (
                            <tr key={t.id}>
                                <td>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                                        <div style={{
                                            width: 28, height: 28, borderRadius: 6,
                                            background: 'var(--surface-2)',
                                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                                            fontSize: 10, fontWeight: 600, color: 'var(--text-2)',
                                            fontFamily: 'var(--font-mono)',
                                            flex: 'none',
                                        }}>
                                            {(t.symbol || '').slice(0, 2)}
                                        </div>
                                        <div style={{ minWidth: 0 }}>
                                            {t.upstoxUrl ? (
                                                <a href={t.upstoxUrl} target="_blank" rel="noopener noreferrer"
                                                    style={{
                                                        fontSize: 13, fontWeight: 600, color: 'var(--text)',
                                                        letterSpacing: '-0.005em', textDecoration: 'none',
                                                        borderBottom: '1px dashed var(--text-4)',
                                                    }}
                                                    onMouseEnter={(e) => e.currentTarget.style.color = 'var(--accent-text)'}
                                                    onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text)'}>
                                                    {t.companyName || t.symbol}
                                                </a>
                                            ) : (
                                                <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text)', letterSpacing: '-0.005em' }}>
                                                    {t.companyName || t.symbol}
                                                </div>
                                            )}
                                            <div className="mono" style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 2 }}>
                                                {t.symbol}
                                            </div>
                                        </div>
                                    </div>
                                </td>
                                <td>
                                    <div className="mono" style={{ fontSize: 13 }}>{UI.fmtDate(t.date)}</div>
                                    <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 1 }}>
                                        {t.date.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit', hour12: false })} IST
                                    </div>
                                </td>
                                <td>
                                    <span className={t.side === 'BUY' ? 'side-buy' : 'side-sell'}>{t.side}</span>
                                </td>
                                <td align="right" className="mono" style={{ fontSize: 13, color: 'var(--text-2)' }}>
                                    {t.qty} × ₹{Number(t.price).toLocaleString('en-IN', { minimumFractionDigits: 2 })}
                                </td>
                                <td align="right" className="mono" style={{ fontSize: 13, fontWeight: 600 }}>
                                    {UI.fmtINR(t.value)}
                                </td>
                                <td align="right" className="mono" style={{ fontSize: 13, fontWeight: 600,
                                    color: t.pnl == null ? 'var(--text-4)' : t.pnl >= 0 ? 'var(--up)' : 'var(--down)' }}>
                                    {t.pnl == null ? '—' : `${t.pnl >= 0 ? '+' : ''}${UI.fmtINR(t.pnl)}`}
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </div>
    );
}

window.PortfolioChart = PortfolioChart;
window.TradesTable = TradesTable;
