'use client'

import { useState, useEffect } from 'react'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import { useLocale } from 'next-intl'
import { useAccount } from 'wagmi'
import type { TrumpPost, BotPerformance, Candle } from '@/types'
import { useDashboardStore } from '@/store/dashboard'
import { usePriceSocket } from '@/lib/useRealtimeData'
import { getMacroSnapshot, getPerformance, getPrices, getUserPublic, getKolDivergence, getKolDigest, type MacroSnapshot } from '@/lib/api'
import type { KolDivergence, KolDigest } from '@/types'
import { getCachedViewEnvelope } from '@/lib/signedRequest'
import { swrFetch, invalidate as invalidateCache } from '@/lib/cache'
import PostRow, { SignalPill, SourceIcon, SOURCE_DISPLAY, fmtPct, TimeAgo, LocalDateTime } from '@/components/dashboard/PostCards'
import OpenPositions from '@/components/positions/OpenPositions'
import PageHint from '@/components/ui/PageHint'
import AnimatedNumber from '@/components/ui/AnimatedNumber'

// Heavy components — lazy-loaded so they don't bloat the initial JS bundle.
// ChartPanel pulls in lightweight-charts (~200KB gz); split it out.
const ChartPanel = dynamic(() => import('@/components/dashboard/ChartPanel'), {
  ssr: false,
  loading: () => <div style={{ height: 320, background: 'var(--bg-sunk)', borderRadius: 12, marginBottom: 16 }} />,
})

interface Props {
  initialPosts: TrumpPost[]
}

// ── Inline post detail panel shown in the right rail ──────────────────────────
function PostDetail({ post, onClose }: { post: TrumpPost; onClose: () => void }) {
  const impact = post.price_impact

  function fmtImpactPct(v: number | null | undefined) {
    if (v == null || isNaN(v)) return '—'
    const s = Math.abs(v).toFixed(2) + '%'
    return v >= 0 ? '+' + s : '-' + s
  }

  return (
    <div className="card" style={{ padding: 20 }}>
      {/* header */}
      <div className="row between" style={{ marginBottom: 14 }}>
        <span className="tiny">Signal detail</span>
        <button
          className="icon-btn"
          onClick={onClose}
          style={{ width: 26, height: 26, borderRadius: '50%' }}
          title="Close"
        >
          <svg width="10" height="10" viewBox="0 0 24 24">
            <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"/>
          </svg>
        </button>
      </div>

      {/* source + time */}
      <div className="row gap-s" style={{ marginBottom: 12 }}>
        <SourceIcon source={post.source} />
        <div>
          <div style={{ fontSize: 13, fontWeight: 500, fontFamily: 'var(--mono)' }}>
            {SOURCE_DISPLAY[post.source?.toLowerCase?.()]?.label ?? post.source ?? 'Signal'}
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)' }}><TimeAgo iso={post.published_at} suffix=" ago" /> · <LocalDateTime iso={post.published_at} /></div>
        </div>
      </div>

      {/* post text */}
      <p style={{ fontSize: 14, lineHeight: 1.55, margin: '0 0 16px', color: 'var(--ink)' }}>{post.text}</p>

      {/* signal + sentiment */}
      <div className="row gap-s" style={{ marginBottom: 16 }}>
        <SignalPill signal={post.signal} />
        <span className={`chip ${post.sentiment === 'bullish' ? 'up' : post.sentiment === 'bearish' ? 'down' : 'neutral'}`}>
          {post.sentiment}
        </span>
      </div>

      {/* AI confidence */}
      <div className="ai-metric-head">
        <span>AI confidence</span>
        <strong>{post.ai_confidence}%</strong>
      </div>
      <div className="confidence-bar" style={{ marginBottom: 16 }}>
        <div style={{ width: post.ai_confidence + '%' }} />
      </div>

      {/* AI reasoning */}
      {post.ai_reasoning && (
        <>
          <div className="ai-reasoning-label">
            {post.signal === 'buy' || post.signal === 'short' ? 'Why this signal fired' : 'Why this was filtered out'}
          </div>
          <div className="ai-reasoning-card scroll" style={{ marginBottom: 16 }}>
            {post.ai_reasoning}
          </div>
        </>
      )}

      {/* Price impact grid */}
      {impact && (
        <>
          <div className="tiny" style={{ marginBottom: 10 }}>Price impact · {impact.asset}</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
            {([['m5', '5m'], ['m15', '15m'], ['m1h', '1h']] as const).map(([key, label]) => {
              const v = impact[key]
              const correct = impact[`correct_${key}` as 'correct_m5' | 'correct_m15' | 'correct_m1h']
              return (
                <div key={key} style={{
                  padding: 10,
                  background: 'var(--bg-sunk)',
                  borderRadius: 'var(--r-sm)',
                  border: '1px solid var(--line)',
                  textAlign: 'center',
                }}>
                  <div style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 4 }}>{label}</div>
                  <div className={`delta ${(v ?? 0) >= 0 ? 'up' : 'down'}`} style={{ fontSize: 14, fontWeight: 600 }}>
                    {fmtImpactPct(v)}
                  </div>
                  {correct != null && (
                    <div style={{ fontSize: 10, marginTop: 4, color: correct ? 'var(--up)' : 'var(--down)' }}>
                      {correct ? '✓' : '✗'}
                    </div>
                  )}
                </div>
              )
            })}
          </div>
        </>
      )}
    </div>
  )
}

