import { useMemo, type ReactNode } from 'react';
import type {
  EarningsRecent,
  FundamentalResponse,
  MetricItem,
  MetricIndustry,
} from '../../lib/types';

interface FundamentalsPanelProps {
  data: FundamentalResponse;
}

/** Favorable / neutral / adverse — always paired with the read text. */
type Tone = 'good' | 'flat' | 'bad';

// Horizon tokens.
const HZ = {
  text: '#EDEEF3',
  muted: '#9AA0B0',
  track: '#5D6373',
  fav: '#6EC9DB', // cyan
  adv: '#EF9182', // coral
};
const toneColor = (t: Tone) => (t === 'good' ? HZ.fav : t === 'bad' ? HZ.adv : HZ.muted);

const POSITIVE_READS = new Set([
  'accelerating', 'improving', 'above peers', 'above own average', 'buying back',
  'attractively valued', 'conservative leverage',
]);
const NEGATIVE_READS = new Set([
  'decelerating', 'deteriorating', 'priced above peers', 'elevated leverage', 'below peers',
]);
function readTone(read: string | null | undefined): Tone {
  if (!read) return 'flat';
  if (POSITIVE_READS.has(read)) return 'good';
  if (NEGATIVE_READS.has(read)) return 'bad';
  if (read.includes('dilution')) return 'bad';
  return 'flat';
}

function pct(v: number | null | undefined): string {
  if (v == null || !Number.isFinite(v)) return 'n/a';
  return `${Math.round(v * 10) / 10}%`;
}
function mult(v: number | null | undefined): string {
  if (v == null || !Number.isFinite(v)) return 'n/a';
  return `${v.toFixed(1)}×`;
}
function money(v: number | null | undefined): string {
  if (v == null || !Number.isFinite(v)) return 'n/a';
  const abs = Math.abs(v);
  if (abs >= 1e12) return `$${(v / 1e12).toFixed(1)}T`;
  if (abs >= 1e9) return `$${(v / 1e9).toFixed(1)}B`;
  if (abs >= 1e6) return `$${(v / 1e6).toFixed(1)}M`;
  return `$${v.toFixed(0)}`;
}
function signedPp(v: number): string {
  return `${v >= 0 ? '+' : ''}${Math.round(v * 10) / 10}pp`;
}
function capitalize(s: string): string {
  return s.length ? s[0].toUpperCase() + s.slice(1) : s;
}
function finiteOrNull(v: number | null | undefined): number | null {
  return v != null && Number.isFinite(v) ? v : null;
}
function latestHistory(metric: MetricItem | undefined): number[] {
  const run: number[] = [];
  const history = metric?.history ?? [];
  for (let i = history.length - 1; i >= 0; i -= 1) {
    const value = finiteOrNull(history[i].value);
    if (value == null) break;
    run.unshift(value);
  }
  return run;
}

/** Parse YYYY-MM-DD as a LOCAL calendar date (no UTC off-by-one west of UTC). */
function parseLocalDate(s: string): Date {
  const [y, m, d] = s.split('-').map(Number);
  return new Date(y, (m ?? 1) - 1, d ?? 1);
}
function shortDate(s: string): string {
  return parseLocalDate(s).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
}

