'use client'

import { useState, useEffect, useMemo, useRef } from 'react'
import type { TrumpPost } from '@/types'
import { getPosts, getPostsPage, type PostListResponse } from '@/lib/api'
import { hasCached, swrFetch } from '@/lib/cache'
import PostRow, { isAiScored } from '@/components/dashboard/PostCards'
import SystemControl from '@/components/signals/SystemControl'
import PageHint from '@/components/ui/PageHint'
import InfoTip from '@/components/ui/InfoTip'
import Pagination from '@/components/ui/Pagination'

const PAGE_SIZE = 30

const SENTIMENTS = ['all', 'bullish', 'bearish', 'neutral'] as const
type SentimentFilter = (typeof SENTIMENTS)[number]
type SignalFilter = 'all' | 'actionable' | 'buy' | 'short'

interface TrumpSignalPageProps {
  initialData?: PostListResponse | null
}

const EMPTY_COUNTS: PostListResponse['counts'] = {
  all: 0,
  actionable: 0,
  buy: 0,
  short: 0,
  off_topic: 0,
}

function matchesBaseFilters(post: TrumpPost, sentFilter: SentimentFilter, hideNoise: boolean) {
  if (hideNoise && !isAiScored(post)) return false
  if (sentFilter !== 'all' && post.sentiment !== sentFilter) return false
  return true
}

function matchesSignalFilter(post: TrumpPost, sigFilter: SignalFilter) {
  if (sigFilter === 'actionable') return post.signal === 'buy' || post.signal === 'short'
  if (sigFilter === 'buy') return post.signal === 'buy'
  if (sigFilter === 'short') return post.signal === 'short'
  return true
}

function buildLocalCounts(
  posts: TrumpPost[],
  sentFilter: SentimentFilter,
  hideNoise: boolean,
): PostListResponse['counts'] {
  const sentimentScoped = posts.filter(p => sentFilter === 'all' || p.sentiment === sentFilter)
  const base = sentimentScoped.filter(p => matchesBaseFilters(p, sentFilter, hideNoise))

  return {
    all: base.length,
    actionable: base.filter(p => p.signal === 'buy' || p.signal === 'short').length,
    buy: base.filter(p => p.signal === 'buy').length,
    short: base.filter(p => p.signal === 'short').length,
    off_topic: sentimentScoped.filter(p => !isAiScored(p)).length,
  }
}