// ── Empty state for when nothing is selected ──────────────────────────────────
function SelectHint() {
  return (
    <div className="card" style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)' }}>
      <svg width="32" height="32" viewBox="0 0 24 24" fill="none" style={{ margin: '0 auto 12px', display: 'block', opacity: 0.4 }}>
        <circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="2"/>
        <path d="M12 2v3M12 19v3M2 12h3M19 12h3" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
      </svg>
      <p style={{ fontSize: 13, margin: 0, lineHeight: 1.5 }}>
        Click any marker on the chart or a post below to see details
      </p>
    </div>
  )
}

// ── Main dashboard ─────────────────────────────────────────────────────────────
export default function DashboardClient({ initialPosts }: Props) {
  const intlLocale = useLocale()
  const isZh = false  // i18n shelved — Chinese branches kept as dead code for future revival; see messages/zh.json
  const { asset, setAsset, timeframe, setTimeframe: _setTimeframe, setLivePrice, setSubscribed, setBotReadiness, setHlApiKeySet, setPaperMode, isSubscribed, hlApiKeySet, botReadiness, livePrices } = useDashboardStore()
  function setTimeframe(tf: string) { _setTimeframe(tf as '5m' | '15m' | '1H' | '4H' | '1D' | '1W') }
  const { address, isConnected } = useAccount()
  const params = useParams()
  const locale = (typeof params?.locale === 'string' ? params.locale : 'en')
  const [posts, setPosts] = useState<TrumpPost[]>(initialPosts)
  const [performance, setPerformance] = useState<BotPerformance | undefined>(undefined)
  const [candles, setCandles] = useState<Candle[]>([])
  const [chartErr, setChartErr] = useState('')
  const [chartReload, setChartReload] = useState(0)
  const [selectedPostId, setSelectedPostId] = useState<number | null>(null)
  const [macro, setMacro] = useState<MacroSnapshot | null>(null)
  const [kolDivergences, setKolDivergences] = useState<KolDivergence[]>([])
  const [kolDigest, setKolDigest] = useState<KolDigest | null>(null)
  // For 1D: show all posts from selected day
  const [selectedDayPosts, setSelectedDayPosts] = useState<TrumpPost[] | null>(null)

  useEffect(() => {
    // B42: cancel the in-flight getUserPublic if the wallet switches again
    // before it resolves — prevents old wallet's data polluting new wallet's state.
    let cancelled = false
    if (!isConnected || !address) {
      setSubscribed(false)
      setHlApiKeySet(false)
      setBotReadiness('unknown')
      setPerformance(undefined)
      return () => { cancelled = true }
    }
    // Clear account-scoped bot state immediately (setWallet already resets these
    // via store, but DashboardClient may be rendered without a wallet switch —
    // keep explicit resets here for clarity and safety).
    setSubscribed(false)
    setHlApiKeySet(false)
    setBotReadiness('unknown')
    // `snapAddr !== address` would be a stale-closure trap: both are the same
    // closed-over value. Use `cancelled` (set by effect cleanup) as the sole guard.
    getUserPublic(address.toLowerCase())
      .then((user) => {
        if (cancelled) return   // effect cleaned up = wallet changed or unmounted
        setSubscribed(user.active)
        setHlApiKeySet(user.hl_api_key_set)
        setBotReadiness(user.hl_api_key_set ? 'saved' : 'unknown')
        setPaperMode(!!user.paper_mode)
      })
      .catch(() => {})
    return () => { cancelled = true }
  }, [address, isConnected, setSubscribed, setHlApiKeySet, setBotReadiness])

  useEffect(() => {
    if (!isConnected || !address) {
      setPerformance(undefined)
      return
    }
    let cancelled = false
    ;(async () => {
      try {
        const env = getCachedViewEnvelope('view_performance', address)
          ?? getCachedViewEnvelope('view_user', address)
        if (!env) {
          if (!cancelled) setPerformance(undefined)
          return
        }
        const data = await getPerformance(address, env)
        if (!cancelled) setPerformance(data)
      } catch {
        if (!cancelled) setPerformance(undefined)
      }
    })()
    return () => { cancelled = true }
  }, [address, isConnected])

  const [freshPostId, setFreshPostId] = useState<number | null>(null)

  usePriceSocket({
    onPrice: (a, price) => setLivePrice(a, price),
    onNewPost: (post) => {
      const p = post as TrumpPost
      // Dedup: WS may resend a post already in initialPosts or a prior push.
      setPosts((prev) => prev.some(x => x.id === p.id) ? prev : [p, ...prev].slice(0, 500))
      setFreshPostId(p.id)
      // Keep a ref to the timer so we can cancel it if the component unmounts
      // before it fires (avoids setState-after-unmount warning).
      const timer = setTimeout(() => setFreshPostId((id) => (id === p.id ? null : id)), 1400)
      return () => clearTimeout(timer)  // returned but not used by usePriceSocket — see L1 note
    },
  })

  useEffect(() => {
    let cancelled = false
    setCandles([])
    setChartErr('')
    const priceKey = `prices-${asset}-${timeframe}`
    if (chartReload > 0) invalidateCache(priceKey)
    swrFetch(
      priceKey,
      90_000,
      () => getPrices(asset, timeframe),
      fresh => { if (!cancelled) { setCandles(fresh); setChartErr('') } },
    )
      .then(c => { if (!cancelled) { setCandles(c); setChartErr('') } })
      .catch(e => { if (!cancelled) setChartErr(e instanceof Error ? e.message : 'Failed to load price data') })
    return () => { cancelled = true }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [asset, timeframe, chartReload])

  useEffect(() => {
    let alive = true
    function load() {
      swrFetch(
        'macro-snapshot',
        10 * 60_000,
        () => getMacroSnapshot(),
        fresh => { if (alive) setMacro(fresh) },
      )
        .then((snap) => { if (alive) setMacro(snap) })
        .catch(() => {})
    }
    load()
    const id = setInterval(load, 10 * 60_000)
    return () => { alive = false; clearInterval(id) }
  }, [])

  // KOL divergence + digest — loaded once, 30 min cache (changes daily)
  useEffect(() => {
    let alive = true
    // KOL ingestion is daily and sparse — a 7d window is frequently empty
    // (e.g. 7d=0 while 30d=196), which hid the whole sidebar card. Use a 30d
    // window so the Overview reflects the data that actually exists.
    swrFetch('kol-divergence-30d', 30 * 60_000, () => getKolDivergence({ days: 30 }), f => { if (alive) setKolDivergences(f.items ?? []) })
      .then(r => { if (alive) setKolDivergences(r.items ?? []) })
      .catch(() => {})
    swrFetch('kol-digest-30d', 30 * 60_000, () => getKolDigest(30), f => { if (alive) setKolDigest(f) })
      .then(r => { if (alive) setKolDigest(r) })
      .catch(() => {})
    return () => { alive = false }
  }, [])

  const selectedPost = posts.find(p => p.id === selectedPostId) ?? null

  // Show actionable signals first (buy/short), then most recent hold/neutral.
  // Cap at 8 total so the list doesn't get too long.
  const actionable = posts.filter(p => p.signal === 'buy' || p.signal === 'short').slice(0, 4)
  const recentOthers = posts.filter(p => p.signal !== 'buy' && p.signal !== 'short').slice(0, 4)
  const recentPosts = [...actionable, ...recentOthers].slice(0, 8)

  const lastCandle = candles[candles.length - 1]
  // Live price beats the most recent candle close — the candle is only
  // refreshed when the user changes asset/timeframe, so without WS the number
  // is frozen at page load.
  const livePrice = livePrices[asset]
  const displayPrice = livePrice ?? lastCandle?.close ?? null

  // 24-hour change. Picking by candle index is wrong (200×4H = 33 days).
  // Find the candle whose `time` is closest to (now − 24h) and use its open.
  const now = lastCandle ? lastCandle.time : Math.floor(Date.now() / 1000)
  const target24h = now - 24 * 3600
  let baseline24h: typeof lastCandle | undefined
  if (candles.length) {
    baseline24h = candles[0]
    for (const c of candles) {
      if (c.time <= target24h) baseline24h = c
      else break
    }
  }
  const priceChange =
    displayPrice != null && baseline24h
      ? ((displayPrice - baseline24h.open) / baseline24h.open) * 100
      : 0

  const totalPosts = posts.length
  const todayKey = new Date().toISOString().slice(0, 10)
  const signalsToday = posts.filter(p => p.published_at?.slice(0, 10) === todayKey).length
  const actionablePosts = posts.filter(p => p.signal === 'buy' || p.signal === 'short').length
  const trumpActionable = posts.filter(p => (p.source || '') === 'truth' && (p.signal === 'buy' || p.signal === 'short')).length
  const macroActionable = posts.filter(p => ((p.source || '') === 'btc_bottom_reversal' || (p.source || '') === 'funding_reversal') && (p.signal === 'buy' || p.signal === 'short')).length
  // Use actual KOL divergence count (divergence-flagged items from the API),
  // not a source==='kol' post count (which never matches — source is 'substack'/'blog'/etc.).
  const kolMentions = kolDivergences.filter(d => d.signal_type === 'divergence').length
  const winRate = performance?.win_rate ?? 0
  const netPnl = performance?.net_pnl_usd ?? 0
  const hasPriceData = candles.length > 0
  const hasPerformanceData = Boolean(performance)
  const macroScore = macro?.composite_score ?? null
  const macroRegime = macro?.regime_label ?? null
  const macroPct = macroScore == null ? 50 : Math.max(0, Math.min(100, (macroScore + 100) / 2))
  // Derive tone from the backend's regime_label, NOT a re-thresholded score.
  // The backend uses ±20 for BULLISH/BEARISH; re-deriving with ±15 here made
  // a score of e.g. 15.2 read "Supportive" in the card while the same card's
  // regime label said NEUTRAL. Trusting regime_label keeps them consistent.
  const macroTone =
    macroRegime == null ? (macroScore == null ? 'neutral' : 'neutral')
    : macroRegime === 'BULL' || macroRegime === 'BULLISH' ? 'bull'
    : macroRegime === 'BEAR' || macroRegime === 'BEARISH' ? 'bear'
    : 'neutral'
  const macroSummary =
    macroScore == null ? 'Daily macro composite not loaded yet.'
    : macroTone === 'bull' ? 'Supportive backdrop — trend setups have room.'
    : macroTone === 'bear' ? 'Defensive backdrop — preserve size, downside moves are cleaner.'
    : 'Mixed backdrop — context only, not a directional trigger.'

  return (
    <div className="page wide">
      <div className="page-head">
        <div>
          <h1 className="page-title">{isZh ? '信号总览' : 'Signal monitor'}</h1>
          <PageHint count={actionablePosts > 0 ? `${actionablePosts} actionable · ${totalPosts} tracked` : undefined}>
            Trump · Macro · KOL divergence — live.
          </PageHint>
        </div>
        <span className="chip"><span className="live-dot" />Live</span>
      </div>

      {/* Open positions — what's on the book right now. Renders only when
          a subscribed wallet is connected, so guests see the normal feed. */}
      <OpenPositions />


      <div className="overview-shell">
        <div className="overview-main">
          <section className="overview-market-card">
            <div className="overview-card-head">
              <div>
                <div className="overview-kicker">Market and macro</div>
                <div className="overview-headline-row">
                  <AnimatedNumber
                    className="hero-value mono"
                    style={{ fontSize: 40 }}
                    value={displayPrice}
                    display={displayPrice != null ? '$' + Math.round(displayPrice).toLocaleString() : '—'}
                  />
                  <span className={`chip ${priceChange >= 0 ? 'up' : 'down'}`}>{hasPriceData ? fmtPct(priceChange) : 'Feed pending'} · 24h</span>
                </div>
                <div className="overview-market-subtitle">{asset} · live signal context</div>
              </div>
              <div className="overview-controls">
                <div className="asset-switch">
                  <button className={asset === 'BTC' ? 'on' : ''} onClick={() => setAsset('BTC')}>
                    <span className="asset-dot btc" /> BTC
                  </button>
                  <button className={asset === 'ETH' ? 'on' : ''} onClick={() => setAsset('ETH')}>
                    <span className="asset-dot eth" /> ETH
                  </button>
                </div>
                <div className="tf-bar">
                  {(['5m', '15m', '1H', '4H', '1D'] as const).map(t => (
                    <button key={t} className={timeframe === t ? 'on' : ''} onClick={() => setTimeframe(t)}>{t}</button>
                  ))}
                </div>
              </div>
            </div>

            <div className={`overview-macro-card ${macroTone}`}>
              <div className="overview-macro-band">
                <div className="overview-macro-copy">
                  <div className="overview-macro-title-row">
                    <div className="overview-macro-title">Macro composite</div>
                    <div className="overview-macro-stamp">today · 8 indicators</div>
                  </div>
                  <div className="overview-macro-text">{macroSummary}</div>
                </div>
                <div className="overview-macro-score">
                  <div className={`overview-score-value ${macroTone}`}>
                    {macroScore == null ? '—' : `${macroScore > 0 ? '+' : ''}${macroScore.toFixed(1)}`}
                  </div>
                  <div className={`overview-score-pill ${macroTone}`}>{macroRegime ?? 'Waiting'}</div>
                </div>
              </div>
              <div className="overview-score-track">
                <div className={`overview-score-needle ${macroTone}`} style={{ left: `calc(${macroPct}% - 11px)` }} />
              </div>
              <div className="overview-score-scale">
                <span>−100 bear</span>
                <span>0 neutral</span>
                <span>+100 bull</span>
              </div>
            </div>

          </section>

          {/* ── Unified stats row ─────────────────────────────────────────── */}
          <div className="overview-stats-bar">
            {([
              { label: 'Trump signals', value: trumpActionable, href: `/${locale}/trump` },
              { label: 'Macro signals', value: macroActionable, href: `/${locale}/macro` },
              { label: 'KOL divergence', value: kolMentions,   href: `/${locale}/kol` },
            ] as const).map(({ label, value, href }) => {
              const empty = value === 0
              const inner = (
                <>
                  <span className="stats-bar-label">{label}</span>
                  <span className="stats-bar-value" style={{ color: empty ? 'var(--ink-4)' : 'var(--ink)' }}>
                    {empty ? '—' : value}
                  </span>
                </>
              )
              return <Link key={label} href={href} className="stats-bar-item">{inner}</Link>
            })}
            <div className="stats-bar-item passive" style={{ borderRight: 'none' }}>
              <span className="stats-bar-label">My P&amp;L · 30d</span>
              <span className="stats-bar-value" style={{
                color: hasPerformanceData ? (netPnl >= 0 ? 'var(--up)' : 'var(--down)') : 'var(--ink-4)',
              }}>
                {hasPerformanceData
                  ? `${netPnl >= 0 ? '+' : '−'}$${Math.abs(netPnl).toLocaleString('en-US', { maximumFractionDigits: 0 })}`
                  : '—'}
              </span>
            </div>
          </div>
        </div>

        <aside className="overview-side">
          {isConnected ? (
            <section className="overview-side-card">
              <div className="overview-kicker">Account</div>
              <div className="overview-account-list">
                <div className="overview-account-item">
                  <span>Wallet</span>
                  <strong className="mono">{address ? `${address.slice(0, 6)}…${address.slice(-4)}` : '—'}</strong>
                </div>
                <div className="overview-account-item">
                  <span>Settings</span>
                  <strong>{hasPerformanceData ? 'Loaded' : 'Not loaded'}</strong>
                </div>
                {hasPerformanceData && (
                  <div className="overview-account-item">
                    <span>Win rate</span>
                    <strong>{`${(winRate * 100).toFixed(1)}%`}</strong>
                  </div>
                )}
              </div>
            </section>
          ) : (
            <section className="overview-side-card" style={{ padding: '14px 16px' }}>
              <div className="overview-kicker" style={{ marginBottom: 12 }}>Get started</div>
              {([
                { icon: '📡', head: 'Free signals', sub: 'Trump · Macro · KOL — no account needed' },
                { icon: '🔔', head: 'Telegram alerts', sub: 'Signal fires → your phone in seconds' },
                { icon: '⚡', head: 'Auto-trade', sub: 'Trade-only key · no withdrawal access' },
              ] as const).map(({ icon, head, sub }) => (
                <div key={head} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 12 }}>
                  <span style={{ fontSize: 16, flexShrink: 0, marginTop: 1 }}>{icon}</span>
                  <div>
                    <div style={{ fontSize: 12, fontWeight: 700, lineHeight: 1.3 }}>{head}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-4)', lineHeight: 1.4, marginTop: 1 }}>{sub}</div>
                  </div>
                </div>
              ))}
            </section>
          )}
          {/* KOL divergence hook — highest-conviction signal type */}
          {(kolDivergences.length > 0 || (kolDigest && kolDigest.tickers.length > 0)) && (
            <section className="overview-side-card" style={{ padding: '14px 16px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                <div className="overview-kicker">KOL intel · 30d</div>
                <a href={`/${locale}/kol`} style={{ fontSize: 10, color: 'var(--amber-ink)', textDecoration: 'none', fontWeight: 700 }}>
                  Full feed ↗
                </a>
              </div>

              {/* Latest divergence — the money signal */}
              {kolDivergences.filter(d => d.signal_type === 'divergence').slice(0, 1).map(d => (
                <div key={d.id} style={{
                  padding: '9px 11px', borderRadius: 7, marginBottom: 8,
                  background: 'rgba(245,158,11,0.08)', border: '1px solid rgba(245,158,11,0.25)',
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
                    <span style={{ fontSize: 10, fontWeight: 800, color: '#f59e0b', letterSpacing: '0.05em' }}>⚠️ DIVERGENCE</span>
                    <span style={{ fontSize: 10, color: 'var(--ink-4)' }}>
                      {/* post_at = when the KOL actually published. created_at
                          is the DB write time, which a backfill/late scan can
                          set to "now", making an old event look fresh. */}
                      {Math.round((Date.now() - new Date(d.post_at).getTime()) / 864e5)}d ago
                    </span>
                  </div>
                  <div style={{ fontSize: 12, fontWeight: 600 }}>
                    @{d.handle} said <span style={{ color: d.post_action === 'bullish' || d.post_action === 'buy' ? 'var(--up)' : 'var(--down)' }}>{d.post_action}</span>
                    {' '}on <strong>{d.ticker}</strong>
                  </div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>
                    Then {d.onchain_action.replace('_', ' ')}{d.usd_after != null ? ` → $${(d.usd_after / 1000).toFixed(0)}k on-chain` : ''}
                  </div>
                </div>
              ))}

              {/* Top digest tickers */}
              {kolDigest && kolDigest.tickers.slice(0, 2).map(t => (
                <div key={t.ticker} style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '6px 0', borderBottom: '1px solid var(--line)',
                }}>
                  <div>
                    <span style={{ fontSize: 12, fontWeight: 700 }}>{t.ticker}</span>
                    <span style={{ fontSize: 10, color: 'var(--ink-4)', marginLeft: 6 }}>{t.kol_count} KOLs</span>
                  </div>
                  <span style={{
                    fontSize: 10, fontWeight: 700, padding: '2px 7px', borderRadius: 4,
                    background: t.side === 'long' ? 'var(--up-soft)' : t.side === 'short' ? 'rgba(220,38,38,.12)' : 'var(--bg-sunk)',
                    color: t.side === 'long' ? 'var(--up)' : t.side === 'short' ? 'var(--down)' : 'var(--ink-3)',
                  }}>
                    {t.dominant_action.toUpperCase()}
                  </span>
                </div>
              ))}
            </section>
          )}
        </aside>
      </div>

      <div className="dash-grid">
        {/* Left: Chart + signal stream */}
        <div className="stack gap-l">
          <div className="card" style={{ padding: 24 }}>
            <div className="row between" style={{ marginBottom: 18, alignItems: 'flex-start' }}>
              <div>
                <div className="tiny">Price · {asset}</div>
                <div className="row gap-m" style={{ marginTop: 6 }}>
                  <AnimatedNumber
                    className="hero-value mono"
                    style={{ fontSize: 32 }}
                    value={displayPrice}
                    display={displayPrice != null ? '$' + Math.round(displayPrice).toLocaleString() : '—'}
                  />
                  <span className={`chip ${priceChange >= 0 ? 'up' : 'down'}`}>{hasPriceData ? fmtPct(priceChange) : 'Feed pending'} · 24h</span>
                </div>
              </div>
              <div className="tiny" style={{ color: 'var(--ink-3)' }}>Live chart with signal markers</div>
            </div>

            {chartErr && (
              <div style={{ padding: '8px 12px', margin: '0 0 8px', fontSize: 12,
                            color: 'var(--down)', background: 'var(--down-soft, rgba(220,38,38,.08))',
                            borderRadius: 8 }}>
                ⚠️ {asset} {timeframe} chart failed to load — {chartErr}{' '}
                <button className="btn ghost" style={{ fontSize: 11, padding: '3px 10px', marginLeft: 6 }}
                  onClick={() => setChartReload(n => n + 1)}>Retry</button>
              </div>
            )}
            <div className="chart-wrap" style={{ position: 'relative' }}>
              {/* Loading overlay — shown while candles are fetching (candles=[] and no error).
                  Prevents the user from seeing a blank lightweight-charts canvas. */}
              {!hasPriceData && !chartErr && (
                <div style={{
                  position: 'absolute', inset: 0, zIndex: 4,
                  background: 'var(--bg-sunk)', borderRadius: 'var(--r-sm)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  gap: 10, color: 'var(--ink-3)', fontSize: 13,
                }}>
                  <span className="live-dot" style={{ background: 'var(--amber)' }} />
                  Loading chart…
                </div>
              )}
              <ChartPanel
                posts={posts}
                candles={candles}
                externalSelectedId={selectedPostId}
                onSelectPost={(id) => {
                  setSelectedDayPosts(null)
                  setSelectedPostId(id)
                }}
                onSelectDayPosts={(dayPosts) => {
                  setSelectedDayPosts(dayPosts)
                  setSelectedPostId(null)
                }}
              />
            </div>

            <div className="chart-footnote">
              <div className="item"><span className="legend-dot" style={{ background: '#26a69a' }} /> Buy signal</div>
              <div className="item"><span className="legend-dot" style={{ background: '#ef5350' }} /> Short signal</div>
              <div className="item"><span className="legend-dot" style={{ background: '#aaaaaa' }} /> Hold / filtered</div>
              {asset === 'BTC' && <div className="item"><span className="legend-dot macro" /> Macro reversal highlight</div>}
              <div style={{ marginLeft: 'auto' }}>Binance candles via backend API · click marker to inspect</div>
            </div>
          </div>

          {/* Recent signals list */}
          <div>
            <div className="section-title">
              <h2>Recent signals</h2>
              <span className="hint">
                {actionable.length > 0 ? `${actionable.length} actionable · ` : ''}
                {`${totalPosts} tracked`}
              </span>
            </div>
            <div className="post-stream">
              {recentPosts.map(p => (
                <div key={p.id} className={p.id === freshPostId ? 'signal-enter' : undefined}>
                  <PostRow post={p} selected={selectedPostId === p.id} onClick={() => {
                    // Clear the day-list view first: the right panel renders
                    // selectedDayPosts with higher priority than selectedPost,
                    // so without this the detail never appears while a candle's
                    // multi-post list is open.
                    setSelectedDayPosts(null)
                    setSelectedPostId(selectedPostId === p.id ? null : p.id)
                  }} />
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Right rail */}
        <div className="rail">
          {/* Day-view: all posts from clicked day */}
          {selectedDayPosts ? (
            <div className="card" style={{ padding: 20 }}>
              <div className="row between" style={{ marginBottom: 14 }}>
                <div>
                  <div className="tiny">{selectedDayPosts.length} posts · this candle</div>
                  <div style={{ fontSize: 15, fontWeight: 600, marginTop: 2 }}>
                    <LocalDateTime iso={selectedDayPosts[0].published_at} opts={{ month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }} />
                  </div>
                </div>
                <button className="icon-btn" onClick={() => { setSelectedDayPosts(null); setSelectedPostId(null) }}
                  style={{ width:26, height:26, borderRadius:'50%' }}>
                  <svg width="10" height="10" viewBox="0 0 24 24">
                    <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"/>
                  </svg>
                </button>
              </div>
              <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                {selectedDayPosts.map(p => {
                  const expanded = selectedPostId === p.id
                  const impact = p.price_impact
                  function fmtImpactPct(v: number | null | undefined) {
                    if (v == null || isNaN(v)) return '—'
                    const s = Math.abs(v).toFixed(2) + '%'
                    return v >= 0 ? '+' + s : '-' + s
                  }
                  return (
                    <div key={p.id}
                      style={{ border:`1px solid ${expanded?'var(--amber)':'var(--line)'}`,
                        borderRadius:'var(--r-sm)', overflow:'hidden', transition:'border-color 0.15s' }}>
                      {/* collapsed row — always visible */}
                      <div onClick={() => setSelectedPostId(expanded ? null : p.id)}
                        style={{ padding:12, cursor:'pointer',
                          background: expanded ? 'var(--bg-sunk)' : 'transparent' }}>
                        <div className="row between" style={{ marginBottom:6 }}>
                          <div className="row gap-s">
                            <SignalPill signal={p.signal} />
                            <span className={`chip ${p.sentiment === 'bullish' ? 'up' : p.sentiment === 'bearish' ? 'down' : 'neutral'}`}
                              style={{ fontSize:10 }}>{p.sentiment}</span>
                          </div>
                          <span style={{ fontSize:11, color:'var(--ink-3)' }}><TimeAgo iso={p.published_at} /></span>
                        </div>
                        <p style={{ fontSize:12, lineHeight:1.5, margin:'0 0 8px', color:'var(--ink-2)',
                          ...(expanded ? {} : {
                            display:'-webkit-box', WebkitLineClamp: 3,
                            WebkitBoxOrient:'vertical', overflow:'hidden'
                          }) }}>
                          {p.text}
                        </p>
                        <div style={{ display:'flex', gap:6, alignItems:'center' }}>
                          <div style={{ flex:1, height:3, background:'var(--line)', borderRadius:2, overflow:'hidden' }}>
                            <div style={{ width:p.ai_confidence+'%', height:'100%', background:'var(--amber)', borderRadius:2 }} />
                          </div>
                          <span style={{ fontSize:10, color:'var(--ink-3)', whiteSpace:'nowrap' }}>{p.ai_confidence}% conf</span>
                        </div>
                      </div>

                      {/* expanded detail */}
                      {expanded && (
                        <div style={{ padding:'0 12px 12px', borderTop:'1px solid var(--line)' }}>
                          {p.ai_reasoning && (
                            <>
                              <div className="ai-reasoning-label" style={{ margin:'10px 0 8px' }}>AI reasoning</div>
                              <div className="ai-reasoning-card" style={{ padding: '14px 15px', fontSize: 14, lineHeight: 1.7 }}>
                                {p.ai_reasoning}
                              </div>
                            </>
                          )}
                          {impact && (
                            <>
                              <div className="tiny" style={{ margin:'10px 0 6px' }}>Price impact · {impact.asset}</div>
                              <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:6 }}>
                                {(['m5','m15','m1h'] as const).map((key) => {
                                  const label = key === 'm5' ? '5m' : key === 'm15' ? '15m' : '1h'
                                  const v = impact[key]
                                  const correct = impact[`correct_${key}` as 'correct_m5'|'correct_m15'|'correct_m1h']
                                  return (
                                    <div key={key} style={{ padding:8, background:'var(--bg-sunk)',
                                      borderRadius:'var(--r-sm)', border:'1px solid var(--line)', textAlign:'center' }}>
                                      <div style={{ fontSize:10, color:'var(--ink-3)', marginBottom:3 }}>{label}</div>
                                      <div className={`delta ${(v??0)>=0?'up':'down'}`} style={{ fontSize:13, fontWeight:600 }}>
                                        {fmtImpactPct(v)}
                                      </div>
                                      {correct != null && (
                                        <div style={{ fontSize:10, marginTop:3, color: correct ? 'var(--up)' : 'var(--down)' }}>
                                          {correct ? '✓' : '✗'}
                                        </div>
                                      )}
                                    </div>
                                  )
                                })}
                              </div>
                            </>
                          )}
                        </div>
                      )}
                    </div>
                  )
                })}
              </div>
            </div>
          ) : selectedPost ? (
            <PostDetail post={selectedPost} onClose={() => setSelectedPostId(null)} />
          ) : (
            <SelectHint />
          )}

        </div>
      </div>
    </div>
  )
}
