'use client';

import { useCallback, useEffect, useState } from 'react';
import { GradePill } from '@/components/GradeCard';
import { useAuth } from '@/contexts/AuthContext';

/**
 * The Ledger (Session 58, Phase 1) — the truth surface, now backed by the
 * persistent `ledger_entries` table.
 *
 *   MY READS — the user's own scans, written the moment a scan completes.
 *   MODEL    — the PUBLIC pipeline record (every pre-grade, misses included).
 *
 * DATA SEMANTICS: lines/odds/books shown here are real captured book values
 * (mono, rendered as fact). model_value is VYNDR output and always carries
 * the MODEL label. The aggregate header NEVER renders a percentage under 20
 * settles — it shows the building record honestly instead.
 */

type Tab = 'mine' | 'model';
type SportFilter = 'ALL' | 'NBA' | 'MLB' | 'WNBA';
type TierFilter = 'ALL' | 'A' | 'B' | 'C' | 'D';

interface LedgerRow {
  id: string;
  player_name: string;
  sport: string;
  stat: string;
  line: number;
  side: 'over' | 'under';
  locked_odds?: string | null;
  book?: string | null;
  grade: string;
  edge?: number | null;
  confidence?: number | null;
  model_value?: number | null;
  graded_at: string;
  game_date: string;
  closing_line?: number | null;
  clv?: number | null;
  clv_result?: 'beat' | 'faded' | 'flat' | null;
  outcome?: 'hit' | 'miss' | 'push' | null;
  actual_value?: number | null;
  revised_from_grade?: string | null;
}

interface TierRecord { settled: number; hits: number; misses: number; hit_pct: number | null }
interface ModelAggregate {
  settled: number;
  hits: number;
  misses: number;
  pushes: number;
  hit_pct: number | null;
  clv_sample: number;
  clv_beat: number;
  beat_close_pct: number | null;
  pending: number;
  min_sample?: number;
  // Session 60 (5.5) — calibration by grade tier (n≥20 rule per tier).
  by_tier?: Record<string, TierRecord>;
}

const SPORT_COLOR: Record<string, string> = {
  nba: '#E94B3C',
  mlb: '#1E90FF',
  wnba: '#FFB347',
  soccer: '#7BC96F',
};

export default function LedgerPage() {
  const { session } = useAuth();
  const [tab, setTab] = useState<Tab>('mine');
  const [sport, setSport] = useState<SportFilter>('ALL');
  const [tier, setTier] = useState<TierFilter>('ALL');
  const [rows, setRows] = useState<LedgerRow[] | null>(null);
  const [aggregate, setAggregate] = useState<ModelAggregate | null>(null);
  const [minSample, setMinSample] = useState(20);

  const load = useCallback(async () => {
    setRows(null);
    const params = new URLSearchParams();
    if (sport !== 'ALL') params.set('sport', sport.toLowerCase());
    if (tier !== 'ALL') params.set('tier', tier);
    try {
      if (tab === 'mine') {
        const token = session?.access_token;
        if (!token) { setRows([]); return; }
        const data = await fetch(`/api/ledger/mine?${params}`, {
          headers: { Authorization: `Bearer ${token}` },
        }).then((r) => r.json());
        setRows(Array.isArray(data?.entries) ? data.entries : []);
      } else {
        const data = await fetch(`/api/ledger/model?${params}`).then((r) => r.json());
        setRows(Array.isArray(data?.entries) ? data.entries : []);
        setAggregate(data?.aggregate ?? null);
        if (Number(data?.min_sample) > 0) setMinSample(Number(data.min_sample));
      }
    } catch {
      setRows([]);
    }
  }, [tab, sport, tier, session]);

  useEffect(() => { void load(); }, [load]);

  return (
    <section style={{ maxWidth: 1100, margin: '0 auto', padding: '32px 16px 120px' }}>
      <header style={{ marginBottom: 24 }}>
        <h1 style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.03em', marginBottom: 6 }}>
          The Ledger.
        </h1>
        <p style={{ color: 'var(--text-secondary)', fontSize: 16 }}>
          Every grade. Every result. No hiding. No deleting.
        </p>
      </header>

      {/* Tabs */}
      <div role="tablist" aria-label="Ledger view" style={{ display: 'flex', gap: 4, marginBottom: 20, borderBottom: '1px solid var(--border)' }}>
        {([['mine', 'MY READS'], ['model', 'MODEL']] as [Tab, string][]).map(([id, label]) => {
          const active = tab === id;
          return (
            <button
              key={id}
              role="tab"
              aria-selected={active}
              onClick={() => setTab(id)}
              className="mono"
              style={{
                padding: '12px 20px', background: 'transparent', border: 'none',
                borderBottom: `2px solid ${active ? 'var(--g-a, #00D4A0)' : 'transparent'}`,
                color: active ? 'var(--text-primary)' : 'var(--text-secondary)',
                fontWeight: 700, fontSize: 12, letterSpacing: '0.08em', cursor: 'pointer', marginBottom: -1,
              }}
            >
              {label}
            </button>
          );
        })}
      </div>

      {/* MODEL aggregate header — never a percentage under min_sample. */}
      {tab === 'model' && aggregate && (
        <ModelHeader agg={aggregate} minSample={minSample} />
      )}

      {/* Filters */}
      <div style={{ display: 'flex', gap: 16, marginBottom: 16, flexWrap: 'wrap' }}>
        <FilterRow label="Sport" value={sport} onChange={(v) => setSport(v as SportFilter)} options={['ALL', 'NBA', 'MLB', 'WNBA']} />
        <FilterRow label="Grade" value={tier} onChange={(v) => setTier(v as TierFilter)} options={['ALL', 'A', 'B', 'C', 'D']} />
      </div>

      {rows === null ? (
        <p className="mono" style={{ color: 'var(--text-tertiary)', padding: 32, textAlign: 'center' }}>Loading…</p>
      ) : rows.length === 0 ? (
        <EmptyLedger tab={tab} />
      ) : (
        <div style={{ display: 'grid', gap: 12, gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))' }}>
          {rows.map((row, i) => (
            <LedgerCard key={row.id} row={row} index={i} />
          ))}
        </div>
      )}
    </section>
  );
}

