/**
 * Monte Carlo fan chart — confidence bands (p10/p25/p75/p90) + median line.
 *
 * Renders five layered series in ECharts:
 *   1. p10                 invisible baseline (line, transparent, no fill)
 *   2. (p25 - p10) band    stacks on p10 with a low-opacity fill
 *   3. (p75 - p25) band    stacks on top with mid-opacity (the inter-quartile)
 *   4. (p90 - p75) band    stacks on top with low-opacity (upper tail)
 *   5. p50 median          standalone solid line, drawn last so it sits above
 *
 * Stacking the deltas instead of plotting raw quantiles keeps the band
 * fills clean — ECharts area renderers fill to the *baseline* by default,
 * which would overlap badly if we just plotted p10/p25/p50/p75/p90 lines
 * each with their own area.
 */
import { useMemo } from 'react';
import ReactECharts from 'echarts-for-react';
import type { EChartsOption } from 'echarts';

import type { ProjectionPoint } from '@/api/client';
import { gbpCompact } from '@/lib/format';

interface Props {
  yearly: ProjectionPoint[];
  height?: number;
  showWithdrawal?: boolean;
}

export function FanChart({ yearly, height = 360, showWithdrawal = false }: Props) {
  const option = useMemo<EChartsOption>(() => buildFan(yearly, showWithdrawal), [
    yearly,
    showWithdrawal,
  ]);
  if (yearly.length === 0) {
    return <p className="text-sm text-slate-500">No projection data.</p>;
  }
  return <ReactECharts option={option} style={{ height, width: '100%' }} notMerge lazyUpdate />;
}

function buildFan(yearly: ProjectionPoint[], showWithdrawal: boolean): EChartsOption {
  const years = yearly.map((p) => p.year_idx);
  const p10 = yearly.map((p) => num(p.p10_portfolio_gbp));
  const p25 = yearly.map((p) => num(p.p25_portfolio_gbp));
  const p50 = yearly.map((p) => num(p.p50_portfolio_gbp));
  const p75 = yearly.map((p) => num(p.p75_portfolio_gbp));
  const p90 = yearly.map((p) => num(p.p90_portfolio_gbp));
  const wd = yearly.map((p) => num(p.p50_withdrawal_gbp));

  // Deltas — what each band stacks on top of the previous.
  const d_25_10 = p25.map((v, i) => Math.max(0, v - p10[i]!));
  const d_75_25 = p75.map((v, i) => Math.max(0, v - p25[i]!));
  const d_90_75 = p90.map((v, i) => Math.max(0, v - p75[i]!));

  const series: EChartsOption['series'] = [
    {
      name: 'p10',
      type: 'line',
      stack: 'fan',
      data: p10,
      lineStyle: { opacity: 0 },
      itemStyle: { opacity: 0 },
      symbol: 'none',
      tooltip: { show: false },
    },
    {
      name: 'p10–p25',
      type: 'line',
      stack: 'fan',
      data: d_25_10,
      lineStyle: { opacity: 0 },
      symbol: 'none',
      areaStyle: { color: 'rgba(80, 110, 230, 0.12)' },
      tooltip: { show: false },
    },
    {
      name: 'p25–p75 (IQR)',
      type: 'line',
      stack: 'fan',
      data: d_75_25,
      lineStyle: { opacity: 0 },
      symbol: 'none',
      areaStyle: { color: 'rgba(80, 110, 230, 0.28)' },
      tooltip: { show: false },
    },
    {
      name: 'p75–p90',
      type: 'line',
      stack: 'fan',
      data: d_90_75,
      lineStyle: { opacity: 0 },
      symbol: 'none',
      areaStyle: { color: 'rgba(80, 110, 230, 0.12)' },
      tooltip: { show: false },
    },
    {
      name: 'median',
      type: 'line',
      data: p50,
      lineStyle: { width: 2, color: 'rgb(40, 70, 200)' },
      itemStyle: { color: 'rgb(40, 70, 200)' },
      symbol: 'none',
      smooth: true,
      z: 10,
    },
    {
      name: 'p10',
      type: 'line',
      data: p10,
      lineStyle: { width: 1, color: 'rgba(40, 70, 200, 0.5)', type: 'dashed' },
      symbol: 'none',
      smooth: true,
      z: 5,
    },
    {
      name: 'p90',
      type: 'line',
      data: p90,
      lineStyle: { width: 1, color: 'rgba(40, 70, 200, 0.5)', type: 'dashed' },
      symbol: 'none',
      smooth: true,
      z: 5,
    },
  ];

  if (showWithdrawal) {
    series.push({
      name: 'median withdrawal',
      type: 'line',
      yAxisIndex: 1,
      data: wd,
      lineStyle: { width: 1, color: 'rgb(190, 90, 60)' },
      symbol: 'none',
      smooth: true,
    });
  }

  return {
    grid: { left: 60, right: showWithdrawal ? 70 : 24, top: 30, bottom: 40 },
    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        const arr = Array.isArray(params) ? params : [params];
        const year = arr[0]?.name ?? '';
        const idx = years.indexOf(Number(year));
        if (idx < 0) return '';
        return [
          `<b>Year ${year}</b>`,
          `p10: ${gbpCompact(p10[idx]!)}`,
          `p25: ${gbpCompact(p25[idx]!)}`,
          `<b>p50: ${gbpCompact(p50[idx]!)}</b>`,
          `p75: ${gbpCompact(p75[idx]!)}`,
          `p90: ${gbpCompact(p90[idx]!)}`,
          showWithdrawal ? `withdrawal: ${gbpCompact(wd[idx]!)}` : '',
        ]
          .filter(Boolean)
          .join('<br/>');
      },
    },
    legend: {
      bottom: 0,
      data: [
        'median',
        'p10',
        'p90',
        'p25–p75 (IQR)',
        ...(showWithdrawal ? ['median withdrawal'] : []),
      ],
    },
    xAxis: {
      type: 'category',
      data: years,
      name: 'years from now',
      nameLocation: 'middle',
      nameGap: 24,
    },
    yAxis: [
      {
        type: 'value',
        name: 'portfolio (real GBP)',
        nameLocation: 'middle',
        nameGap: 50,
        axisLabel: { formatter: (v: number) => gbpCompact(v) },
      },
      ...(showWithdrawal
        ? [
            {
              type: 'value' as const,
              name: 'withdrawal',
              nameLocation: 'middle' as const,
              nameGap: 50,
              axisLabel: { formatter: (v: number) => gbpCompact(v) },
            },
          ]
        : []),
    ],
    series,
  };
}

function num(s: string): number {
  return Number(s);
}