export function FundamentalsPanel({ data }: FundamentalsPanelProps) {
  const metrics = useMemo(
    () => Object.fromEntries((data.metrics ?? []).map((m) => [m.key, m])),
    [data.metrics],
  ) as Record<string, MetricItem | undefined>;
  const reads = data.reads?.by_key ?? {};
  const val = data.valuation;
  const earnings = data.earnings;
  const provenance = (data.metrics ?? []).find((m) => m.period_end) ?? null;

  const hasAny = (data.metrics ?? []).some((m) => m.value != null) || !!val || !!earnings?.next
    || (earnings?.recent?.length ?? 0) > 0;

  const trendRows: { key: string; label: string; kind: 'growth' | 'margin' | 'share' }[] = [
    { key: 'revenue_growth_yoy', label: 'Revenue growth', kind: 'growth' },
    { key: 'eps_growth_yoy', label: 'EPS growth', kind: 'growth' },
    { key: 'operating_margin', label: 'Operating margin', kind: 'margin' },
    { key: 'fcf_margin', label: 'FCF margin', kind: 'margin' },
    { key: 'share_count_change_yoy', label: 'Share count YoY', kind: 'share' },
  ];

  const valueRows: {
    label: string; value: number | null; industry: MetricIndustry | null;
    readKey: string; fmt: (v: number | null) => string;
  }[] = [
    { label: 'Net debt / EBITDA', value: metrics.net_debt_to_ebitda?.value ?? null,
      industry: metrics.net_debt_to_ebitda?.industry ?? null, readKey: 'net_debt_to_ebitda', fmt: mult },
    { label: 'P/E', value: val?.pe ?? null, industry: val?.pe_industry ?? null, readKey: 'pe', fmt: mult },
    { label: 'FCF yield', value: val?.fcf_yield ?? null, industry: val?.fcf_yield_industry ?? null,
      readKey: 'fcf_yield', fmt: pct },
  ];

  return (
    <section className="glass p-5" aria-label="Fundamentals">
      <h3 className="text-[10px] font-medium uppercase tracking-widest" style={{ color: HZ.muted }}>Fundamentals</h3>
      {data.reads?.header ? (
        <p className="mt-0.5 text-[15px] leading-snug" style={{ color: HZ.text }}>
          {capitalize(data.reads.header)}
        </p>
      ) : !hasAny ? (
        <p className="mt-1 text-[15px] leading-snug" style={{ color: HZ.muted }}>
          No fundamentals reported yet.
        </p>
      ) : null}

      {hasAny && (
        <>
          <EarningsStrip earnings={earnings} />

          <div className="mt-4 grid gap-x-8 gap-y-5 sm:grid-cols-2">
            <div>
              <SectionHead label="Operating trend" axis="less favorable ← ref → more favorable" />
              <div className="mt-2.5 space-y-3.5">
                {trendRows.map((r) => (
                  <TrendRow key={r.key} label={r.label} kind={r.kind}
                            metric={metrics[r.key]} read={reads[r.key]}
                            caveat={metrics[r.key]?.caveat} />
                ))}
              </div>
            </div>
            <div>
              <SectionHead label="Valuation & balance" axis="less favorable ← median → more favorable" />
              <div className="mt-2.5 space-y-3.5">
                {valueRows.map((r) => (
                  <ValueRow key={r.label} {...r} read={reads[r.readKey]} />
                ))}
              </div>
            </div>
          </div>

          <Provenance provenance={provenance} priceDate={val?.price_date ?? null}
                      marketCap={val?.market_cap_est ?? null} />
        </>
      )}
    </section>
  );
}

function SectionHead({ label, axis }: { label: string; axis: string }) {
  return (
    <div className="flex flex-wrap items-baseline justify-between gap-x-2">
      <span className="text-[10px] font-medium uppercase tracking-widest" style={{ color: HZ.muted }}>{label}</span>
      <span className="text-[11px]" style={{ color: HZ.track }}>{axis}</span>
    </div>
  );
}

function Bullet({ label, value, rail, comparison }: {
  label: string; value: ReactNode; rail: ReactNode | null; comparison: ReactNode;
}) {
  return (
    <div>
      <div className="flex items-baseline justify-between gap-2">
        <span className="truncate text-sm" style={{ color: HZ.muted }}>{label}</span>
        <span className="num text-[15px]" style={{ color: HZ.text }}>{value}</span>
      </div>
      {rail && <div className="mt-1.5">{rail}</div>}
      <div className={rail ? 'mt-1 text-[11.5px] leading-snug' : 'mt-1.5 text-[11.5px] leading-snug'}>
        {comparison}
      </div>
    </div>
  );
}

// ---- operating-trend row (delta vs reference, favorable = right) ------------

function TrendRow({ label, kind, metric, read, caveat }: {
  label: string; kind: 'growth' | 'margin' | 'share';
  metric: MetricItem | undefined; read: string | null | undefined;
  caveat: string | null | undefined;
}) {
  const tone = readTone(read);
  const value = finiteOrNull(metric?.value);
  const history = latestHistory(metric);

  let ref: number | null = null;
  let refWord = '';
  let halfRange = 8;
  let neutral = 2;
  let favSign = 1; // +1: higher is favorable; -1: lower is favorable
  if (kind === 'growth') {
    ref = history.length >= 2 ? history[history.length - 2] : null;
    refWord = 'prior'; halfRange = 8; neutral = 2; favSign = 1;
  } else if (kind === 'margin') {
    const prior = history.slice(0, -1);
    ref = prior.length ? prior.reduce((a, b) => a + b, 0) / prior.length : null;
    refWord = 'avg'; halfRange = 4; neutral = 1; favSign = 1;
  } else {
    ref = 0; refWord = ''; halfRange = 5; neutral = 1; favSign = -1; // buyback (negative) is favorable
  }
  const delta = value != null && ref != null ? value - ref : null;

  const comparison = caveat ? (
    <span style={{ color: HZ.muted }}>{caveat}</span>
  ) : value == null ? (
    <span style={{ color: HZ.track }}>n/a</span>
  ) : delta == null ? (
    <span style={{ color: HZ.track }}>history n/a</span>
  ) : (
    <span style={{ color: toneColor(tone) }}>
      {kind !== 'share' && (
        <span className="num">{signedPp(delta)} vs {refWord} · </span>
      )}
      {read ?? '—'}
    </span>
  );

  const rail = delta != null
    ? <DeltaRail favOffset={favSign * delta} halfRange={halfRange} neutral={neutral} tone={tone}
                 ariaLabel={`${label} ${pct(value)}, ${delta != null ? `${signedPp(delta)} vs reference` : ''}, ${read ?? 'no read'}`} />
    : null;

  return <Bullet label={label} value={pct(value)} rail={rail} comparison={comparison} />;
}

