<script lang="ts">
  import { tick } from 'svelte';

  let {
    open = false,
    activeMetrics = [] as string[],
    focusKey = null as string | null,
    onClose,
  }: {
    open?: boolean;
    activeMetrics?: string[];
    focusKey?: string | null;
    onClose: () => void;
  } = $props();

  let searchQuery = $state('');
  let expandedItem = $state<string | null>(null);
  let bodyEl = $state<HTMLElement | null>(null);

  // When focusKey changes, expand and scroll to that item
  $effect(() => {
    if (focusKey && open) {
      expandedItem = focusKey;
      searchQuery = '';
      tick().then(() => {
        if (!bodyEl) return;
        const el = bodyEl.querySelector(`[data-gkey="${focusKey}"]`);
        if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
      });
    }
  });

  // ── Glossary data ─────────────────────────────────────────────────────
  type RangeBand = { val: string; label: string };
  type GlossaryItem = {
    key: string;
    label: string;
    category: 'Market Context' | 'Valuation' | 'Quality' | 'Risk' | 'Signals' | 'ETF' | 'Bond';
    definition: string;
    gate?: string;
    goodRange?: RangeBand;
    neutralRange?: RangeBand;
    badRange?: RangeBand;
    assetTypes?: ('STOCK' | 'ETF' | 'BOND')[];
  };

  const GLOSSARY: GlossaryItem[] = [
    // ── Market Context ─────────────────────────────────────────────────
    {
      key: '10Y',
      label: '10Y Treasury Yield',
      category: 'Market Context',
      definition: 'The yield on 10-year US government bonds — the global risk-free rate benchmark. Drives discount rates for all assets: higher yield = lower present value of future earnings.',
      gate: 'Rate regime: < 2% LOW | 2–5% NORMAL | > 5% HIGH. HIGH rates compress growth stock P/E multipliers.',
      goodRange:    { val: '2–4%',  label: 'Normal, accommodative' },
      neutralRange: { val: '4–5%',  label: 'Elevated, watch growth' },
      badRange:     { val: '> 5%',  label: 'HIGH regime, P/E compression' },
    },
    {
      key: 'VIX',
      label: 'VIX — Volatility Index',
      category: 'Market Context',
      definition: 'The CBOE Volatility Index — measures expected 30-day S&P 500 volatility derived from options prices. Known as the "fear gauge."',
      gate: 'Volatility regime: < 15 CALM | 15–25 NORMAL | > 25 ELEVATED | > 35 EXTREME',
      goodRange:    { val: '< 15',   label: 'Calm market, low fear' },
      neutralRange: { val: '15–25',  label: 'Normal uncertainty' },
      badRange:     { val: '> 25',   label: 'Elevated fear' },
    },
    {
      key: 'Rate Regime',
      label: 'Rate Regime',
      category: 'Market Context',
      definition: 'Derived from the 10Y Treasury yield. Controls how aggressively the INFLATED scoring mode adjusts P/E gates — HIGH rates tighten the multiplier from 1.5× to 1.2× of S&P P/E.',
      gate: 'LOW < 2% | NORMAL 2–5% | HIGH > 5%',
      goodRange: { val: 'LOW',    label: 'Growth-friendly' },
      neutralRange: { val: 'NORMAL', label: 'Balanced' },
      badRange:  { val: 'HIGH',   label: 'Value favoured, growth penalised' },
    },
    // ── Valuation ──────────────────────────────────────────────────────
    {
      key: 'P/E',
      label: 'P/E Ratio',
      category: 'Valuation',
      definition: 'Price-to-Earnings: how many dollars investors pay per $1 of annual profit. Lower = cheaper relative to earnings.',
      gate: 'Graham gate: ≤ 15× | Inflated gate: ≤ S&P P/E × 1.5 (live)',
      goodRange:    { val: '< 15×',   label: 'Value / below sector avg' },
      neutralRange: { val: '15–35×',  label: 'Elevated but common' },
      badRange:     { val: '> 35×',   label: 'Expensive without high growth' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'PEG',
      label: 'PEG Ratio',
      category: 'Valuation',
      definition: 'P/E divided by earnings growth rate. Adjusts for growth — a 30× P/E stock growing 30% has PEG 1.0, same as a 15× stock growing 15%.',
      gate: 'Gate: < 1.0 (Lynch standard) · Weight: 2',
      goodRange:    { val: '< 1.0',   label: 'Bargain' },
      neutralRange: { val: '1.0–2.0', label: 'Fair' },
      badRange:     { val: '> 2.0',   label: 'Costly' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'DCF Safety',
      label: 'DCF Margin of Safety',
      category: 'Valuation',
      definition: 'How much below the discounted cash flow intrinsic value the stock trades. Positive = undervalued vs. DCF model; negative = overvalued. Requires positive FCF to compute.',
      gate: '≥ +20% → full score | 0–20% → +1 | -20–0% → -1 | < -20% → negative score',
      goodRange:    { val: '> +20%',   label: 'Significant discount' },
      neutralRange: { val: '0–20%',    label: 'Modest discount' },
      badRange:     { val: '< -20%',   label: 'Premium to fair value' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'Upside',
      label: 'Analyst Price Target Upside',
      category: 'Valuation',
      definition: 'Percentage gap between current price and Wall Street consensus target price. Positive = analysts expect the stock to rise.',
      gate: 'Risk flag if ≥ +25% upside or ≤ -15% downside',
      goodRange:    { val: '+5–20%',  label: 'Moderate consensus upside' },
      neutralRange: { val: '0–5%',   label: 'Fairly priced' },
      badRange:     { val: '< -10%', label: 'Analysts bearish' },
      assetTypes: ['STOCK'],
    },
    // ── Quality ────────────────────────────────────────────────────────
    {
      key: 'ROE%',
      label: 'Return on Equity',
      category: 'Quality',
      definition: 'Net income as a % of shareholders\' equity. Measures how efficiently management generates profit from invested capital.',
      gate: 'Gate: ROE ≥ 15%',
      goodRange:    { val: '> 20%',  label: 'Excellent capital efficiency' },
      neutralRange: { val: '10–20%', label: 'Adequate' },
      badRange:     { val: '< 10%',  label: 'Poor capital use' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'OpMgn%',
      label: 'Operating Margin',
      category: 'Quality',
      definition: 'Operating profit as a % of revenue — what\'s left after COGS and operating expenses, before interest and taxes.',
      gate: 'Gate: Op Margin ≥ 10%',
      goodRange:    { val: '> 20%', label: 'High quality business' },
      neutralRange: { val: '5–20%', label: 'Modest margins' },
      badRange:     { val: '< 5%',  label: 'Thin, fragile' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'GrossM%',
      label: 'Gross Margin',
      category: 'Quality',
      definition: 'Revenue minus cost of goods sold, as a %. Shows pricing power and production efficiency before overhead.',
      gate: 'Informational — not a hard gate, used contextually',
      goodRange:    { val: '> 50%',  label: 'Software / services quality' },
      neutralRange: { val: '15–50%', label: 'Moderate' },
      badRange:     { val: '< 15%',  label: 'Commodity-like, price-taker' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'FCF Yld%',
      label: 'Free Cash Flow Yield',
      category: 'Quality',
      definition: 'Free cash flow per share divided by price — cash the business actually generates, expressed as a yield. Unlike earnings, FCF is hard to fake.',
      gate: 'Gate: FCF > 0 (negative FCF = gate fail) | Weight: 3× in scoring',
      goodRange:    { val: '> 5%', label: 'Strong cash generation' },
      neutralRange: { val: '0–5%', label: 'Weak positive' },
      badRange:     { val: '< 0%', label: 'Cash-burning' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'Analyst',
      label: 'Analyst Consensus Rating',
      category: 'Quality',
      definition: 'Wall Street average recommendation on a 1–5 scale (Yahoo). 1 = Strong Buy, 5 = Strong Sell. Requires ≥ 3 analysts for signal to fire.',
      gate: '≤ 2.0 → full score | ≤ 3.0 → +1 | ≤ 4.0 → -1 | > 4.0 → negative score',
      goodRange:    { val: '1.0–2.5', label: 'Buy consensus' },
      neutralRange: { val: '2.5–4.0', label: 'Neutral / Hold' },
      badRange:     { val: '> 4.0',   label: 'Sell consensus' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'Revenue',
      label: 'Revenue Growth',
      category: 'Quality',
      definition: 'Year-over-year percentage change in total revenue. Measures whether the business is expanding its top line. A secondary scoring factor — positive growth adds to score, declining revenue subtracts.',
      gate: 'Gate: Revenue growth > 0% for positive contribution | Weight: 2× in scoring',
      goodRange:    { val: '> 10%', label: 'Strong expansion' },
      neutralRange: { val: '0–10%', label: 'Slow growth' },
      badRange:     { val: '< 0%',  label: 'Shrinking top line' },
      assetTypes: ['STOCK'],
    },
    // ── Risk ───────────────────────────────────────────────────────────
    {
      key: 'D/E',
      label: 'Debt-to-Equity Ratio',
      category: 'Risk',
      definition: 'Total debt divided by shareholders\' equity. Measures financial leverage — how much borrowed money vs. owned capital the company uses.',
      gate: 'Gate: D/E ≤ 1.5× | Tech: ≤ 2.0× | Financials: gate disabled (scored on P/B instead)',
      goodRange:    { val: '< 0.5×',  label: 'Conservative' },
      neutralRange: { val: '0.5–1.5×', label: 'Moderate' },
      badRange:     { val: '> 2.0×',  label: 'High leverage risk' },
      assetTypes: ['STOCK'],
    },
    {
      key: '52W Chg',
      label: '52-Week Price Change',
      category: 'Risk',
      definition: 'Total % price return over the past year. Captures trend strength and momentum.',
      gate: 'Risk flag: ≥ +50% (at peak, reversal risk) | ≤ -30% (significant drawdown)',
      goodRange:    { val: '+5–30%',  label: 'Steady uptrend' },
      neutralRange: { val: '-5–+5%',  label: 'Flat / sideways' },
      badRange:     { val: '< -30%',  label: 'Significant drawdown' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'From High',
      label: 'Distance from 52-Week High',
      category: 'Risk',
      definition: 'How far (%) the current price sits below the 52-week peak. Negative = below peak. A -15% reading means the stock is 15% off its high.',
      gate: 'Risk flag if > -20% from high (at or near peak)',
      goodRange:    { val: '-5–25%',  label: 'Healthy pullback' },
      neutralRange: { val: '-25–40%', label: 'Larger drawdown' },
      badRange:     { val: '0–3%',    label: 'At peak, limited buffer' },
      assetTypes: ['STOCK'],
    },
    // ── Signals ────────────────────────────────────────────────────────
    {
      key: 'Graham',
      label: 'Graham (Fundamental) Score',
      category: 'Signals',
      definition: 'Strict value-investing score using fixed Graham gates: P/E ≤ 15×, PEG ≤ 1.0, D/E ≤ 1.5×, ROE ≥ 15%, FCF > 0. Does not adjust for market conditions — these thresholds never move.',
      gate: 'All gates fixed regardless of S&P P/E or rate regime',
      goodRange:    { val: 'PASS',      label: 'Passes all Graham gates' },
      neutralRange: { val: 'PARTIAL',   label: 'Passes some, fails others' },
      badRange:     { val: 'FAIL',      label: 'Fails one or more hard gates' },
    },
    {
      key: 'Mkt-Adj',
      label: 'Market-Adjusted Score',
      category: 'Signals',
      definition: 'Relaxed scoring mode that calibrates gates to live market benchmarks. P/E gate = S&P P/E × 1.5 (or × 1.2 in HIGH rate regime). Reflects what is "acceptable" in today\'s market, not absolute value.',
      gate: 'P/E gate: S&P P/E × 1.5 (NORMAL) or × 1.2 (HIGH) | Tech P/E: XLK P/E × 1.3',
      goodRange:    { val: 'PASS',      label: 'Passes mkt-adjusted gates' },
      neutralRange: { val: 'PARTIAL',   label: 'Borderline vs live benchmarks' },
      badRange:     { val: 'FAIL',      label: 'Fails even relaxed gates' },
    },
    {
      key: 'signal',
      label: 'Signal',
      category: 'Signals',
      definition: 'Overall recommendation derived by comparing Market-Adjusted and Graham (fundamental) scores.',
      gate: 'Strong Buy = passes both | Momentum = passes Mkt-Adj only | Speculation = passes Mkt-Adj, fails Graham | Neutral = borderline | Avoid = fails both',
      goodRange:    { val: '✅ ⚡',  label: 'Strong Buy / Momentum' },
      neutralRange: { val: '🔄',    label: 'Neutral — hold' },
      badRange:     { val: '⚠️ ❌', label: 'Speculation / Avoid' },
    },
    {
      key: 'score',
      label: 'Score (dot scale)',
      category: 'Signals',
      definition: 'Weighted sum of factor scores (ROE, FCF, margin, PEG, revenue growth, analyst, DCF). Displayed as ●●●●○ dots out of 5 + raw number.',
      gate: 'Positive factors add to score; negative riskFlags subtract. Gate failures bypass scoring entirely (shown as ✗).',
      goodRange:    { val: '> 12',  label: 'High conviction' },
      neutralRange: { val: '6–12',  label: 'Borderline' },
      badRange:     { val: '< 6',   label: 'Weak factors' },
    },
    {
      key: 'Cap Tier',
      label: 'Market Cap Tier',
      category: 'Signals',
      definition: 'Size classification based on market capitalisation. Mega Cap (> $200B), Large ($10–200B), Mid ($2–10B), Small ($300M–$2B), Micro (< $300M).',
      gate: 'Informational — not a gate. Useful for position sizing and risk calibration.',
      goodRange:    { val: 'Mega / Large', label: 'Most liquid' },
      neutralRange: { val: 'Mid',          label: 'Balanced' },
      badRange:     { val: 'Micro',        label: 'High vol, thin liquidity' },
      assetTypes: ['STOCK'],
    },
    {
      key: 'Style',
      label: 'Growth / Style Category',
      category: 'Signals',
      definition: 'Derived from revenue growth and earnings growth. High Growth (rev ≥ 15% or earnings ≥ 20%), Growth (5–15%), Value (< 5% + yield ≥ 3%), Stable, Turnaround, Declining.',
      gate: 'Informational — not a gate. Helps match the stock to your strategy.',
      goodRange:    { val: 'High Growth / Growth', label: 'Matches momentum strategy' },
      neutralRange: { val: 'Stable / Value',       label: 'Income / defensive' },
      badRange:     { val: 'Declining',            label: 'Revenue shrinking > -5%' },
      assetTypes: ['STOCK'],
    },
    // ── ETF ────────────────────────────────────────────────────────────
    {
      key: 'Exp Ratio%',
      label: 'Expense Ratio',
      category: 'ETF',
      definition: 'Annual management fee as a % of AUM. Deducted from returns automatically. Lower is always better — costs compound against you.',
      gate: 'Hard gate: Expense Ratio ≤ 0.20%',
      goodRange:    { val: '< 0.10%',     label: 'Index-like, minimal drag' },
      neutralRange: { val: '0.10–0.50%',  label: 'Acceptable' },
      badRange:     { val: '> 0.50%',     label: 'High cost drag' },
      assetTypes: ['ETF'],
    },
    {
      key: '5Y Return%',
      label: '5-Year Annualised Return',
      category: 'ETF',
      definition: 'Compound annual growth rate over 5 years. The S&P 500 long-run average is ~10%; use that as a baseline.',
      gate: 'Gate: 5Y Return ≥ 8% (S&P long-run floor)',
      goodRange:    { val: '> 12%', label: 'Outperforming market' },
      neutralRange: { val: '8–12%', label: 'Market-rate returns' },
      badRange:     { val: '< 6%',  label: 'Underperforming bonds + inflation' },
      assetTypes: ['ETF'],
    },
    {
      key: 'Yield%',
      label: 'Distribution Yield',
      category: 'ETF',
      definition: 'Annual income distributions (dividends, interest) as a % of NAV. Important for income-focused or REIT ETFs.',
      gate: 'REIT ETF: Yield floor based on XLRE yield × regime factor',
      goodRange:    { val: '> 3%', label: 'Strong income' },
      neutralRange: { val: '1–3%', label: 'Low but positive' },
      badRange:     { val: '< 1%', label: 'Insufficient for income' },
      assetTypes: ['ETF'],
    },
    // ── Bond ───────────────────────────────────────────────────────────
    {
      key: 'YTM%',
      label: 'Yield to Maturity',
      category: 'Bond',
      definition: 'Total return if you hold the bond to maturity — includes coupon payments plus any price gain/loss vs. par. The true all-in yield.',
      gate: 'Spread gate: YTM must exceed risk-free rate by ≥ 1.5% (NORMAL) or ≥ 1.8% (HIGH rates)',
      goodRange:    { val: 'Sprd > 2%', label: 'Good compensation for risk' },
      neutralRange: { val: '1–2%',      label: 'Adequate spread' },
      badRange:     { val: '< 1%',      label: 'Not compensating for credit risk' },
      assetTypes: ['BOND'],
    },
    {
      key: 'Duration',
      label: 'Duration (years)',
      category: 'Bond',
      definition: 'Sensitivity to interest rate changes. A duration of 5 means a 1% rate rise → ~5% price drop. Shorter = less rate risk.',
      gate: 'Gate: Duration ≤ 7 years',
      goodRange:    { val: '< 4 yrs',  label: 'Low rate sensitivity' },
      neutralRange: { val: '4–7 yrs',  label: 'Moderate' },
      badRange:     { val: '> 10 yrs', label: 'High rate risk' },
      assetTypes: ['BOND'],
    },
    {
      key: 'Rating',
      label: 'Credit Rating',
      category: 'Bond',
      definition: 'Agency rating of default probability: AAA (safest) → AA → A → BBB (investment grade floor) → BB → B → CCC (junk).',
      gate: 'Hard gate: Rating ≥ BBB (investment-grade, numeric ≥ 7)',
      goodRange:    { val: 'AAA–A',  label: 'Very low default risk' },
      neutralRange: { val: 'BBB',    label: 'Investment-grade floor' },
      badRange:     { val: '≤ BB',   label: 'High yield / junk' },
      assetTypes: ['BOND'],
    },
  ];

  const CATEGORIES = ['Market Context', 'Valuation', 'Quality', 'Risk', 'Signals', 'ETF', 'Bond'] as const;

  function filteredItems(): GlossaryItem[] {
    const q = searchQuery.trim().toLowerCase();
    if (!q) return GLOSSARY;
    return GLOSSARY.filter(
      (item) =>
        item.label.toLowerCase().includes(q) ||
        item.definition.toLowerCase().includes(q) ||
        item.category.toLowerCase().includes(q),
    );
  }

  function itemsForCategory(cat: string): GlossaryItem[] {
    return filteredItems().filter((i) => i.category === cat);
  }

  function isActive(item: GlossaryItem): boolean {
    return activeMetrics.some(
      (k) => k === item.key || k === item.label,
    );
  }

  function toggleItem(key: string) {
    expandedItem = expandedItem === key ? null : key;
  }

  // Close on Escape
  function handleKeydown(e: KeyboardEvent) {
    if (e.key === 'Escape') onClose();
  }
</script>

<svelte:window onkeydown={handleKeydown} />

{#if open}
  <!-- Click-outside backdrop — thin, no visual overlay, just captures clicks -->
  <!-- svelte-ignore a11y_click_events_have_key_events -->
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="glossary-backdrop" onclick={onClose}></div>

  <aside class="glossary-panel" aria-label="Metrics Glossary">
    <!-- Header -->
    <div class="glossary-header">
      <span class="glossary-title"><span class="glossary-title-q">?</span> Metric Glossary</span>
      <button class="glossary-close" onclick={onClose} aria-label="Close glossary">×</button>
    </div>

    <!-- Search -->
    <div class="glossary-search-wrap">
      <input
        class="glossary-search"
        type="text"
        placeholder="Search metrics…"
        bind:value={searchQuery}
        aria-label="Search glossary"
      />
      {#if searchQuery}
        <button class="glossary-search-clear" onclick={() => (searchQuery = '')} aria-label="Clear search">✕</button>
      {/if}
    </div>

    <!-- Context banner — fixed between search and body, only when row is selected -->
    {#if activeMetrics.length > 0}
      <div class="glossary-ctx-banner">
        ✦ Highlighted metrics are relevant to the selected row
      </div>
    {/if}

    <!-- Body -->
    <div class="glossary-body" bind:this={bodyEl}>

      {#each CATEGORIES as cat}
        {@const items = itemsForCategory(cat)}
        {#if items.length > 0}
          <div class="glossary-category">
            <div class="glossary-cat-header">{cat}</div>
            {#each items as item}
              {@const active = isActive(item)}
              {@const isExpanded = expandedItem === item.key}
              <div
                class="glossary-item"
                class:glossary-item-active={active}
                class:glossary-item-open={isExpanded}
                data-gkey={item.key}
              >
                <button
                  class="glossary-item-trigger"
                  onclick={() => toggleItem(item.key)}
                  aria-expanded={isExpanded}
                >
                  <span class="glossary-item-label">
                    {#if active}<span class="glossary-active-dot"></span>{/if}
                    {item.label}
                  </span>
                  <span class="glossary-cat-tag gcat-{cat.toLowerCase().replace(/\s/g,'-')}">{cat}</span>
                </button>

                {#if isExpanded}
                  <div class="glossary-item-body">
                    <p class="glossary-definition">{item.definition}</p>

                    {#if item.gate}
                      <div class="glossary-gate-box">
                        <code>{item.gate}</code>
                      </div>
                    {/if}

                    {#if item.goodRange || item.neutralRange || item.badRange}
                      <div class="glossary-range-pills">
                        {#if item.goodRange}
                          <span class="glossary-range-pill grange-good">{item.goodRange.val}</span>
                        {/if}
                        {#if item.neutralRange}
                          <span class="glossary-range-pill grange-neutral">{item.neutralRange.val}</span>
                        {/if}
                        {#if item.badRange}
                          <span class="glossary-range-pill grange-bad">{item.badRange.val}</span>
                        {/if}
                      </div>
                      <div class="glossary-range-labels">
                        {#if item.goodRange}
                          <span class="grlabel-good">{item.goodRange.label}</span>
                        {/if}
                        {#if item.neutralRange}
                          <span class="grlabel-neutral">{item.neutralRange.label}</span>
                        {/if}
                        {#if item.badRange}
                          <span class="grlabel-bad">{item.badRange.label}</span>
                        {/if}
                      </div>
                    {/if}
                  </div>
                {/if}
              </div>
            {/each}
          </div>
        {/if}
      {/each}

      {#if filteredItems().length === 0}
        <div class="glossary-empty">No metrics match "{searchQuery}"</div>
      {/if}
    </div>
  </aside>
{/if}
