<script lang="ts">
  import { onMount } from 'svelte';
  import * as Plot from '@observablehq/plot';
  import type { AthleteJson, MmpCurve, ActivitySummary } from '../lib/types';

  export let athlete: AthleteJson;
  export let activities: ActivitySummary[] = [];
  export let base: string = '/';

  // ── Range selection ────────────────────────────────────────────────────────
  type RangeKey = 'all_time' | 'last_365d' | 'last_90d' | string;

  interface Season { name: string; start: string; end: string }
  const seasons: Season[] = (athlete as any).seasons ?? [];

  let selectedRanges: Set<RangeKey> = new Set(['all_time']);

  const PRESET_LABELS: Record<string, string> = {
    all_time:   'All time',
    last_365d:  'Last 365 d',
    last_90d:   'Last 90 d',
  };

  // Colours for overlaid curves (cycling through a palette)
  const PALETTE = [
    '#60a5fa', // blue-400
    '#f97316', // orange-500
    '#34d399', // emerald-400
    '#a78bfa', // violet-400
    '#f43f5e', // rose-500
    '#facc15', // yellow-400
    '#22d3ee', // cyan-400
  ];

  const allRangeKeys = [
    ...Object.keys(PRESET_LABELS),
    ...seasons.map(s => s.name),
  ];

  // Stable color per key — based on position in allRangeKeys, not in the active selection
  const RANGE_COLOR: Record<string, string> = Object.fromEntries(
    allRangeKeys.map((k, i) => [k, PALETTE[i % PALETTE.length]])
  );

  function curveColor(key: RangeKey): string {
    return RANGE_COLOR[key] ?? PALETTE[0];
  }

  // ── Record-holder lookup ───────────────────────────────────────────────────

  const _now = new Date();
  function _cutoff(days: number): string {
    return new Date(_now.getTime() - days * 86400000).toISOString();
  }
  const cutoff365 = _cutoff(365);
  const cutoff90  = _cutoff(90);

  function activitiesInRange(key: RangeKey): ActivitySummary[] {
    if (key === 'all_time')  return activities;
    if (key === 'last_365d') return activities.filter(a => a.started_at >= cutoff365);
    if (key === 'last_90d')  return activities.filter(a => a.started_at >= cutoff90);
    const season = seasons.find(s => s.name === key);
    if (!season) return [];
    return activities.filter(a => a.started_at >= season.start && a.started_at <= season.end + 'T23:59:59');
  }

  // For each (rangeKey, duration_s) → the activity that holds the record.
  // Precomputed as a Map to avoid re-scanning on every render.
  function buildRecordMap(key: RangeKey): Map<number, ActivitySummary> {
    const map = new Map<number, ActivitySummary>();
    for (const a of activitiesInRange(key)) {
      if (!a.mmp) continue;
      for (const [d, w] of a.mmp) {
        const cur = map.get(d);
        const curW = cur ? (cur.mmp!.find(([dd]) => dd === d)?.[1] ?? 0) : -1;
        if (w > curW) map.set(d, a);
      }
    }
    return map;
  }

  // ── MMP curve computation ──────────────────────────────────────────────────

  function mergeMmps(mmps: MmpCurve[]): MmpCurve {
    const best = new Map<number, number>();
    for (const curve of mmps) {
      for (const [d, w] of curve) {
        const prev = best.get(d);
        if (prev === undefined || w > prev) best.set(d, w);
      }
    }
    return [...best.entries()].sort((a, b) => a[0] - b[0]) as MmpCurve;
  }

  function mmpsForRange(key: RangeKey): MmpCurve | null {
    // Built-in preset ranges come from athlete.json (pre-computed at extract time)
    if (key in PRESET_LABELS) {
      return (athlete.power_curve as any)[key] ?? null;
    }
    // User-defined seasons: compute on-the-fly from per-activity mmp in index.json
    const season = seasons.find(s => s.name === key);
    if (!season) return null;
    const curves = activities
      .filter(a => a.mmp && a.started_at >= season.start && a.started_at <= season.end + 'T23:59:59')
      .map(a => a.mmp!);
    return curves.length ? mergeMmps(curves) : null;
  }

  // ── Chart rendering ────────────────────────────────────────────────────────

  let chartEl: HTMLElement;

  function formatDuration(s: number): string {
    if (s < 60)   return `${s}s`;
    if (s < 3600) return `${Math.round(s / 60)}min`;
    return `${s / 3600}h`;
  }

  $: selectedKeys = [...selectedRanges];

  // Record maps per selected range — recomputed when activities or selected keys change.
  $: recordMaps = Object.fromEntries(selectedKeys.map(k => [k, buildRecordMap(k)]));

  $: plotData = selectedKeys.flatMap((key) => {
    const curve = mmpsForRange(key);
    if (!curve) return [];
    const rmap = recordMaps[key] ?? new Map();
    return curve.map(([d, w]) => {
      const act = rmap.get(d);
      return { d, w, label: key, actTitle: act?.title ?? null, actId: act?.id ?? null };
    });
  });

  $: colorMap = Object.fromEntries(selectedKeys.map(k => [k, curveColor(k)]));

  function getAxisColor() {
    return document.documentElement.getAttribute('data-theme') === 'light' ? '#52525b' : '#a1a1aa';
  }

  function renderChart(data: typeof plotData, cmap: typeof colorMap) {
    if (!chartEl) return;
    chartEl.innerHTML = '';
    if (!data.length) return;

    const labelFn = (key: string) =>
      PRESET_LABELS[key] ?? key;

    const chart = Plot.plot({
      width: chartEl.clientWidth || 700,
      height: 320,
      marginLeft: 52,
      marginBottom: 40,
      style: { background: 'transparent', color: getAxisColor() },
      x: {
        type: 'log',
        label: 'Duration',
        tickFormat: (d: number) => formatDuration(d),
        grid: true,
        domain: [data[0]?.d ?? 1, Math.max(7200, ...data.map(d => d.d))],
      },
      y: {
        label: 'Avg power (W)',
        grid: true,
        zero: true,
      },
      color: {
        domain: selectedKeys,
        range: selectedKeys.map(k => curveColor(k)),
        legend: selectedKeys.length > 1,
      },
      marks: [
        Plot.line(data, {
          x: 'd',
          y: 'w',
          stroke: 'label',
          strokeWidth: 2,
          curve: 'monotone-x',
        }),
        Plot.dot(data, {
          x: 'd',
          y: 'w',
          fill: 'label',
          r: 3,
          tip: true,
          title: (d: any) => `${labelFn(d.label)}\n${formatDuration(d.d)}: ${d.w} W${d.actTitle ? '\n' + d.actTitle : ''}`,
        }),
        ...(athlete.ftp_w ? [
          Plot.ruleY([athlete.ftp_w], {
            stroke: '#71717a',
            strokeDasharray: '4 3',
          }),
          Plot.text([{ x: 3600, y: athlete.ftp_w }], {
            x: 'x', y: 'y',
            text: () => `FTP ${athlete.ftp_w} W`,
            fill: '#71717a',
            fontSize: 11,
            dy: -6,
            textAnchor: 'end',
          }),
        ] : []),
      ],
    });

    chartEl.appendChild(chart);
  }

  $: renderChart(plotData, colorMap);

  // Re-render on resize — use indirect call so we always get current reactive values
  let currentPlotData = plotData;
  let currentColorMap = colorMap;
  $: currentPlotData = plotData;
  $: currentColorMap = colorMap;

  onMount(() => {
    const ro = new ResizeObserver(() => renderChart(currentPlotData, currentColorMap));
    ro.observe(chartEl);
    const mo = new MutationObserver(() => renderChart(currentPlotData, currentColorMap));
    mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
    return () => { ro.disconnect(); mo.disconnect(); };
  });

  // ── Toggle helpers ─────────────────────────────────────────────────────────

  function toggleRange(key: RangeKey) {
    const next = new Set(selectedRanges);
    if (next.has(key)) {
      if (next.size > 1) next.delete(key); // always keep at least one
    } else {
      next.add(key);
    }
    selectedRanges = next;
  }

  // ── Records table ──────────────────────────────────────────────────────────

  let tableRange: RangeKey = 'all_time';

  $: tableRecordMap = buildRecordMap(tableRange);

  $: tableRows = (() => {
    const curve = mmpsForRange(tableRange);
    if (!curve) return [];
    return curve.map(([d, w]) => {
      const act = tableRecordMap.get(d);
      return { d, w, act };
    });
  })();

  function fmtDate(iso: string): string {
    return new Date(iso).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
  }