/** Comparison rail centered on a reference line (not a progress bar). favOffset > 0
 * is favorable and moves the dot RIGHT for every metric. */
function DeltaRail({ favOffset, halfRange, neutral, tone, ariaLabel }: {
  favOffset: number; halfRange: number; neutral: number; tone: Tone; ariaLabel: string;
}) {
  const clamped = Math.max(-halfRange, Math.min(halfRange, favOffset));
  const pos = 50 + (clamped / halfRange) * 50;
  const barLeft = Math.min(50, pos);
  const barWidth = Math.abs(pos - 50);
  const bandHalf = (neutral / halfRange) * 50;
  return (
    <span className="relative block h-1.5 min-w-[7rem] flex-1 rounded-full" role="img" aria-label={ariaLabel}
          style={{ background: 'rgba(93,99,115,0.22)' }}>
      <span className="absolute inset-y-0 rounded-full" aria-hidden
            style={{ left: `${50 - bandHalf}%`, width: `${2 * bandHalf}%`, background: 'rgba(93,99,115,0.4)' }} />
      <span className="absolute inset-y-[-2px] w-px" aria-hidden style={{ left: '50%', background: 'rgba(237,238,243,0.55)' }} />
      <span className="absolute inset-y-0 rounded-full" aria-hidden style={{ left: `${barLeft}%`, width: `${barWidth}%`, background: toneColor(tone) }} />
      <Dot pos={pos} tone={tone} />
    </span>
  );
}

// ---- valuation/balance row (favorable percentile vs median) ----------------

function ValueRow({ label, value, industry, read, fmt }: {
  label: string; value: number | null; industry: MetricIndustry | null;
  read: string | null | undefined; fmt: (v: number | null) => string;
}) {
  const tone = readTone(read);
  const safeValue = finiteOrNull(value);
  const rail = safeValue == null || !industry
    ? null
    : <PercentileRail percentile={industry.favorable_percentile} tone={tone}
        ariaLabel={`${label}: ${industry.favorable_percentile}th favorable percentile vs ${industry.label}, median ${fmt(industry.median)}, ${industry.peer_count} peers`} />;
  const comparison = safeValue == null ? (
    <span style={{ color: HZ.track }}>n/a</span>
  ) : industry ? (
    <span style={{ color: toneColor(tone) }}>
      {read ?? 'in line'}<span style={{ color: HZ.muted }}> · median {fmt(industry.median)} · {industry.peer_count} peers</span>
    </span>
  ) : (
    <span style={{ color: HZ.track }}>peers n/a</span>
  );
  return <Bullet label={label} value={fmt(safeValue)} rail={rail} comparison={comparison} />;
}

/** 0-100 favorable-percentile rail with the peer median fixed at 50; right = more favorable. */
function PercentileRail({ percentile, tone, ariaLabel }: {
  percentile: number; tone: Tone; ariaLabel: string;
}) {
  const p = Math.max(0, Math.min(100, percentile));
  const barLeft = Math.min(50, p);
  const barWidth = Math.abs(p - 50);
  return (
    <span className="relative block h-1.5 min-w-[7rem] flex-1 rounded-full" role="img" aria-label={ariaLabel}
          style={{ background: 'rgba(93,99,115,0.22)' }}>
      <span className="absolute inset-y-[-2px] w-px" aria-hidden style={{ left: '50%', background: 'rgba(237,238,243,0.55)' }} />
      <span className="absolute inset-y-0 rounded-full" aria-hidden style={{ left: `${barLeft}%`, width: `${barWidth}%`, background: toneColor(tone) }} />
      <Dot pos={p} tone={tone} />
    </span>
  );
}

