/**
 * The Quant Command Center — 10 Dimensions
 * TRADING_REPORT_UI_DESIGN.pdf 설계 기반
 */
import React, { useMemo, useState } from 'react';
import type { Theme } from '../../types';
import type { AnalysisReportTypeId } from '../../utils/analysisReportTypes';
import { getReportType } from '../../utils/analysisReportTypes';
import type { AnalysisReportContext } from '../../utils/analysisReportContext';
import type { BacktestResultRecord } from '../../utils/backendApi';
import {
  estimateCagr,
  buildMonthlyMatrix,
  buildYtdByYear,
  buildReturnDistribution,
  buildHoldingPoints,
  buildAllocationSlices,
  buildVolatilitySeries,
  drawdownSeries,
} from '../../utils/analysisReportMetrics';
import { repairUtf8Mojibake } from '../../utils/textEncoding';
import { formatUpbitKrwPrice } from '../../utils/safeFormat';

/* ── 포맷 유틸 ── */
const pct   = (v: number, dec = 2) => `${v >= 0 ? '+' : ''}${(v * 100).toFixed(dec)}%`;
const pctAbs= (v: number, dec = 1) => `${(v * 100).toFixed(dec)}%`;
const num   = (v: number, dec = 2) => (isFinite(v) ? v.toFixed(dec) : '—');
const wonFmt = (v: number) => {
  const abs = Math.abs(v);
  const sign = v < 0 ? '-' : v > 0 ? '+' : '';
  if (abs >= 1e8) return `${sign}${(abs / 1e8).toFixed(1)}억`;
  if (abs >= 1e4) return `${sign}${(abs / 1e4).toFixed(0)}만`;
  return `${sign}${formatUpbitKrwPrice(abs, '0')}원`;
};
const colorCls = (v: number) => (v > 0 ? 'up' : v < 0 ? 'down' : '');

interface Props {
  typeId: AnalysisReportTypeId;
  ctx: AnalysisReportContext | null;
  theme: Theme;
  compareBacktest?: BacktestResultRecord | null;
  /** 현재 사용 안 함 — AnalysisReportPage 호환용 */
  allBacktests?: BacktestResultRecord[];
  compareLive?: unknown;
}

/* ── 공통 유틸 ── */
function fmtDateTime(ts: number) {
  const d = new Date(ts * 1000);
  return `${d.getFullYear().toString().slice(2)}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}

function fmtDuration(sec: number) {
  if (sec < 3600) return `${Math.round(sec / 60)}m`;
  const h = Math.floor(sec / 3600);
  const m = Math.round((sec % 3600) / 60);
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
}

/* ── SVG 스파크 라인 ── */
function SparkLine({
  values,
  w = 400,
  h = 100,
  positive = true,
  fill = true,
}: {
  values: number[];
  w?: number;
  h?: number;
  positive?: boolean;
  fill?: boolean;
}) {
  const path = useMemo(() => {
    if (values.length < 2) return { line: '', area: '' };
    const lo = Math.min(...values);
    const hi = Math.max(...values);
    const range = hi - lo || 1;
    const pts = values.map((v, i) => {
      const x = (i / (values.length - 1)) * w;
      const y = h - 8 - ((v - lo) / range) * (h - 16);
      return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`;
    });
    const line = pts.join(' ');
    const area = `${line} L${w},${h} L0,${h} Z`;
    return { line, area };
  }, [values, w, h]);

  const stroke = positive ? '#00e5ff' : '#ff1744';
  const fillId = `sf${positive ? 'c' : 'r'}`;

  return (
    <svg className="qcc-spark" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
      <defs>
        <linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={stroke} stopOpacity="0.4" />
          <stop offset="100%" stopColor={stroke} stopOpacity="0.02" />
        </linearGradient>
      </defs>
      {fill && <path d={path.area} fill={`url(#${fillId})`} />}
      <path d={path.line} fill="none" stroke={stroke} strokeWidth="2" />
    </svg>
  );
}

/* ── 도넛 차트 ── */
function DonutChart({
  slices,
  centerLabel,
  centerValue,
}: {
  slices: { pct: number; color: string }[];
  centerLabel: string;
  centerValue: string;
}) {
  const r = 70;
  const c = 2 * Math.PI * r;
  let acc = 0;
  const total = slices.reduce((s, x) => s + x.pct, 0) || 1;

  return (
    <svg viewBox="0 0 180 180" className="qcc-donut-svg">
      {slices.map((sl, i) => {
        const dash = (sl.pct / total) * c;
        const offset = c * 0.25 - acc;
        acc += dash;
        return (
          <circle
            key={i}
            cx="90" cy="90" r={r}
            fill="none"
            stroke={sl.color}
            strokeWidth="20"
            strokeDasharray={`${dash.toFixed(1)} ${(c - dash).toFixed(1)}`}
            strokeDashoffset={offset.toFixed(1)}
          />
        );
      })}
      <text x="90" y="85" textAnchor="middle" fontSize="11" fill="var(--se-text-muted)">{centerLabel}</text>
      <text x="90" y="103" textAnchor="middle" fontSize="13" fontWeight="700" fill="#00e5ff">{centerValue}</text>
    </svg>
  );
}