export default function TrumpSignalPage({ initialData = null }: TrumpSignalPageProps) {
  const [posts, setPosts]       = useState<TrumpPost[]>(initialData?.items ?? [])
  const [totalPosts, setTotalPosts] = useState(initialData?.total ?? 0)
  const [counts, setCounts]     = useState<PostListResponse['counts']>(initialData?.counts ?? EMPTY_COUNTS)
  const [loading, setLoading]   = useState(initialData === null)
  const [loadErr, setLoadErr]   = useState('')
  const [sentFilter, setSentFilter] = useState<SentimentFilter>('all')
  const [sigFilter, setSigFilter]   = useState<SignalFilter>('all')
  const [hideNoise, setHideNoise]   = useState(false)
  const [page, setPage]         = useState(1)
  const [serverPaging, setServerPaging] = useState(true)

  // Tracks whether we already have any rows on screen, WITHOUT putting
  // posts.length in the effect deps (which would re-run the effect on every
  // setPosts and fire a redundant fetch). Updated after each load below.
  const hasRowsRef = useRef((initialData?.items?.length ?? 0) > 0)

  useEffect(() => {
    const filters = {
      // Don't apply the sentiment bias when a directional signal filter is
      // active — Buy/Short/Actionable already imply direction, and combining
      // them with a sentiment filter produces confusing empty results.
      // sentFilter is intentionally NOT reset when the user switches tabs so
      // their preference is preserved if they switch back to 'all'.
      sentiment: sentFilter === 'all' || sigFilter !== 'all' ? undefined : sentFilter,
      signal: sigFilter === 'all' ? undefined : sigFilter,
      aiScoredOnly: hideNoise,
    } as const
    // sentFilter only participates in the key when sigFilter is 'all' — it's
    // ignored by the query otherwise, so caching it in the key would create
    // phantom cache entries that never match on the way back.
    const effectiveSent = sigFilter === 'all' ? sentFilter : 'all'
    const key = `posts-truth-page-${page}-sent-${effectiveSent}-sig-${sigFilter}-noise-${hideNoise ? '1' : '0'}`
    const legacyKey = 'posts-truth-legacy-500'
    setLoadErr('')
    setLoading(!hasRowsRef.current && !hasCached(key) && !hasCached(legacyKey))

    swrFetch(
      key,
      3 * 60_000,
      () => getPostsPage(PAGE_SIZE, page, 'truth', filters),
      fresh => {
        setPosts(fresh.items)
        setTotalPosts(fresh.total)
        setCounts(fresh.counts)
        hasRowsRef.current = fresh.items.length > 0
      },
    )
      .then(r => {
        setPosts(r.items)
        setTotalPosts(r.total)
        setCounts(r.counts)
        setServerPaging(true)
        setLoadErr('')
        hasRowsRef.current = r.items.length > 0
      })
      .catch(async e => {
        const detail = e instanceof Error ? e.message : 'Failed to load posts'
        if (!detail.includes('404')) {
          setLoadErr(detail)
          return
        }

        try {
          // Legacy fallback (old backend without /posts-paged). Pull the
          // largest window the /posts endpoint allows (le=500) so client-side
          // pagination below has the full set to slice — 200 silently dropped
          // older matching posts once the truth feed grew past one page.
          const legacyPosts = await swrFetch(
            legacyKey,
            3 * 60_000,
            () => getPosts(500, 1, 'truth'),
          )
          const localCounts = buildLocalCounts(legacyPosts, sentFilter, hideNoise)
          setPosts(legacyPosts)
          setTotalPosts(localCounts.all)
          setCounts(localCounts)
          setServerPaging(false)
          setLoadErr('')
          hasRowsRef.current = legacyPosts.length > 0
        } catch (legacyErr) {
          setLoadErr(legacyErr instanceof Error ? legacyErr.message : detail)
        }
      })
      .finally(() => setLoading(false))
  }, [page, sentFilter, sigFilter, hideNoise])

  const filtered = useMemo(
    () => serverPaging
      ? posts
      : posts.filter(p => matchesBaseFilters(p, sentFilter, hideNoise) && matchesSignalFilter(p, sigFilter)),
    [hideNoise, posts, sentFilter, serverPaging, sigFilter],
  )

  const noiseCount = counts.off_topic
  const totalPages = Math.max(1, Math.ceil(totalPosts / PAGE_SIZE))
  const safePage   = Math.min(page, totalPages)
  const pageItems  = serverPaging
    ? posts
    : filtered.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE)

  const signalLabels: Record<SignalFilter, string> = {
    all: 'All', actionable: 'Actionable', buy: 'Buy', short: 'Short',
  }
  const sentimentLabels: Record<SentimentFilter, string> = {
    all: 'All', bullish: 'Bullish', bearish: 'Bearish', neutral: 'Neutral',
  }

  return (
    <div className="page">
      <div className="page-head">
        <div>
          {/* No "⚡ Auto-trade" badge — the SystemControl strip right below
              always shows the Auto-Trade feature + its live state. */}
          <h1 className="page-title" style={{ margin: 0 }}>Trump Signal</h1>
          {/* No count slot — the filter tabs right below already show the
              per-filter numbers (All / Actionable / Buy / Short). */}
          <PageHint>
            Every Truth Social post scored in &lt;3s. Trades only when conviction clears the threshold.
          </PageHint>
        </div>
        <span className="chip"><span className="live-dot" />Live</span>
      </div>

      <SystemControl system="trump" />

      <div style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        gap: 12, margin: '16px 0 12px', flexWrap: 'wrap',
      }}>
        {/* Signal filter tabs */}
        <div className="nav-tabs" style={{ background: 'var(--bg-sunk)' }}>
          {([
            { key: 'all',        hint: 'Every post the scraper has captured.' },
            { key: 'actionable', hint: 'Only posts the AI flagged as BUY or SHORT.' },
            { key: 'buy',        hint: 'Posts the AI scored as a long signal.' },
            { key: 'short',      hint: 'Posts the AI scored as a short signal.' },
          ] as const).map(f => (
            <button
              key={f.key}
              className={`nav-tab ${sigFilter === f.key ? 'active' : ''}`}
              onClick={() => {
                setSigFilter(f.key)
                setPage(1)
                // sentFilter is intentionally NOT reset here — the user's
                // bias preference is preserved so it's still active when
                // they switch back to 'all'. The query layer ignores
                // sentFilter whenever sigFilter is directional.
              }}
            >
              {f.key === 'actionable' ? '🔥 ' : ''}
              {signalLabels[f.key]}{' '}
              <span style={{ color: 'var(--ink-4)' }}>{counts[f.key]}</span>
            </button>
          ))}
        </div>

        {/* Sentiment + noise filters — hidden when a directional filter is
            already active (actionable / buy / short), because:
            - "buy" already implies bullish direction
            - "short" already implies bearish direction
            - "actionable" = buy ∪ short — sentiment adds no further info
            Showing them would create confusing combinations (e.g. Buy + Bearish
            returns 0 results with no explanation). */}
        {sigFilter === 'all' && (
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
            {/* One-click collapse of off-topic (non-crypto, un-scored) posts.
                Hidden when a sentiment filter is active — Bullish/Bearish/Neutral
                results are all AI-scored by definition, so there are no off-topic
                posts to hide and the button would be meaningless. */}
            {(noiseCount > 0 || hideNoise) && sentFilter === 'all' && (
              <button
                onClick={() => { setHideNoise(v => !v); setPage(1) }}
                title={`Show only crypto-relevant posts — hides ${noiseCount} off-topic Trump posts`}
                style={{
                  padding: '4px 10px', borderRadius: 6,
                  border: '1px solid var(--line)',
                  background: hideNoise ? 'var(--ink)' : 'transparent',
                  color: hideNoise ? 'var(--bg)' : 'var(--ink-3)',
                  fontSize: 11, cursor: 'pointer', fontWeight: 600,
                  marginRight: 4,
                }}
              >
                {hideNoise ? 'Show all' : 'Signals only'}
              </button>
            )}
            <span style={{ fontSize: 11, color: 'var(--ink-4)', marginRight: 4 }}>
              Bias
              <InfoTip
                text="Filters by the post's directional bias (bullish / bearish / neutral). Bias ≠ trade signal — a bearish post can still be a BUY if the market reads it as priced-in."
                placement="left"
              />
            </span>
            {SENTIMENTS.map(f => (
              <button
                key={f}
                onClick={() => { setSentFilter(f); setPage(1) }}
                style={{
                  padding: '4px 10px', borderRadius: 6, border: '1px solid var(--line)',
                  background: sentFilter === f ? 'var(--ink)' : 'transparent',
                  color: sentFilter === f ? 'var(--bg)' : 'var(--ink-3)',
                  fontSize: 11, cursor: 'pointer', fontWeight: sentFilter === f ? 600 : 400,
                }}
              >
                {sentimentLabels[f]}
              </button>
            ))}
          </div>
        )}
      </div>

      {loading && <TrumpSkeleton />}

      {!loading && loadErr && (
        <div className="card" style={{ padding: 24, textAlign: 'center', color: 'var(--down)' }}>
          ⚠️ {`Couldn't load signals — ${loadErr}`}
          <div style={{ marginTop: 10 }}>
            <button className="btn ghost" style={{ fontSize: 12, padding: '6px 14px' }}
              onClick={() => location.reload()}>Retry</button>
          </div>
        </div>
      )}

      {!loading && !loadErr && totalPosts === 0 && (
        <div className="card" style={{ padding: 48, textAlign: 'center', color: 'var(--ink-3)' }}>
          No Trump signals match the current filter.
        </div>
      )}

      {!loading && pageItems.length > 0 && (
        <>
          <div className="post-stream">
            {pageItems.map(p => <PostRow key={p.id} post={p} />)}
          </div>

          <Pagination
            page={safePage}
            total={totalPages}
            count={totalPosts}
            pageSize={PAGE_SIZE}
            onChange={setPage}
          />
        </>
      )}
    </div>
  )
}

function TrumpSkeleton() {
  return (
    <div className="post-stream" style={{ marginTop: 8 }}>
      {Array.from({ length: 6 }).map((_, i) => (
        <div key={i} className="skeleton-card" style={{ marginBottom: 8 }}>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <div className="skeleton sk-line sk-w-q" />
            <div className="skeleton sk-line" style={{ width: 60 }} />
          </div>
          <div className="skeleton sk-title sk-w-3q" />
          <div className="skeleton sk-line sk-w-full" />
          <div className="skeleton sk-line sk-w-half" />
          <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
            <div className="skeleton sk-line-sm" style={{ width: 56 }} />
            <div className="skeleton sk-line-sm" style={{ width: 80 }} />
            <div className="skeleton sk-line-sm" style={{ width: 64 }} />
          </div>
        </div>
      ))}
    </div>
  )
}