function Dot({ pos, tone }: { pos: number; tone: Tone }) {
  return (
    <span className="absolute top-1/2 h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full" aria-hidden
          style={{ left: `${pos}%`, background: toneColor(tone), boxShadow: '0 0 0 2px #11131C' }} />
  );
}

// ---- earnings + provenance -------------------------------------------------

function Provenance({ provenance, priceDate, marketCap }: {
  provenance: MetricItem | null; priceDate: string | null; marketCap: number | null;
}) {
  if (!provenance?.period_end && !priceDate) return null;
  return (
    <p className="mt-4 border-t border-white/10 pt-2 text-[11px] leading-relaxed" style={{ color: HZ.track }}>
      {provenance?.period_end && (
        <>SEC filings · latest {shortDate(provenance.period_end)}
          {provenance.filed_date && <> (filed {shortDate(provenance.filed_date)})</>}</>
      )}
      {priceDate && (
        <>{provenance?.period_end ? ' · ' : ''}Valuation at {shortDate(priceDate)} close · market cap {money(marketCap)} est.</>
      )}
    </p>
  );
}

function EarningsStrip({ earnings }: { earnings: FundamentalResponse['earnings'] }) {
  const next = earnings?.next;
  const recent = earnings?.recent ?? [];
  const when = next ? (next.days_until === 0 ? 'today' : `${next.days_until}d`) : null;
  return (
    <div className="mt-2 flex flex-wrap items-center justify-between gap-x-4 gap-y-1.5">
      <span className="text-sm" style={{ color: HZ.text }}>
        <span style={{ color: HZ.muted }}>Next earnings </span>
        {next ? (
          <>
            {shortDate(next.date)}
            {' · '}
            <span className="uppercase">{next.session === 'unknown' ? 'TBD' : next.session}</span>
            <span style={{ color: HZ.muted }}> · {when}</span>
          </>
        ) : (
          <span style={{ color: HZ.muted }}>no date</span>
        )}
      </span>
      {recent.length > 0 && <SurpriseSpark recent={recent} />}
    </div>
  );
}

/** Four tiny diverging bars around a zero baseline: beat above (cyan), miss below
 * (coral), height ~ |surprise %|. Reads as a beat/miss history at a glance. */
function SurpriseSpark({ recent }: { recent: EarningsRecent[] }) {
  const ordered = recent.slice().reverse();
  const description = ordered.map((e) => {
    const surprise = e.surprise_pct;
    const amount = surprise != null ? ` ${surprise > 0 ? '+' : ''}${surprise}%` : '';
    return `${e.announce_date} ${surpriseLabel(e)}${amount}`;
  }).join(', ');
  return (
    <span className="flex items-center gap-2">
      <span className="text-[10px] uppercase tracking-widest" style={{ color: HZ.track }}>
        EPS surprises
      </span>
      <span
        className="relative flex h-7 items-center gap-1.5 px-0.5"
        role="img"
        tabIndex={0}
        title={description}
        aria-label={`Recent EPS surprises, oldest to newest: ${description}`}
      >
        <span className="absolute inset-x-0 top-1/2 h-px" aria-hidden style={{ background: 'rgba(93,99,115,0.5)' }} />
        {ordered.map((e, i) => <SurpriseBar key={i} e={e} />)}
      </span>
    </span>
  );
}

function surpriseLabel(e: EarningsRecent): string {
  const beat = e.eps_actual != null && e.eps_estimate != null ? e.eps_actual - e.eps_estimate : null;
  return beat == null ? 'n/a' : beat > 0 ? 'beat' : beat < 0 ? 'miss' : 'in line';
}

function SurpriseBar({ e }: { e: EarningsRecent }) {
  const beat = e.eps_actual != null && e.eps_estimate != null ? e.eps_actual - e.eps_estimate : null;
  const tone: Tone = beat == null ? 'flat' : beat > 0 ? 'good' : beat < 0 ? 'bad' : 'flat';
  const s = e.surprise_pct;
  const mag = s != null ? Math.min(Math.abs(s), 15) / 15 : 0; // cap at 15%
  const h = beat == null ? 2 : 3 + mag * 9; // px
  const up = (beat ?? 0) >= 0;
  return (
    <span className="relative z-[1] block h-7 w-2"
          title={`${e.announce_date}: ${surpriseLabel(e)}${s != null ? ` ${s > 0 ? '+' : ''}${s}%` : ''}`}>
      <span className="absolute inset-x-0 rounded-[1px]" aria-hidden
            style={{ height: h, background: toneColor(tone), ...(up ? { bottom: '50%' } : { top: '50%' }) }} />
    </span>
  );
}