/* ── VaR 반원 게이지 ── */
function VarGauge({ varPct }: { varPct: number }) {
  const abs = Math.abs(varPct) * 100;
  const degree = Math.min(180, abs * 9); // 0~20% → 0~180도
  const rad = ((degree - 180) * Math.PI) / 180;
  const x = 90 + 70 * Math.cos(rad);
  const y = 90 + 70 * Math.sin(rad);

  return (
    <svg viewBox="0 0 180 100" className="qcc-var-svg">
      <defs>
        <linearGradient id="varGrad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor="#00e5ff" />
          <stop offset="50%" stopColor="#ffd700" />
          <stop offset="100%" stopColor="#ff1744" />
        </linearGradient>
      </defs>
      <path d="M10,90 A80,80 0 0,1 170,90" fill="none" stroke="#1e2a3a" strokeWidth="18" />
      <path d="M10,90 A80,80 0 0,1 170,90" fill="none" stroke="url(#varGrad)" strokeWidth="18" opacity="0.35" />
      <circle cx={x.toFixed(1)} cy={y.toFixed(1)} r="6" fill="#fff" />
      <text x="15" y="100" fontSize="9" fill="#00e5ff">Low Risk</text>
      <text x="135" y="100" fontSize="9" fill="#ff1744">High Risk</text>
      <text x="90" y="80" textAnchor="middle" fontSize="12" fontWeight="700" fill="#ffd700">
        VaR: {abs.toFixed(2)}%
      </text>
    </svg>
  );
}

/* ── 원형 게이지 (Efficiency) ── */
function CircularGauge({ value, label, max = 100 }: { value: number; label: string; max?: number }) {
  const r = 60;
  const c = 2 * Math.PI * r;
  const pct2 = Math.min(1, Math.max(0, value / max));
  const dash = pct2 * c;
  const color = value >= max * 0.9 ? '#ffd700' : value >= max * 0.5 ? '#00e676' : '#ff1744';

  return (
    <svg viewBox="0 0 160 160" className="qcc-gauge-svg">
      <circle cx="80" cy="80" r={r} fill="none" stroke="#1e2a3a" strokeWidth="14" />
      <circle
        cx="80" cy="80" r={r}
        fill="none"
        stroke={color}
        strokeWidth="14"
        strokeDasharray={`${dash.toFixed(1)} ${(c - dash).toFixed(1)}`}
        strokeDashoffset={(c * 0.25).toFixed(1)}
        strokeLinecap="round"
        style={{ filter: `drop-shadow(0 0 8px ${color})` }}
      />
      <text x="80" y="74" textAnchor="middle" fontSize="22" fontWeight="900" fill={color}>
        {value >= 99 ? '99+' : value.toFixed(value >= 10 ? 2 : 2)}
      </text>
      <text x="80" y="93" textAnchor="middle" fontSize="11" fill="var(--se-text-muted)">{label}</text>
    </svg>
  );
}

/* ── 산포도 (Timing) ── */
function ScatterPlot({ points }: { points: { x: number; y: number; label?: string }[] }) {
  if (points.length === 0) {
    return <div className="qcc-empty">보유시간 데이터 없음</div>;
  }
  const maxX = Math.max(...points.map(p => p.x), 120);
  const maxY = Math.max(...points.map(p => p.y), 10);
  const W = 400; const H = 220;
  const pad = { l: 36, r: 12, t: 12, b: 28 };
  const xScale = (v: number) => pad.l + (v / maxX) * (W - pad.l - pad.r);
  const yScale = (v: number) => H - pad.b - (v / maxY) * (H - pad.t - pad.b);

  const xTicks = [0, 15, 30, 45, 60, 80, 100, 120].filter(v => v <= maxX);
  const yTicks = [0, 50, 100, 150, 200].filter(v => v <= maxY);

  return (
    <svg viewBox={`0 0 ${W} ${H}`} className="qcc-scatter">
      {/* 그리드 */}
      {xTicks.map(v => (
        <g key={v}>
          <line x1={xScale(v)} y1={pad.t} x2={xScale(v)} y2={H - pad.b} stroke="rgba(255,255,255,0.05)" />
          <text x={xScale(v)} y={H - 4} textAnchor="middle" fontSize="8" fill="#556">{v}</text>
        </g>
      ))}
      {yTicks.map(v => (
        <g key={v}>
          <line x1={pad.l} y1={yScale(v)} x2={W - pad.r} y2={yScale(v)} stroke="rgba(255,255,255,0.05)" />
          <text x={pad.l - 4} y={yScale(v) + 3} textAnchor="end" fontSize="8" fill="#556">{v}%</text>
        </g>
      ))}
      {/* 축 */}
      <line x1={pad.l} y1={pad.t} x2={pad.l} y2={H - pad.b} stroke="#334" />
      <line x1={pad.l} y1={H - pad.b} x2={W - pad.r} y2={H - pad.b} stroke="#334" />
      {/* 점 */}
      {points.map((p, i) => (
        <circle key={i} cx={xScale(p.x)} cy={yScale(Math.min(p.y, maxY))}
          r="4" fill="#00e676" opacity="0.75" />
      ))}
      {/* 축 레이블 */}
      <text x={(W + pad.l) / 2} y={H} textAnchor="middle" fontSize="8.5" fill="#778">
        Holding Duration (Minutes/Hours)
      </text>
      <text x={8} y={H / 2} textAnchor="middle" fontSize="8.5" fill="#778"
        transform={`rotate(-90,8,${H / 2})`}>P&L (%)</text>
    </svg>
  );
}