</script>

<style>
  /* Plot tooltips always have a white background — force black text for contrast */
  :global(.plot-tip text) { fill: #18181b !important; }
</style>

<!-- Range selector pills -->
<div class="flex flex-wrap gap-2 mb-4">
  {#each allRangeKeys as key}
    {@const active = selectedRanges.has(key)}
    {@const color = curveColor(key)}
    <button
      on:click={() => toggleRange(key)}
      class="px-3 py-1 rounded-full text-sm font-medium border transition-colors"
      style={active
        ? `background:${color}22; border-color:${color}; color:${color}`
        : 'background:transparent; border-color:#3f3f46; color:#71717a'}
    >
      {PRESET_LABELS[key] ?? key}
    </button>
  {/each}
</div>

<!-- Chart -->
<div bind:this={chartEl} class="w-full min-h-[320px]"></div>

{#if !plotData.length}
  <p class="text-zinc-500 text-sm mt-4">No power data for the selected range.</p>
{/if}

<!-- Records table -->
{#if tableRows.length}
  <div class="mt-6">
    <div class="flex items-center justify-between mb-3">
      <h4 class="text-xs font-semibold text-zinc-400 uppercase tracking-wide">Power records</h4>
      <div class="flex gap-1">
        {#each allRangeKeys as key}
          <button
            on:click={() => tableRange = key}
            class="px-2.5 py-1 rounded-full text-xs font-medium border transition-colors"
            style={tableRange === key
              ? `background:${curveColor(key)}22; border-color:${curveColor(key)}; color:${curveColor(key)}`
              : 'background:transparent; border-color:#3f3f46; color:#71717a'}
          >{PRESET_LABELS[key] ?? key}</button>
        {/each}
      </div>
    </div>
    <div class="rounded-xl border border-zinc-800 overflow-hidden">
      <table class="w-full text-sm">
        <thead class="border-b border-zinc-800">
          <tr class="text-left text-xs text-zinc-500">
            <th class="px-4 py-2">Duration</th>
            <th class="px-4 py-2 text-right">Power</th>
            <th class="px-4 py-2">Activity</th>
            <th class="px-4 py-2 hidden sm:table-cell text-right">Date</th>
          </tr>
        </thead>
        <tbody>
          {#each tableRows as row}
            <tr class="border-b border-zinc-800/50 last:border-0 hover:bg-zinc-800/30 transition-colors">
              <td class="px-4 py-2.5 font-mono text-zinc-300 text-xs">{formatDuration(row.d)}</td>
              <td class="px-4 py-2.5 text-right font-medium text-white">{row.w} W</td>
              <td class="px-4 py-2.5">
                {#if row.act}
                  <a href="{base}activity/{row.act.id}/"
                     class="text-blue-400 hover:text-blue-300 transition-colors truncate block max-w-[200px]"
                     title={row.act.title}>
                    {row.act.title}
                  </a>
                {:else}
                  <span class="text-zinc-600 text-xs">—</span>
                {/if}
              </td>
              <td class="px-4 py-2.5 text-zinc-500 text-xs hidden sm:table-cell text-right">
                {row.act ? fmtDate(row.act.started_at) : ''}
              </td>
            </tr>
          {/each}
        </tbody>
      </table>
    </div>
  </div>
{/if}