const TIER_ORDER = ['A+', 'A', 'B', 'C', 'D', 'F'];

/** Session 60 (5.5) — per-tier calibration: the separation between tiers is
 *  the proof the grades mean something. A tier under n≥20 shows "building",
 *  never a small-sample percentage. Self-hides until ANY tier is ready. */
function TierCalibration({ agg, minSample }: { agg: ModelAggregate; minSample: number }) {
  const tiers = agg.by_tier || {};
  const anyReady = TIER_ORDER.some((t) => tiers[t] && tiers[t].hit_pct != null);
  if (!anyReady) return null;
  return (
    <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', marginTop: 14, paddingTop: 12, borderTop: '1px solid var(--border)' }}>
      {TIER_ORDER.filter((t) => tiers[t] && tiers[t].settled > 0).map((t) => {
        const b = tiers[t];
        const ready = b.hit_pct != null;
        return (
          <span key={t} className="mono" style={{ fontSize: 11.5, fontWeight: 700, padding: '4px 10px', borderRadius: 6, border: '1px solid var(--border-hi)', color: ready ? 'var(--text-primary)' : 'var(--text-tertiary)' }}>
            {t}-TIER · {ready ? `${b.hits}-${b.misses} · ${b.hit_pct}%` : `building (${b.settled}/${minSample})`}
          </span>
        );
      })}
    </div>
  );
}

function ModelHeader({ agg, minSample }: { agg: ModelAggregate; minSample: number }) {
  const ready = agg.settled >= minSample && agg.hit_pct != null;
  return (
    <div
      className="surface diagonal-cut"
      style={{ padding: 20, marginBottom: 20, border: `1px solid ${ready ? 'var(--g-a, #00D4A0)' : 'var(--border)'}` }}
    >
      {ready ? (
        <div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'baseline' }}>
          <span className="mono" style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'var(--text-tertiary)' }}>
            MODEL · LAST 30D
          </span>
          <span className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--g-a, #00D4A0)' }}>
            {agg.hits}-{agg.misses} · {agg.hit_pct}% HIT
          </span>
          {agg.beat_close_pct != null && (
            <span className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--text-primary)' }}>
              {agg.beat_close_pct}% BEAT CLOSE
            </span>
          )}
          <span className="mono" style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>
            {agg.pending} pending
          </span>
        </div>
      ) : (
        <div>
          <p className="mono" style={{ fontSize: 13, fontWeight: 800, letterSpacing: '0.1em', color: 'var(--amber, #FFB347)', marginBottom: 6 }}>
            RECORD BUILDING
          </p>
          <p style={{ fontSize: 14, color: 'var(--text-secondary)' }}>
            Every read settles here, misses included.{' '}
            <span className="mono" style={{ color: 'var(--text-primary)' }}>
              {agg.pending} read{agg.pending === 1 ? '' : 's'} pending settlement
            </span>
            {agg.settled > 0 && (
              <span className="mono" style={{ color: 'var(--text-tertiary)' }}> · {agg.settled} settled</span>
            )}
          </p>
        </div>
      )}
      <TierCalibration agg={agg} minSample={minSample} />
    </div>
  );
}