/* ── 뷰 쉘 ── */
function ViewShell({
  ctx,
  typeId,
  children,
}: {
  ctx: AnalysisReportContext | null;
  typeId: AnalysisReportTypeId;
  children: React.ReactNode;
}) {
  const def = getReportType(typeId);
  if (!ctx) {
    return (
      <div className="qcc-empty-page">
        <span>📊</span>
        <p>좌측 목록에서 백테스팅 또는 실시간 투자 실행을 선택하세요.</p>
      </div>
    );
  }
  return (
    <div className="qcc-view">
      <div className="qcc-view-meta">
        <span className="qcc-badge">{ctx.symbol}</span>
        <span className="qcc-badge">{ctx.timeframe}</span>
        <span className="qcc-badge qcc-badge--muted">{ctx.sourceTab === 'backtest' ? '백테스팅' : '실시간'}</span>
        <span className="qcc-badge qcc-badge--strategy">{repairUtf8Mojibake(ctx.strategyName)}</span>
      </div>
      {children}
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   01 Standard — Assets & P&L + 3 Hero KPIs + Growth Chart
   ══════════════════════════════════════════════════════════ */
function StandardView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const cagr = a ? estimateCagr(a.totalReturnPct, ctx.signals) : 0;
  const equityVals = ctx.equityCurve.map(p => p.equity);
  const up = (a?.totalReturnPct ?? 0) >= 0;

  return (
    <div className="qcc-standard">
      {/* 좌측: Assets & P&L Summary */}
      <div className="qcc-panel qcc-panel--gold qcc-standard-left">
        <div className="qcc-panel-title">Assets &amp; P&amp;L Summary</div>
        <div className="qcc-summary-rows">
          <div className="qcc-summary-row">
            <span>Total Trades</span>
            <strong>{a?.numberOfPositions ?? '—'}</strong>
          </div>
          <div className="qcc-summary-row">
            <span>Win Rate</span>
            <strong className={colorCls((a?.winRate ?? 0) - 0.5)}>{pctAbs(a?.winRate ?? 0)}</strong>
          </div>
          <div className="qcc-summary-row">
            <span>Profit Factor</span>
            <strong className={colorCls((a?.profitLossRatio ?? 0) - 1)}>{num(a?.profitLossRatio ?? 0)}</strong>
          </div>
          <div className="qcc-summary-row">
            <span>Average P&amp;L</span>
            <strong className={colorCls(a?.avgReturnPct ?? 0)}>{pct(a?.avgReturnPct ?? 0)}</strong>
          </div>
          <div className="qcc-summary-divider" />
          <div className="qcc-summary-row">
            <span>Gross Profit</span>
            <strong className="up">{wonFmt(a?.grossProfit ?? 0)}</strong>
          </div>
          <div className="qcc-summary-row">
            <span>Gross Loss</span>
            <strong className="down">{wonFmt(a?.grossLoss ?? 0)}</strong>
          </div>
          <div className="qcc-summary-row">
            <span>Net P&amp;L</span>
            <strong className={colorCls(a?.totalProfitLoss ?? 0)}>{wonFmt(a?.totalProfitLoss ?? 0)}</strong>
          </div>
        </div>
      </div>

      {/* 우측: 3 Hero KPIs + Growth Chart */}
      <div className="qcc-standard-right">
        <div className="qcc-hero-row">
          {/* Total ROI */}
          <div className={`qcc-hero qcc-hero--roi qcc-hero--${up ? 'up' : 'down'}`}>
            <div className="qcc-hero-label">Total ROI</div>
            <div className="qcc-hero-value">{pct(a?.totalReturnPct ?? 0)}</div>
            <div className="qcc-hero-sub">(CAGR: {pct(cagr)})</div>
            <SparkLine values={equityVals.length >= 2 ? equityVals : [0, 1]} h={40} positive={up} />
          </div>
          {/* MDD */}
          <div className="qcc-hero qcc-hero--mdd">
            <div className="qcc-hero-label">Max Drawdown (MDD)</div>
            <div className={`qcc-hero-value ${(a?.maxDrawdownPct ?? 0) < 0 ? 'down' : ''}`}>
              {pct(a?.maxDrawdownPct ?? 0)}
            </div>
            <div className="qcc-hero-sub-line" />
          </div>
          {/* Sharpe */}
          <div className="qcc-hero qcc-hero--sharpe">
            <div className="qcc-hero-label">Sharpe Ratio</div>
            <div className="qcc-hero-value qcc-hero-gold">{num(a?.sharpeRatio ?? 0, 2)}</div>
            <SparkLine values={equityVals.length >= 2 ? equityVals : [0, 1]} h={36} fill={false} />
          </div>
        </div>

        {/* Assets Growth Chart */}
        <div className="qcc-panel qcc-panel--neutral qcc-growth-panel">
          <div className="qcc-panel-title">Assets Growth Chart</div>
          <div className="qcc-growth-chart-wrap">
            <SparkLine values={equityVals.length >= 2 ? equityVals : [0, 1]} h={200} positive={up} />
          </div>
        </div>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   02 Risk — Drawdown Depth + Volatility + VaR Gauge
   ══════════════════════════════════════════════════════════ */
function RiskView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const dd = useMemo(() => drawdownSeries(ctx.equityCurve), [ctx.equityCurve]);
  const vol = useMemo(() => buildVolatilitySeries(ctx.equityCurve), [ctx.equityCurve]);
  const ddVals = dd.map(d => d.ddPct * 100);
  const volVals = vol.map(v => v.vol);
  const mdd = a?.maxDrawdownPct ?? 0;

  return (
    <div className="qcc-risk">
      <div className="qcc-panel qcc-panel--cyan qcc-risk-left">
        <div className="qcc-panel-title">Stress-Testing Capital Preservation</div>
        <ul className="qcc-risk-bullets">
          <li>
            <strong>Capital Drawdown (Underwater Curve):</strong> Peak-to-trough declines evaluated across the backtest period.
            Current MDD: <span className={mdd < 0 ? 'down' : ''}>{pct(mdd)}</span>.
          </li>
          <li>
            <strong>Volatility Metrics:</strong> Annualized standard deviation of returns and Ulcer Index (depth and duration of drawdowns).
          </li>
          <li>
            <strong>Value at Risk (VaR - 95%):</strong> Maximum expected loss over a specific timeframe at a 95% confidence interval.
          </li>
        </ul>
      </div>
      <div className="qcc-risk-right">
        <div className="qcc-panel qcc-panel--neutral">
          <div className="qcc-panel-title">Drawdown Depth <span className="qcc-panel-sub">Current MDD: {pct(mdd)}</span></div>
          <div className="qcc-chart-wrap">
            <SparkLine values={ddVals.length >= 2 ? ddVals : [0, -0.01]} h={160} positive={false} />
          </div>
        </div>
        <div className="qcc-risk-bottom">
          <div className="qcc-panel qcc-panel--neutral">
            <div className="qcc-panel-title">Volatility Index</div>
            <div className="qcc-chart-wrap">
              <SparkLine values={volVals.length >= 2 ? volVals : [0.5, 0.5, 0.5]} h={100} positive={true} fill={false} />
            </div>
            <div className="qcc-vol-label">Low Volatility Channel</div>
          </div>
          <div className="qcc-panel qcc-panel--neutral">
            <div className="qcc-panel-title">Value at Risk (VaR – 95%)</div>
            <VarGauge varPct={a?.valueAtRisk95 ?? 0.0001} />
          </div>
        </div>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   03 Efficiency — Profit Factor Gauge + Win Rate + Avg Win/Loss
   ══════════════════════════════════════════════════════════ */
function EfficiencyView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const pf = a?.profitLossRatio ?? 0;
  const wr = (a?.winRate ?? 0) * 100;
  const avgWin = a ? (a.grossProfit / (a.numberOfWinning || 1)) / (a.initialCapital || 1) * 100 : 0;
  const avgLoss = a ? Math.abs(a.grossLoss / (a.numberOfLosing || 1)) / (a.initialCapital || 1) * 100 : 0;
  const maxBar = Math.max(avgWin, avgLoss, 0.1);

  return (
    <div className="qcc-efficiency">
      <div className="qcc-panel qcc-panel--gold qcc-efficiency-left">
        <div className="qcc-panel-title">Execution Quality and Profit Extraction</div>
        <div className="qcc-eff-desc">
          <p><strong>Profit Factor [{pf >= 99 ? '99.00' : pf.toFixed(2)}]:</strong> Gross Profit / Gross Loss. {pf >= 10 ? 'An extreme metric indicating near-perfect trade filtering.' : 'Measures overall profitability efficiency.'}</p>
          <p><strong>Win Rate [{pctAbs(a?.winRate ?? 0)}]:</strong> Profitable Trades / Total Trades ({a?.numberOfWinning ?? 0} consecutive wins).</p>
          <p><strong>Average Profit vs. Average Loss:</strong> Avg P&amp;L stands at {pct(a?.avgReturnPct ?? 0)} {(a?.numberOfLosing ?? 0) === 0 ? 'with zero losing trades executed.' : '.'}</p>
        </div>
      </div>
      <div className="qcc-efficiency-right">
        <div className="qcc-eff-center">
          <CircularGauge value={Math.min(pf, 99)} label="Profit Factor" max={99} />
          <div className="qcc-winrate-bar-wrap">
            <div className="qcc-winrate-bar" style={{ width: `${wr}%` }} />
            <div className="qcc-winrate-label">Win Rate {wr.toFixed(1)}%</div>
          </div>
        </div>
        <div className="qcc-eff-bars">
          <div className="qcc-eff-bar-item">
            <div className="qcc-eff-bar-label up">+{avgWin.toFixed(2)}%<br /><span>Avg Win</span></div>
            <div className="qcc-eff-bar-track">
              <div className="qcc-eff-bar-fill up" style={{ height: `${(avgWin / maxBar) * 90}%` }} />
            </div>
          </div>
          <div className="qcc-eff-bar-item">
            <div className="qcc-eff-bar-label down">{avgLoss > 0 ? `-${avgLoss.toFixed(2)}%` : '0.00%'}<br /><span>Avg Loss</span></div>
            <div className="qcc-eff-bar-track">
              <div className="qcc-eff-bar-fill down" style={{ height: `${(avgLoss / maxBar) * 90}%` }} />
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   04 Temporal — Seasonality Monthly Matrix
   ══════════════════════════════════════════════════════════ */
const MON_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function TemporalView({ ctx }: { ctx: AnalysisReportContext }) {
  const monthly = useMemo(() => buildMonthlyMatrix(ctx.trades), [ctx.trades]);
  const ytdMap  = useMemo(() => buildYtdByYear(monthly), [monthly]);
  const years   = useMemo(() => [...new Set(monthly.map(r => r.year))].sort(), [monthly]);
  const map = useMemo(() => new Map(monthly.map(r => [`${r.year}-${r.month}`, r.returnPct])), [monthly]);

  if (years.length === 0) {
    return <div className="qcc-empty-page"><span>📅</span><p>거래 데이터가 부족합니다.</p></div>;
  }

  return (
    <div className="qcc-temporal">
      <div className="qcc-temporal-header">
        <h2 className="qcc-section-title">Seasonality and Time-Based Consistency</h2>
        <p className="qcc-section-sub">Monthly &amp; Yearly Performance Matrix: Verifying that the algorithm generates consistent alpha regardless of specific macro-market phases.</p>
      </div>
      <div className="qcc-temporal-table-wrap">
        <table className="qcc-temporal-table">
          <thead>
            <tr>
              <th />
              {MON_LABELS.map(m => <th key={m}>{m}</th>)}
              <th className="qcc-ytd-col">YTD</th>
            </tr>
          </thead>
          <tbody>
            {years.map(year => {
              const ytd = ytdMap.get(year) ?? 0;
              return (
                <tr key={year}>
                  <td className="qcc-year-cell">{year}</td>
                  {MON_LABELS.map((_, mi) => {
                    const v = map.get(`${year}-${mi}`);
                    const intensity = v != null ? Math.min(1, Math.abs(v) * 8) : 0;
                    const bg = v == null
                      ? 'transparent'
                      : v > 0
                        ? `rgba(0,230,118,${0.15 + intensity * 0.65})`
                        : v < 0
                          ? `rgba(255,23,68,${0.15 + intensity * 0.65})`
                          : 'rgba(60,70,90,0.3)';
                    return (
                      <td key={mi} style={{ background: bg }} className="qcc-month-cell">
                        {v != null ? pct(v, 2) : <span className="qcc-zero">0.00%</span>}
                      </td>
                    );
                  })}
                  <td className={`qcc-ytd-col ${colorCls(ytd)}`}>{pct(ytd, 2)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   05 Logs — Granular Trade Log (Entry+Exit Price, Duration)
   ══════════════════════════════════════════════════════════ */
interface TradeLogFull {
  id: number;
  dateTime: string;
  symbol: string;
  side: string;
  entryPrice: number;
  exitPrice: number;
  qty: number;
  pnlPct: number;
  durationSec: number;
}

function buildFullLogRows(ctx: AnalysisReportContext): TradeLogFull[] {
  if (ctx.sourceTab === 'live' && ctx.paperTrades.length > 0) {
    return ctx.paperTrades.map((t, i) => {
      const ts = t.createdAt ? Math.floor(new Date(t.createdAt).getTime() / 1000) : 0;
      return {
        id: t.id ?? i,
        dateTime: ts ? fmtDateTime(ts) : '—',
        symbol: t.symbol,
        side: t.side,
        entryPrice: t.side === 'SELL' ? t.price : t.price * 0.99,
        exitPrice: t.price,
        qty: t.quantity,
        pnlPct: t.realizedPnlDelta != null ? t.realizedPnlDelta / (t.price * t.quantity || 1) * 100 : 0,
        durationSec: 0,
      };
    });
  }

  const buys = new Map<string, { time: number; price: number; qty: number }>();
  const rows: TradeLogFull[] = [];
  const sorted = [...ctx.trades].sort((a, b) => a.time - b.time);
  for (const t of sorted) {
    if (t.side === 'buy') {
      buys.set(t.symbol, { time: t.time, price: t.price, qty: t.quantity });
    } else {
      const buy = buys.get(t.symbol);
      const entry = buy?.price ?? t.price;
      const dur = buy ? t.time - buy.time : 0;
      rows.push({
        id: t.id,
        dateTime: fmtDateTime(t.time),
        symbol: t.symbol,
        side: 'BUY/SELL',
        entryPrice: entry,
        exitPrice: t.price,
        qty: t.quantity,
        pnlPct: (t.pnlPct ?? 0) * 100,
        durationSec: dur,
      });
      buys.delete(t.symbol);
    }
  }
  return rows;
}

function LogsView({ ctx }: { ctx: AnalysisReportContext }) {
  const rows = useMemo(() => buildFullLogRows(ctx), [ctx]);

  return (
    <div className="qcc-logs">
      <h2 className="qcc-section-title">Granular Transparency for Every Execution</h2>
      <p className="qcc-section-sub">Data derived from the GoldenChart execution feed (100ms latency WebSocket).</p>
      <div className="qcc-logs-table-wrap">
        <table className="qcc-logs-table">
          <thead>
            <tr>
              <th>Date &amp; Time</th>
              <th>Symbol</th>
              <th>Side</th>
              <th>Entry Price</th>
              <th>Exit Price</th>
              <th>Qty</th>
              <th>P&amp;L (%)</th>
              <th>Duration</th>
            </tr>
          </thead>
          <tbody>
            {rows.length === 0 ? (
              <tr><td colSpan={8} className="qcc-empty-cell">거래 내역 없음</td></tr>
            ) : rows.map(r => (
              <tr key={r.id} className={r.pnlPct > 0 ? 'qcc-row--up' : r.pnlPct < 0 ? 'qcc-row--down' : ''}>
                <td>{r.dateTime}</td>
                <td className="qcc-sym">{r.symbol.replace('KRW-', 'KRW-')}</td>
                <td className="qcc-side">{r.side}</td>
                <td className="qcc-num">{formatUpbitKrwPrice(r.entryPrice)}</td>
                <td className="qcc-num">{formatUpbitKrwPrice(r.exitPrice)}</td>
                <td className="qcc-num">{r.qty.toFixed(3)}</td>
                <td className={r.pnlPct >= 0 ? 'up' : 'down'}>{r.pnlPct >= 0 ? '+' : ''}{r.pnlPct.toFixed(1)}%</td>
                <td className="qcc-dur">{r.durationSec > 0 ? fmtDuration(r.durationSec) : '—'}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   06 Allocation — Donut + Legend Table
   ══════════════════════════════════════════════════════════ */
function AllocationView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const slices = useMemo(
    () => buildAllocationSlices(ctx.trades, a?.initialCapital ?? 10_000_000),
    [ctx.trades, a],
  );
  const totalCapital = a?.initialCapital ?? 10_000_000;

  return (
    <div className="qcc-allocation">
      <div className="qcc-alloc-left">
        <h2 className="qcc-section-title">Capital Distribution<br />and Asset Weights</h2>
        <p className="qcc-section-sub">
          Dynamic Capital Allocation: Visualizing the distribution of the initial simulated capital across different cryptocurrency pairs during the backtest.
        </p>
        <p className="qcc-section-sub" style={{ marginTop: 8 }}>
          Exposure Analysis: Validating risk distribution across multiple assets in accordance with the CAPITAL_PCT engine settings.
        </p>
        <div className="qcc-alloc-legend">
          {slices.map(sl => (
            <div key={sl.symbol} className="qcc-alloc-row">
              <span className="qcc-alloc-dot" style={{ background: sl.color }} />
              <span className="qcc-alloc-sym">{sl.symbol.replace('KRW-', 'KRW-')}</span>
              <span className="qcc-alloc-pct">{(sl.pct * 100).toFixed(2)}%</span>
              <span className="qcc-alloc-krw">₩{formatUpbitKrwPrice(sl.capitalKrw)}</span>
            </div>
          ))}
        </div>
      </div>
      <div className="qcc-alloc-right">
        <DonutChart
          slices={slices.map(sl => ({ pct: sl.pct * 100, color: sl.color }))}
          centerLabel="Total Capital Deployed"
          centerValue={`₩${Math.round(totalCapital / 1e8).toLocaleString()}억`}
        />
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   07 Stats — Return Distribution Histogram
   ══════════════════════════════════════════════════════════ */
function StatsView({ ctx }: { ctx: AnalysisReportContext }) {
  const buckets = useMemo(() => buildReturnDistribution(ctx.trades), [ctx.trades]);
  const maxCount = Math.max(...buckets.map(b => b.count), 1);

  const W = 500; const H = 240;
  const pad = { l: 36, r: 20, t: 20, b: 32 };
  const bw = buckets.length > 0 ? (W - pad.l - pad.r) / buckets.length - 2 : 0;

  return (
    <div className="qcc-stats">
      <div className="qcc-panel qcc-panel--cyan qcc-stats-left">
        <div className="qcc-panel-title">The Shape of Returns: Statistical Distribution</div>
        <ul className="qcc-risk-bullets">
          <li><strong>Right Tail:</strong> Outlier massive wins — extreme positive P&L executions.</li>
          <li><strong>Center Mass:</strong> The bulk of standard, high-probability trades falling in the +1% to +6% range.</li>
          <li><strong>Left Tail:</strong> Frequency and severity of losing trades.</li>
        </ul>
        <div className="qcc-stats-summary">
          {buckets.filter(b => b.count > 0).slice(0, 5).map(b => (
            <div key={b.bucket} className="qcc-stats-row">
              <span>{b.bucket}</span>
              <strong>{b.count}</strong>
            </div>
          ))}
        </div>
      </div>
      <div className="qcc-stats-chart">
        {buckets.length === 0 ? (
          <div className="qcc-empty">거래 분포 데이터 없음</div>
        ) : (
          <svg viewBox={`0 0 ${W} ${H}`} className="qcc-hist-svg">
            {/* 그리드 */}
            {[0, 25, 50, 75, 100].map(p => {
              const y = H - pad.b - (p / 100) * (H - pad.t - pad.b);
              return <line key={p} x1={pad.l} y1={y} x2={W - pad.r} y2={y} stroke="rgba(255,255,255,0.05)" />;
            })}
            {/* 막대 */}
            {buckets.map((b, i) => {
              const barH = (b.count / maxCount) * (H - pad.t - pad.b);
              const x = pad.l + i * ((W - pad.l - pad.r) / buckets.length);
              const isExtreme = b.from >= 100;
              const fill = isExtreme ? '#ffd700' : b.from >= 0 ? '#00e676' : '#ff1744';
              return (
                <g key={b.bucket}>
                  <rect
                    x={x + 1} y={H - pad.b - barH}
                    width={bw} height={barH}
                    fill={fill} opacity="0.85"
                  />
                  {b.count > 0 && (
                    <text x={x + bw / 2} y={H - pad.b - barH - 4}
                      textAnchor="middle" fontSize="8" fill={fill}>{b.count}</text>
                  )}
                  {isExtreme && b.count > 0 && (
                    <text x={x + bw / 2} y={H - pad.b - barH - 14}
                      textAnchor="middle" fontSize="9" fill="#ffd700">+{b.from}%</text>
                  )}
                  <text x={x + bw / 2} y={H - 4}
                    textAnchor="middle" fontSize="7" fill="#556">{b.bucket.replace('%', '')}</text>
                </g>
              );
            })}
            {/* Y축 */}
            {[0, 25, 50, 75, 100].map(p => {
              const cnt = Math.round((p / 100) * maxCount);
              const y = H - pad.b - (p / 100) * (H - pad.t - pad.b);
              return <text key={p} x={pad.l - 4} y={y + 3} textAnchor="end" fontSize="8" fill="#556">{cnt}</text>;
            })}
            {/* 종 곡선 (정규분포 근사) */}
            {buckets.length >= 3 && (() => {
              const pts = buckets.map((_, i) => {
                const x = pad.l + (i + 0.5) * ((W - pad.l - pad.r) / buckets.length);
                const normI = (i - buckets.length / 2) / (buckets.length / 4);
                const bell = Math.exp(-0.5 * normI * normI) * 0.9;
                const y = H - pad.b - bell * (H - pad.t - pad.b);
                return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`;
              });
              return <path d={pts.join(' ')} fill="none" stroke="#00e5ff" strokeWidth="2" opacity="0.6" />;
            })()}
            <text x={(W + pad.l) / 2} y={H} textAnchor="middle" fontSize="9" fill="#778">Return % Buckets</text>
            <text x={10} y={H / 2} textAnchor="middle" fontSize="9" fill="#778"
              transform={`rotate(-90,10,${H / 2})`}>Frequency</text>
          </svg>
        )}
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   08 AI Feedback — LLM Terminal Output
   ══════════════════════════════════════════════════════════ */
function AiFeedbackView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const mdd = a?.maxDrawdownPct ?? 0;
  const sharpe = a?.sharpeRatio ?? 0;
  const pf = a?.profitLossRatio ?? 0;
  const wr = a?.winRate ?? 0;

  const lines: { key: string; label: string; text: string }[] = [
    {
      key: 'market',
      label: 'Market Context',
      text: sharpe > 1
        ? `Strategy performed optimally during the backtest period. Sharpe ratio of ${sharpe.toFixed(2)} confirms risk-adjusted alpha generation with ${pctAbs(wr)} win rate.`
        : `Strategy shows moderate performance. Win rate of ${pctAbs(wr)} with Sharpe ${sharpe.toFixed(2)} suggests potential for parameter optimization.`,
    },
    {
      key: 'indicator',
      label: 'Indicator Interpretation',
      text: pf > 2
        ? `Profit Factor of ${pf.toFixed(2)} indicates highly efficient trade filtering. Gross profit significantly exceeds gross loss, validating the entry/exit logic.`
        : `Profit Factor of ${pf.toFixed(2)} is ${pf >= 1 ? 'profitable but moderate' : 'below breakeven'}. Review entry conditions to improve signal quality.`,
    },
    {
      key: 'weakness',
      label: 'Weakness Detection',
      text: Math.abs(mdd) > 0.1
        ? `MDD of ${pct(mdd)} suggests significant drawdown exposure. Consider adding an ATR-based trailing stop or reducing position sizing during volatile periods.`
        : `Maximum drawdown remains controlled at ${pct(mdd)}. Current risk management parameters appear well-calibrated for the tested market conditions.`,
    },
  ];

  return (
    <div className="qcc-ai">
      <div className="qcc-panel qcc-panel--gold qcc-ai-panel">
        <div className="qcc-ai-header">
          <div>
            <h2 className="qcc-section-title" style={{ color: '#ffd700' }}>Qualitative Insights via Local LLM (qwen3:4b)</h2>
            <p className="qcc-section-sub">Powered by <span className="qcc-ai-hl">LangGraph strategy_tool</span> and <span className="qcc-ai-hl">backtest_tool</span> orchestration.</p>
          </div>
          <div className="qcc-ai-icon">
            <svg viewBox="0 0 60 60" width="52" height="52" fill="none" stroke="#00e5ff" strokeWidth="1.5">
              <circle cx="10" cy="10" r="6" /><circle cx="50" cy="10" r="6" />
              <circle cx="10" cy="50" r="6" /><circle cx="50" cy="50" r="6" />
              <circle cx="30" cy="30" r="7" />
              <line x1="16" y1="10" x2="44" y2="10" /><line x1="10" y1="16" x2="10" y2="44" />
              <line x1="16" y1="50" x2="44" y2="50" /><line x1="50" y1="16" x2="50" y2="44" />
              <line x1="16" y1="16" x2="24" y2="24" /><line x1="44" y1="16" x2="36" y2="24" />
              <line x1="16" y1="44" x2="24" y2="36" /><line x1="44" y1="44" x2="36" y2="36" />
            </svg>
          </div>
        </div>
        <div className="qcc-ai-terminal">
          {lines.map(l => (
            <div key={l.key} className="qcc-ai-line">
              <span className="qcc-ai-prompt">&gt;</span>
              <span className="qcc-ai-label"> {l.label}: </span>
              <span className="qcc-ai-text">{l.text}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   09 Comparative — Strategy vs. Buy & Hold
   ══════════════════════════════════════════════════════════ */
function ComparativeView({ ctx }: { ctx: AnalysisReportContext }) {
  const a = ctx.analysis;
  const stratVals = ctx.equityCurve.map(p => p.equity);
  const initial = a?.initialCapital ?? 10_000_000;

  // Buy & Hold 시리즈 재구성 (첫 신호 진입가→마지막 신호 가격)
  const benchVals = useMemo(() => {
    const signals = ctx.signals;
    if (signals.length < 2) return stratVals.map(() => initial);
    const p0 = signals[0].price;
    return ctx.equityCurve.map((_, i) => {
      const s = signals[Math.min(i, signals.length - 1)];
      return initial * (s.price / p0);
    });
  }, [ctx.signals, ctx.equityCurve, initial]);

  const W = 600; const H = 300;
  const pad = { l: 44, r: 80, t: 16, b: 28 };

  const allVals = [...stratVals, ...benchVals];
  const lo = Math.min(...allVals);
  const hi = Math.max(...allVals);
  const range = hi - lo || 1;
  const xS = (i: number) => pad.l + (i / (stratVals.length - 1)) * (W - pad.l - pad.r);
  const yS = (v: number) => H - pad.b - ((v - lo) / range) * (H - pad.t - pad.b);

  const stratPath = stratVals.map((v, i) => `${i === 0 ? 'M' : 'L'}${xS(i).toFixed(1)},${yS(v).toFixed(1)}`).join(' ');
  const benchPath = benchVals.map((v, i) => `${i === 0 ? 'M' : 'L'}${xS(i).toFixed(1)},${yS(v).toFixed(1)}`).join(' ');
  const benchPct = a?.buyAndHoldReturnPct ?? (benchVals.length > 1 ? (benchVals[benchVals.length - 1] - initial) / initial : 0);
  const stratRetFinal = a?.totalReturnPct ?? 0;

  return (
    <div className="qcc-comparative">
      <div className="qcc-panel qcc-panel--cyan qcc-comp-header">
        <h2 className="qcc-section-title">Strategy vs. Benchmark: Alpha Generation</h2>
        <p className="qcc-section-sub">Relative Performance Analytics: Proving the value of active, algorithmically traded signals over passive market exposure.</p>
      </div>
      <div className="qcc-comp-chart-wrap qcc-panel qcc-panel--neutral">
        <svg viewBox={`0 0 ${W} ${H}`} className="qcc-comp-svg">
          {/* 그리드 */}
          {[0, 25, 50, 75, 100].map(p => {
            const v = lo + (p / 100) * range;
            const y = yS(v);
            const lbl = `${(((v - initial) / initial) * 100).toFixed(0)}%`;
            return (
              <g key={p}>
                <line x1={pad.l} y1={y} x2={W - pad.r} y2={y} stroke="rgba(255,255,255,0.05)" />
                <text x={pad.l - 4} y={y + 3} textAnchor="end" fontSize="8" fill="#556">{lbl}</text>
              </g>
            );
          })}
          {/* Bench area */}
          <path d={`${benchPath} L${xS(benchVals.length - 1)},${H - pad.b} L${xS(0)},${H - pad.b} Z`}
            fill="rgba(80,80,80,0.2)" />
          <path d={benchPath} fill="none" stroke="#666" strokeWidth="1.5" strokeDasharray="4 3" />
          {/* Strategy */}
          <path
            d={`${stratPath} L${xS(stratVals.length - 1)},${H - pad.b} L${xS(0)},${H - pad.b} Z`}
            fill="rgba(0,230,118,0.08)" />
          <path d={stratPath} fill="none" stroke="#00e676" strokeWidth="2.5"
            style={{ filter: 'drop-shadow(0 0 4px #00e676)' }} />
          {/* 우측 레이블 */}
          <text x={W - pad.r + 6} y={yS(stratVals[stratVals.length - 1]) + 4} fontSize="9" fill="#00e676">
            Return +{(stratRetFinal * 100).toFixed(0)}%
          </text>
          <text x={W - pad.r + 6} y={yS(stratVals[stratVals.length - 1]) + 16} fontSize="8" fill="#ff1744">
            Max drawdown {pct(a?.maxDrawdownPct ?? 0)}
          </text>
          <text x={W - pad.r + 6} y={yS(stratVals[stratVals.length - 1]) + 28} fontSize="8" fill="#ffd700">
            Sharpe {num(a?.sharpeRatio ?? 0)}
          </text>
          <text x={W - pad.r + 6} y={yS(benchVals[benchVals.length - 1]) + 4} fontSize="9" fill="#888">
            Benchmark +{(benchPct * 100).toFixed(0)}%
          </text>
        </svg>
        <div className="qcc-comp-legend">
          <span className="qcc-comp-leg qcc-comp-leg--strat">━ GoldenChart Strategy</span>
          <span className="qcc-comp-leg qcc-comp-leg--bench">┅ Buy &amp; Hold Benchmark</span>
        </div>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   10 Timing — Holding Periods & Entry Precision Scatter
   ══════════════════════════════════════════════════════════ */
function TimingView({ ctx }: { ctx: AnalysisReportContext }) {
  const pts = useMemo(
    () => buildHoldingPoints(ctx.signals, ctx.symbol),
    [ctx.signals, ctx.symbol],
  );

  return (
    <div className="qcc-timing">
      <div className="qcc-panel qcc-panel--gold qcc-timing-left">
        <div className="qcc-panel-title">Holding Periods &amp; Entry Precision</div>
        <div className="qcc-eff-desc">
          <p>
            <span className="qcc-ai-label">Time-Efficiency: </span>
            Analyzing how long capital is locked up to generate returns, evaluating{' '}
            <span className="qcc-ai-hl">AVERAGE_HOLDING_PERIOD</span> against the execution type.
          </p>
          <p>
            <span className="qcc-ai-label">Cluster Analysis: </span>
            High concentration of short-duration trades signifies highly efficient, surgical market entries.
          </p>
        </div>
        {pts.length > 0 && (
          <div className="qcc-timing-stats">
            <div className="qcc-timing-stat">
              <span>Avg Duration</span>
              <strong>{fmtDuration(pts.reduce((s, p) => s + p.durationMin * 60, 0) / pts.length)}</strong>
            </div>
            <div className="qcc-timing-stat">
              <span>Total Trades</span>
              <strong>{pts.length}</strong>
            </div>
            <div className="qcc-timing-stat">
              <span>Avg P&amp;L</span>
              <strong className="up">+{(pts.reduce((s, p) => s + p.pnlPct, 0) / pts.length).toFixed(2)}%</strong>
            </div>
          </div>
        )}
      </div>
      <div className="qcc-panel qcc-panel--neutral qcc-timing-right">
        <ScatterPlot
          points={pts.map(p => ({ x: Math.min(p.durationMin, 200), y: Math.abs(p.pnlPct) }))}
        />
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════
   메인 라우터
   ══════════════════════════════════════════════════════════ */
export default function AnalysisReportViews({ typeId, ctx, theme, compareBacktest }: Props) {
  const body = useMemo(() => {
    if (!ctx) return null;
    switch (typeId) {
      case 'standard':   return <StandardView ctx={ctx} />;
      case 'risk':       return <RiskView ctx={ctx} />;
      case 'efficiency': return <EfficiencyView ctx={ctx} />;
      case 'temporal':   return <TemporalView ctx={ctx} />;
      case 'logs':       return <LogsView ctx={ctx} />;
      case 'allocation': return <AllocationView ctx={ctx} />;
      case 'stats':      return <StatsView ctx={ctx} />;
      case 'ai-feedback': return <AiFeedbackView ctx={ctx} />;
      case 'comparative': return <ComparativeView ctx={ctx} />;
      case 'timing':     return <TimingView ctx={ctx} />;
      default:           return <StandardView ctx={ctx} />;
    }
  }, [typeId, ctx, theme]);

  return (
    <ViewShell typeId={typeId} ctx={ctx}>
      {body}
    </ViewShell>
  );
}