function OutcomeChip({ row }: { row: LedgerRow }) {
  if (!row.outcome) {
    return <span className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>PENDING</span>;
  }
  const color = row.outcome === 'hit' ? 'var(--g-a, #00D4A0)'
    : row.outcome === 'miss' ? 'var(--miss, #FF6B6B)' : 'var(--text-secondary)';
  const mark = row.outcome === 'hit' ? '✓ HIT' : row.outcome === 'miss' ? '✕ MISS' : '– PUSH';
  return (
    <span className="mono" style={{ fontSize: 12, fontWeight: 700, color }}>
      {mark}{row.actual_value != null ? ` (${row.actual_value})` : ''}
    </span>
  );
}

function ClvChip({ row }: { row: LedgerRow }) {
  if (!row.clv_result || row.clv == null) return null;
  const color = row.clv_result === 'beat' ? 'var(--g-a, #00D4A0)'
    : row.clv_result === 'faded' ? 'var(--miss, #FF6B6B)' : 'var(--text-tertiary)';
  return (
    <span className="mono" title={`Closing line value: locked ${row.line}, closed ${row.closing_line}`}
      style={{ fontSize: 10.5, fontWeight: 700, color, letterSpacing: '0.04em' }}>
      CLV {row.clv > 0 ? '+' : ''}{row.clv} · {row.clv_result.toUpperCase()}
    </span>
  );
}

function LedgerCard({ row, index }: { row: LedgerRow; index: number }) {
  const sportColor = SPORT_COLOR[row.sport] || 'var(--text-secondary)';
  return (
    <article className={`surface diagonal-cut animate-fade-up stagger-${(index % 6) + 1}`} style={{ padding: 16 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
        <span className="mono" style={{ fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 999, background: `${sportColor}1F`, color: sportColor }}>
          {row.sport.toUpperCase()}
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          {/* Phase 2.5 — a revised grade is public, never silent. */}
          {row.revised_from_grade && (
            <span className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)', textDecoration: 'line-through' }}>
              {row.revised_from_grade}
            </span>
          )}
          <GradePill grade={row.grade} />
        </span>
      </div>
      <h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 2 }}>{row.player_name}</h3>
      <p className="mono" style={{ fontSize: 12, color: 'var(--text-secondary)', textTransform: 'capitalize', marginBottom: 4 }}>
        {row.side} {row.line} {row.stat.replace(/_/g, ' ')}
      </p>
      <p className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)', marginBottom: 12 }}>
        {row.book || '—'}{row.locked_odds ? ` · ${row.locked_odds}` : ''} · {row.game_date}
        {/* model_value is MODEL output — always labeled, never blended with market numbers. */}
        {row.model_value != null && <span> · MODEL {row.model_value}</span>}
      </p>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
        <OutcomeChip row={row} />
        <ClvChip row={row} />
      </div>
    </article>
  );
}

function EmptyLedger({ tab }: { tab: Tab }) {
  return (
    <div className="surface diagonal-cut tex-scan" style={{ padding: 48, textAlign: 'center', display: 'grid', gap: 10, justifyItems: 'center' }}>
      <p className="lbl" style={{ color: 'var(--grade-c)' }}>LEDGER EMPTY</p>
      {tab === 'mine' ? (
        <>
          <h3 style={{ fontSize: 18, fontWeight: 700 }}>No reads yet.</h3>
          <p style={{ color: 'var(--text-1)', fontSize: 14, maxWidth: 440 }}>
            Read your first prop to start building your Ledger. Every grade you run lands here the moment it completes — and settles against the real result.
          </p>
          <a href="/scan" className="btn-primary" style={{ marginTop: 8, padding: '10px 18px' }}>
            Read a Prop →
          </a>
        </>
      ) : (
        <>
          <h3 style={{ fontSize: 18, fontWeight: 700 }}>The model record starts with the next pipeline run.</h3>
          <p style={{ color: 'var(--text-1)', fontSize: 14, maxWidth: 440 }}>
            Every pre-graded prop lands here — hits, misses, pushes, and closing-line value. Nothing is deleted.
          </p>
        </>
      )}
    </div>
  );
}

function FilterRow({ label, value, onChange, options }: { label: string; value: string; onChange: (v: string) => void; options: string[] }) {
  return (
    <div>
      <span className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)', letterSpacing: '0.08em', marginRight: 12 }}>
        {label.toUpperCase()}
      </span>
      <span style={{ display: 'inline-flex', gap: 4 }}>
        {options.map((o) => {
          const active = o === value;
          return (
            <button
              key={o}
              onClick={() => onChange(o)}
              className={active ? 'btn-primary' : 'btn-ghost'}
              style={{ padding: '6px 12px', fontSize: 11 }}
            >
              {o}
            </button>
          );
        })}
      </span>
    </div>
  );
}
