"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { fetchAPI, postAPI } from "@/lib/api";
import type {
  IndexOverview,
  LatestResult,
  MarketTemperatureData,
  OpsStatusResponse,
  RecommendationData,
  SectorData,
  StrategyBoard,
} from "@/lib/api";
import { ThemeToggle } from "@/components/theme-toggle";
import { useAuth } from "@/hooks/use-auth";
import { useWebSocket } from "@/hooks/use-websocket";

interface ScanStatus {
  is_trading: boolean;
  scan_mode: string;
  description: string;
}

const SCAN_TIMEOUT_MS = 120_000;

export default function DashboardPage() {
  const { user } = useAuth();
  const [data, setData] = useState<LatestResult | null>(null);
  const [marketTemperature, setMarketTemperature] = useState<MarketTemperatureData | null>(null);
  const [sectors, setSectors] = useState<SectorData[]>([]);
  const [scanStatus, setScanStatus] = useState<ScanStatus | null>(null);
  const [loading, setLoading] = useState(true);
  const [refreshing, setRefreshing] = useState(false);
  const [refreshResult, setRefreshResult] = useState<string | null>(null);
  const [indices, setIndices] = useState<IndexOverview[]>([]);
  const [strategyBoard, setStrategyBoard] = useState<StrategyBoard | null>(null);
  const [opsStatus, setOpsStatus] = useState<OpsStatusResponse | null>(null);
  const [opsRunning, setOpsRunning] = useState<string | null>(null);
  const scanTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const loadData = useCallback(async () => {
    try {
      const [latestResult, sectorResult, statusResult, overviewResult] = await Promise.allSettled([
        fetchAPI<LatestResult>("/api/recommendations/latest"),
        fetchAPI<SectorData[]>("/api/sectors/hot?limit=8"),
        fetchAPI<ScanStatus>("/api/recommendations/status"),
        fetchAPI<IndexOverview[]>("/api/market/overview"),
      ]);

      const latest = latestResult.status === "fulfilled" ? latestResult.value : null;
      const sectorData = sectorResult.status === "fulfilled" ? sectorResult.value : [];
      const status = statusResult.status === "fulfilled" ? statusResult.value : null;
      const overview = overviewResult.status === "fulfilled" ? overviewResult.value : [];

      if (latest) {
        setData(latest);
        const realtimeTemp = await fetchAPI<MarketTemperatureData>("/api/market/temperature").catch(() => latest.market_temperature);
        setMarketTemperature(realtimeTemp ?? latest.market_temperature ?? null);
      }
      setSectors(sectorData);
      if (status) setScanStatus(status);
      setIndices(overview);
    } catch (error) {
      console.error("加载数据失败:", error);
    } finally {
      setLoading(false);
    }
  }, []);

  const loadSecondaryData = useCallback(async () => {
    try {
      const [board, ops] = await Promise.all([
        fetchAPI<StrategyBoard>("/api/market/strategy-board").catch(() => null),
        fetchAPI<OpsStatusResponse>("/api/market/ops-status").catch(() => null),
      ]);
      setStrategyBoard(board);
      setOpsStatus(ops);
    } catch (error) {
      console.error("加载次要数据失败:", error);
    }
  }, []);

  const clearScanTimeout = useCallback(() => {
    if (scanTimeoutRef.current) {
      clearTimeout(scanTimeoutRef.current);
      scanTimeoutRef.current = null;
    }
  }, []);

  useEffect(() => {
    loadData();
  }, [loadData]);

  useEffect(() => {
    loadSecondaryData();
  }, [loadSecondaryData]);

  useWebSocket(
    useCallback((msg: { type: string; count?: number; scan_mode?: string; message?: string }) => {
      clearScanTimeout();
      if (msg.type === "scan_update") {
        const modeLabel = msg.scan_mode === "realtime_today" || msg.scan_mode === "intraday" ? "今日实时" : "历史收盘";
        setRefreshResult(`${modeLabel}扫描完成，发现 ${msg.count ?? 0} 只股票`);
        setRefreshing(false);
        loadData();
        loadSecondaryData();
        setTimeout(() => setRefreshResult(null), 5000);
      } else if (msg.type === "scan_error") {
        setRefreshResult("扫描失败，请重试");
        setRefreshing(false);
        setTimeout(() => setRefreshResult(null), 5000);
      } else {
        loadData();
        loadSecondaryData();
      }
    }, [clearScanTimeout, loadData, loadSecondaryData]),
    ["scan_update", "scan_error", "llm_analysis_ready", "sector_scan_ready", "scan_complete"]
  );

  const handleRefresh = async () => {
    setRefreshing(true);
    setRefreshResult(null);
    try {
      const res = await postAPI<{
        status: string;
        message?: string;
        is_trading: boolean;
      }>("/api/recommendations/refresh?scan_session=manual");

      if (res.status === "already_running") {
        setRefreshResult(res.message || "扫描正在执行中，请稍候");
      } else if (res.status === "scanning") {
        setRefreshResult("扫描已启动，完成后自动刷新...");
      }

      scanTimeoutRef.current = setTimeout(() => {
        setRefreshResult("扫描超时，已自动刷新数据");
        setRefreshing(false);
        loadData();
        loadSecondaryData();
        setTimeout(() => setRefreshResult(null), 5000);
      }, SCAN_TIMEOUT_MS);
    } catch (error) {
      console.error("触发扫描失败:", error);
      setRefreshResult("触发扫描失败，请重试");
      setRefreshing(false);
      setTimeout(() => setRefreshResult(null), 5000);
    }
  };

  const handleAdminAction = async (action: "update_tracking") => {
    setOpsRunning(action);
    setRefreshResult(null);
    try {
      const result = await postAPI<{ tracked?: number; win_rate?: number }>("/api/recommendations/update-tracking");
      setRefreshResult(`跟踪已更新，样本 ${result.tracked ?? 0}，胜率 ${clampPercent(result.win_rate ?? 0).toFixed(1)}%`);
      await loadData();
      await loadSecondaryData();
      setTimeout(() => setRefreshResult(null), 5000);
    } catch (error) {
      console.error("管理员操作失败:", error);
      setRefreshResult("管理员操作失败，请重试");
      setTimeout(() => setRefreshResult(null), 5000);
    } finally {
      setOpsRunning(null);
    }
  };

  useEffect(() => {
    return () => clearScanTimeout();
  }, [clearScanTimeout]);

  const recommendations = data?.recommendations ?? [];
  const actionable = recommendations.filter((rec) => rec.action_plan === "可操作");
  const watch = recommendations.filter((rec) => rec.action_plan === "重点关注");
  const observe = recommendations.filter((rec) => !["可操作", "重点关注"].includes(rec.action_plan ?? ""));

  const marketSummary = useMemo(
    () => buildMarketSummary(data?.strategy_profile ?? null, marketTemperature, strategyBoard, scanStatus, actionable.length, watch.length),
    [actionable.length, data?.strategy_profile, marketTemperature, scanStatus, strategyBoard, watch.length]
  );

  const todayActions = useMemo(
    () => buildActionGuides(data?.strategy_profile ?? null, strategyBoard, marketTemperature, actionable, watch, observe, sectors),
    [actionable, data?.strategy_profile, marketTemperature, observe, sectors, strategyBoard, watch]
  );

  const focusQueue = actionable.length ? actionable : watch.length ? watch : recommendations;

  if (loading) {
    return (
      <div className="max-w-7xl mx-auto px-4 md:px-8 pt-6 space-y-4">
        <div className="h-28 glass-card-static animate-shimmer" />
        <div className="h-48 glass-card-static animate-shimmer" />
        <div className="h-64 glass-card-static animate-shimmer" />
      </div>
    );
  }

  return (
    <div className="max-w-7xl mx-auto px-4 md:px-8 pt-6 pb-20 md:pb-10 space-y-5">
      <div className="flex items-start justify-between gap-4 animate-fade-in-up">
        <div>
          <div className="flex items-center gap-2">
            <h1 className="text-lg font-bold tracking-tight">今日作战</h1>
            {scanStatus?.is_trading ? (
              <span className="inline-flex items-center gap-1.5 rounded-full bg-emerald-500/10 px-2 py-0.5 text-[10px] text-emerald-400">
                <span className="h-1.5 w-1.5 rounded-full bg-emerald-400 animate-pulse" />
                交易中
              </span>
            ) : (
              <span className="inline-flex items-center gap-1.5 rounded-full bg-surface-2 px-2 py-0.5 text-[10px] text-text-muted">
                已收盘
              </span>
            )}
          </div>
        </div>

        <div className="flex items-center gap-2">
          <ThemeToggle />
          {user?.role === "admin" ? (
            <button
              onClick={handleRefresh}
              disabled={refreshing}
              className="rounded-xl border border-amber-500/15 bg-amber-500/10 px-4 py-2 text-xs font-medium text-amber-400 transition-colors hover:bg-amber-500/15 disabled:opacity-40"
            >
              {refreshing ? "扫描中..." : scanStatus?.is_trading ? "盘中扫描" : "立即扫描"}
            </button>
          ) : null}
        </div>
      </div>

      {refreshResult ? (
        <div className="glass-card-static animate-fade-in-up border-amber-500/15 px-4 py-2.5 text-xs text-amber-400">
          {refreshResult}
        </div>
      ) : null}

      <DecisionHero
        board={strategyBoard}
        summary={marketSummary}
        actionableCount={actionable.length}
        watchCount={watch.length}
        observeCount={observe.length}
      />

      <div className="grid grid-cols-1 xl:grid-cols-[minmax(0,1.2fr)_minmax(300px,0.8fr)] gap-4 animate-fade-in-up">
        <ActionPanel
          actions={todayActions}
          summary={marketSummary}
        />
        <FocusPanel
          focusQueue={focusQueue.slice(0, 3)}
          actionableCount={actionable.length}
          watchCount={watch.length}
          observeCount={observe.length}
        />
      </div>

      <div className="grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_320px] gap-4 animate-fade-in-up">
        <MarketSnapshot
          marketTemperature={marketTemperature ?? data?.market_temperature ?? null}
          indices={indices}
          sectors={sectors}
          summary={marketSummary}
        />
        <AdminPanel
          isAdmin={user?.role === "admin"}
          opsStatus={opsStatus}
          strategyProfile={data?.strategy_profile ?? null}
          refreshing={refreshing}
          opsRunning={opsRunning}
          onRefresh={handleRefresh}
          onAction={handleAdminAction}
        />
      </div>
    </div>
  );
}

function DecisionHero({
  board,
  summary,
  actionableCount,
  watchCount,
  observeCount,
}: {
  board: StrategyBoard | null;
  summary: ReturnType<typeof buildMarketSummary>;
  actionableCount: number;
  watchCount: number;
  observeCount: number;
}) {
  const isRealtime = board?.data_mode === "realtime_today";

  return (
    <div className="glass-card-static animate-fade-in-up overflow-hidden p-4 md:p-5">
      <div className="grid grid-cols-1 lg:grid-cols-[minmax(0,1fr)_340px] gap-4">
        <div className="min-w-0">
          <div className="flex flex-wrap items-center gap-2">
            <span className="text-[10px] font-semibold uppercase tracking-[0.2em] text-amber-400">今日结论</span>
            {isRealtime ? (
              <span className="rounded-full bg-emerald-500/10 px-2 py-0.5 text-[10px] text-emerald-400">实时</span>
            ) : null}
          </div>
          <h2 className="mt-2 text-xl font-bold tracking-tight text-text-primary">{summary.headline}</h2>
          <p className="mt-2 max-w-3xl text-sm leading-6 text-text-secondary line-clamp-2">
            {summary.detail}
          </p>

          <div className="mt-4 grid grid-cols-1 md:grid-cols-2 gap-3">
            <DecisionList title="动作" items={summary.canDo} tone="positive" />
            <DecisionList title="边界" items={summary.cannotDo} tone="risk" />
          </div>
        </div>

        <div className="grid grid-cols-3 gap-2 self-start">
          <HeroMetric label="可操作" value={actionableCount} tone="text-red-400" />
          <HeroMetric label="关注" value={watchCount} tone="text-amber-400" />
          <HeroMetric label="观察" value={observeCount} tone="text-text-secondary" />
          <HeroFact label="打法" value={summary.modeLabel} />
          <HeroFact label="仓位" value={summary.positionLabel} />
          <HeroFact label="风险" value={board?.risk_level ?? "等待更新"} />
        </div>
      </div>
    </div>
  );
}

function ActionPanel({
  actions,
  summary,
}: {
  actions: ReturnType<typeof buildActionGuides>;
  summary: ReturnType<typeof buildMarketSummary>;
}) {
  return (
    <div className="glass-card-static p-4 md:p-5">
      <div className="flex flex-wrap items-center gap-2">
        <CompactBadge label="策略" value={summary.modeLabel} />
        <CompactBadge label="仓位" value={summary.positionLabel} />
        <CompactBadge label="风险" value={summary.riskLabel} />
      </div>

      <div className="mt-4 grid grid-cols-1 md:grid-cols-3 gap-3">
        <ActionBucket title="现在做" items={actions.priority} tone="priority" />
        <ActionBucket title="盯住" items={actions.watch} tone="watch" />
        <ActionBucket title="不要做" items={actions.avoid} tone="avoid" />
      </div>
    </div>
  );
}

function FocusPanel({
  focusQueue,
  actionableCount,
  watchCount,
  observeCount,
}: {
  focusQueue: RecommendationData[];
  actionableCount: number;
  watchCount: number;
  observeCount: number;
}) {
  return (
    <div className="glass-card-static p-4 md:p-5">
      <div className="flex items-start justify-between gap-3">
        <div>
          <h3 className="text-sm font-semibold text-text-primary">焦点标的</h3>
        </div>
        <div className="grid grid-cols-3 gap-2 text-center">
          <MiniCount label="可操作" value={actionableCount} tone="text-red-400" />
          <MiniCount label="关注" value={watchCount} tone="text-amber-400" />
          <MiniCount label="观察" value={observeCount} tone="text-text-secondary" />
        </div>
      </div>

      <div className="mt-4 space-y-3">
        {focusQueue.length ? (
          focusQueue.map((rec) => <FocusStockCard key={rec.ts_code} rec={rec} />)
        ) : (
          <div className="rounded-2xl border border-border-subtle bg-surface-1/70 p-6 text-center text-sm text-text-muted">
            暂无焦点标的。
          </div>
        )}
      </div>
    </div>
  );
}

function MarketSnapshot({
  marketTemperature,
  indices,
  sectors,
  summary,
}: {
  marketTemperature: MarketTemperatureData | null;
  indices: IndexOverview[];
  sectors: SectorData[];
  summary: ReturnType<typeof buildMarketSummary>;
}) {
  const leadingSectors = sectors.slice(0, 4);
  const majorIndices = indices.slice(0, 3);

  return (
    <div className="glass-card-static p-4 md:p-5">
      <div className="flex items-center justify-between gap-3">
        <div>
          <h3 className="text-sm font-semibold text-text-primary">市场证据</h3>
        </div>
        <span className="rounded-xl bg-surface-1/70 px-3 py-1.5 text-xs font-mono tabular-nums text-text-secondary">
          温度 {Math.round(marketTemperature?.temperature ?? 0)}
        </span>
      </div>

      <div className="mt-4 grid grid-cols-2 md:grid-cols-4 gap-2">
        <EvidenceStat label="上涨" value={marketTemperature?.up_count ?? 0} tone="text-red-400" />
        <EvidenceStat label="下跌" value={marketTemperature?.down_count ?? 0} tone="text-emerald-400" />
        <EvidenceStat label="涨停" value={marketTemperature?.limit_up_count ?? 0} tone="text-amber-400" />
        <EvidenceStat label="跌停" value={marketTemperature?.limit_down_count ?? 0} tone="text-cyan-400" />
      </div>

      {majorIndices.length ? (
        <div className="mt-4 grid grid-cols-1 md:grid-cols-3 gap-2">
          {majorIndices.map((item) => (
            <div key={item.code} className="rounded-2xl border border-border-subtle bg-surface-1/70 px-3 py-3">
              <div className="text-xs font-semibold text-text-primary">{item.name}</div>
              <div className="mt-1 flex items-center justify-between gap-3">
                <span className="text-xs font-mono tabular-nums text-text-secondary">{item.close.toFixed(2)}</span>
                <span className={`text-xs font-mono tabular-nums ${item.pct_chg >= 0 ? "text-red-400" : "text-emerald-400"}`}>
                  {item.pct_chg >= 0 ? "+" : ""}{item.pct_chg.toFixed(2)}%
                </span>
              </div>
            </div>
          ))}
        </div>
      ) : null}

      {leadingSectors.length ? (
        <div className="mt-4">
          <div className="flex items-center justify-between border-b border-border-subtle pb-2">
            <div className="text-[11px] font-semibold text-text-secondary">今日盯住的板块</div>
            <div className="text-[10px] text-text-muted">主线优先</div>
          </div>
          <div className="divide-y divide-border-subtle">
            {leadingSectors.map((sector) => {
              const pct = sector.realtime_pct_change ?? sector.pct_change;
              return (
                <div key={sector.sector_code} className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 py-2.5 text-sm">
                  <div className="min-w-0 overflow-hidden">
                    <div className="truncate text-[13px] font-medium text-text-primary">{sector.sector_name}</div>
                    <div className="mt-0.5 truncate text-[11px] text-text-muted">
                      {sector.limit_up_count > 0 ? `${sector.limit_up_count} 涨停` : "无涨停"} · {sector.stage || "mid"}
                    </div>
                  </div>
                  <span className={`min-w-[4.5rem] text-right font-mono text-xs tabular-nums ${pct >= 0 ? "text-red-400" : "text-emerald-400"}`}>
                    {pct >= 0 ? "+" : ""}{pct.toFixed(2)}%
                  </span>
                </div>
              );
            })}
          </div>
        </div>
      ) : null}

      <div className="mt-4 rounded-2xl border border-border-subtle bg-surface-1/70 px-3 py-3 text-sm text-text-secondary">
        结论：{summary.headline}
      </div>
    </div>
  );
}

function AdminPanel({
  isAdmin,
  opsStatus,
  strategyProfile,
  refreshing,
  opsRunning,
  onRefresh,
  onAction,
}: {
  isAdmin?: boolean;
  opsStatus: OpsStatusResponse | null;
  strategyProfile: LatestResult["strategy_profile"];
  refreshing: boolean;
  opsRunning: string | null;
  onRefresh: () => void;
  onAction: (action: "update_tracking") => void;
}) {
  if (!isAdmin || !opsStatus) return null;

  return (
    <div className="glass-card-static p-4">
      <div className="flex items-center justify-between gap-3">
        <div>
          <h3 className="text-sm font-semibold text-text-primary">管理员</h3>
          <p className="mt-1 text-xs text-text-muted">{opsStatus.data_freshness.message}</p>
        </div>
        <span className="rounded-full bg-surface-2 px-2 py-0.5 text-[10px] text-text-muted">
          {opsStatus.scan_running ? "扫描中" : "空闲"}
        </span>
      </div>

      <div className="mt-3 grid grid-cols-2 gap-2 text-[11px] text-text-muted">
        <FreshnessPill label="市场" value={opsStatus.data_freshness.market_trade_date || "暂无"} />
        <FreshnessPill label="板块" value={opsStatus.data_freshness.sector_trade_date || "暂无"} />
        <FreshnessPill label="跟踪" value={opsStatus.data_freshness.tracking_trade_date || "暂无"} />
        <FreshnessPill label="推荐" value={opsStatus.data_freshness.last_recommendation_created_at || "暂无"} />
      </div>

      {strategyProfile?.feedback_applied ? (
        <div className="mt-3 rounded-2xl border border-cyan-500/15 bg-cyan-500/[0.05] p-3">
          <div className="text-[10px] uppercase tracking-wider text-cyan-400 font-semibold">反馈回写已生效</div>
          <div className="mt-2 space-y-1.5">
            {(strategyProfile.feedback_notes?.length ? strategyProfile.feedback_notes : strategyProfile.notes || []).slice(0, 3).map((note, index) => (
              <div key={`${note}-${index}`} className="text-xs leading-5 text-cyan-400/85">
                {note}
              </div>
            ))}
          </div>
        </div>
      ) : null}

      <div className="mt-3 flex flex-wrap gap-2">
        <button
          onClick={onRefresh}
          disabled={refreshing || !!opsRunning}
          className="rounded-xl border border-amber-500/15 bg-amber-500/10 px-3 py-2 text-xs text-amber-400 disabled:opacity-40"
        >
          {refreshing ? "扫描中..." : "立即扫描"}
        </button>
        <button
          onClick={() => onAction("update_tracking")}
          disabled={refreshing || !!opsRunning}
          className="rounded-xl border border-border-subtle bg-surface-1 px-3 py-2 text-xs text-text-secondary disabled:opacity-40"
        >
          {opsRunning === "update_tracking" ? "更新中..." : "更新跟踪"}
        </button>
      </div>
    </div>
  );
}

function DecisionList({
  title,
  items,
  tone,
}: {
  title: string;
  items: string[];
  tone: "positive" | "risk";
}) {
  const dotClass = tone === "positive" ? "bg-emerald-400" : "bg-amber-400";

  return (
    <div className="rounded-2xl border border-border-subtle bg-surface-1/70 p-3">
      <div className="text-[11px] font-semibold text-text-secondary">{title}</div>
      <div className="mt-2 space-y-2">
        {items.map((item, index) => (
          <div key={`${title}-${index}`} className="flex items-start gap-2 text-sm text-text-secondary">
            <span className={`mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full ${dotClass}`} />
            <span>{item}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function HeroMetric({ label, value, tone }: { label: string; value: number; tone: string }) {
  return (
    <div className="rounded-2xl border border-border-subtle bg-surface-1/70 px-3 py-3 text-center">
      <div className="text-[10px] uppercase tracking-wider text-text-muted">{label}</div>
      <div className={`mt-1 text-xl font-bold font-mono tabular-nums ${tone}`}>{value}</div>
    </div>
  );
}

function HeroFact({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-2xl border border-border-subtle bg-surface-1/70 px-3 py-3">
      <div className="text-[10px] uppercase tracking-wider text-text-muted">{label}</div>
      <div className="mt-1 text-xs font-semibold leading-5 text-text-secondary line-clamp-2">{value}</div>
    </div>
  );
}

function CompactBadge({ label, value }: { label: string; value: string }) {
  return (
    <span className="inline-flex items-center gap-2 rounded-xl border border-border-subtle bg-surface-1/70 px-3 py-1.5 text-xs text-text-secondary">
      <span className="text-text-muted">{label}</span>
      <span className="font-medium text-text-primary">{value}</span>
    </span>
  );
}

function ActionBucket({
  title,
  items,
  tone,
}: {
  title: string;
  items: string[];
  tone: "priority" | "watch" | "avoid";
}) {
  const toneClass =
    tone === "priority"
      ? "bg-red-500/8 text-red-400"
      : tone === "watch"
        ? "bg-amber-500/8 text-amber-400"
        : "bg-surface-2 text-text-muted";

  return (
    <div className="rounded-2xl border border-border-subtle bg-surface-1/70 p-3">
      <div className={`inline-flex rounded-lg px-2 py-1 text-[10px] font-semibold uppercase tracking-wider ${toneClass}`}>
        {title}
      </div>
      <div className="mt-3 space-y-2">
        {items.map((item, index) => (
          <div key={`${title}-${index}`} className="rounded-xl bg-surface-2/70 px-3 py-2 text-sm leading-6 text-text-secondary">
            {item}
          </div>
        ))}
      </div>
    </div>
  );
}

function FocusStockCard({ rec }: { rec: RecommendationData }) {
  const badgeClass =
    rec.action_plan === "可操作"
      ? "border-red-500/20 bg-red-500/10 text-red-400"
      : rec.action_plan === "重点关注"
        ? "border-amber-500/20 bg-amber-500/10 text-amber-400"
        : "border-border-subtle bg-surface-2 text-text-muted";

  return (
    <a
      href={`/stock/${rec.ts_code}`}
      className="rounded-2xl border border-border-subtle bg-surface-1/70 p-4 transition-colors hover:border-amber-500/20"
    >
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-text-primary">{rec.name}</span>
            <span className={`rounded-lg border px-2 py-0.5 text-[10px] ${badgeClass}`}>{rec.action_plan ?? "观察"}</span>
          </div>
          <div className="mt-1 flex min-w-0 flex-wrap items-center gap-1.5 text-[11px] text-text-muted">
            <span className="font-mono tabular-nums">{rec.ts_code}</span>
            {rec.sector ? (
              <span className="inline-flex max-w-[10rem] items-center rounded-md border border-border-subtle bg-surface-2/70 px-1.5 py-0.5 font-medium text-text-secondary">
                <span className="truncate">{rec.sector}</span>
              </span>
            ) : null}
          </div>
        </div>
        <div className="text-right">
          <div className="text-xs font-mono tabular-nums text-text-secondary">{Math.round(rec.score)}</div>
          <div className="mt-0.5 text-[10px] text-text-muted">参考分</div>
        </div>
      </div>

      <div className="mt-3 text-sm leading-6 text-text-secondary">
        {rec.trigger_condition ?? rec.entry_timing ?? rec.prefilter_reason ?? rec.reasons?.[0] ?? "等待新的触发条件。"}
      </div>

      {(rec.invalidation_condition || rec.risk_note) ? (
        <div className="mt-3 rounded-xl bg-surface-2/70 px-3 py-2 text-[11px] leading-5 text-text-muted">
          风险: {rec.invalidation_condition ?? rec.risk_note}
        </div>
      ) : null}
    </a>
  );
}

function EvidenceStat({ label, value, tone }: { label: string; value: number; tone: string }) {
  return (
    <div className="rounded-2xl border border-border-subtle bg-surface-1/70 px-3 py-3">
      <div className="text-[10px] uppercase tracking-wider text-text-muted">{label}</div>
      <div className={`mt-1 text-lg font-bold font-mono tabular-nums ${tone}`}>{value}</div>
    </div>
  );
}

function MiniCount({ label, value, tone }: { label: string; value: number; tone: string }) {
  return (
    <div className="rounded-xl bg-surface-2/70 px-2 py-2">
      <div className="text-[10px] text-text-muted">{label}</div>
      <div className={`mt-1 text-sm font-semibold font-mono tabular-nums ${tone}`}>{value}</div>
    </div>
  );
}

function FreshnessPill({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-xl bg-surface-1/70 px-2.5 py-2">
      <div className="text-[10px] text-text-muted">{label}</div>
      <div className="mt-1 truncate text-[11px] text-text-secondary">{value}</div>
    </div>
  );
}

function buildMarketSummary(
  strategyProfile: LatestResult["strategy_profile"],
  marketTemperature: MarketTemperatureData | null,
  board: StrategyBoard | null,
  scanStatus: ScanStatus | null,
  actionableCount: number,
  watchCount: number
) {
  const temp = marketTemperature?.temperature ?? board?.metrics.temperature ?? 0;
  const allowTrading = strategyProfile?.allow_trading ?? actionableCount > 0;
  const headline = !allowTrading
    ? "防守观察"
    : board?.market_regime ??
      (temp >= 70 ? "主线进攻" :
        temp >= 50 ? "确认机会" :
        temp >= 30 ? "轻仓试错" :
        "偏弱观察");

  const detail =
    strategyProfile?.decision_note ??
    board?.summary ??
    (scanStatus?.is_trading
      ? "盘中实时：看节奏、仓位、主线强弱。"
      : "收盘快照：复盘主线，准备下一交易日。");

  const canDo = [
    !allowTrading
      ? "观察等待。"
      : actionableCount > 0
        ? `只看 ${actionableCount} 只可操作标的。`
        : "无可操作标的，保留观察。",
    watchCount > 0 ? `关注 ${watchCount} 只确认信号。` : "盯最强板块前排。",
    board?.position_suggestion ?? (strategyProfile?.max_position_pct ? `仓位上限 ${strategyProfile.max_position_pct}%。` : temp >= 50 ? "围绕主线试错。" : "控制仓位。"),
  ];

  const cannotDo = [
    board?.avoid_rules?.[0] ?? "不要追后排、跟风和没有板块支撑的个股。",
    board?.avoid_rules?.[1] ?? (temp < 50 ? "不把个别异动当回暖。" : "不把盘中脉冲当主线。"),
    !allowTrading || temp < 40 ? "不逆势加仓。" : "不随意切换题材。",
  ];

  return {
    headline,
    detail,
    canDo,
    cannotDo,
    modeLabel: strategyProfile?.market_stance || board?.recommended_mode || "等待更新",
    positionLabel: board?.position_suggestion || (strategyProfile?.max_position_pct ? `${strategyProfile.max_position_pct}% 以内` : "等待更新"),
    riskLabel: board?.risk_level || "等待更新",
  };
}

function clampPercent(value: number) {
  if (!Number.isFinite(value)) return 0;
  return Math.max(0, Math.min(100, value));
}

function buildActionGuides(
  strategyProfile: LatestResult["strategy_profile"],
  board: StrategyBoard | null,
  marketTemperature: MarketTemperatureData | null,
  actionable: RecommendationData[],
  watch: RecommendationData[],
  observe: RecommendationData[],
  sectors: SectorData[]
) {
  const topSectors = (board?.watch_sectors?.slice(0, 2) ?? []).map((sector) => sector.sector_name);
  const backupSectors = sectors.slice(0, 2).map((sector) => sector.sector_name);
  const focusSectors = topSectors.length ? topSectors : backupSectors;
  const temp = marketTemperature?.temperature ?? board?.metrics.temperature ?? 0;
  const allowTrading = strategyProfile?.allow_trading ?? actionable.length > 0;

  const priority = [
    !allowTrading
      ? "等待清晰主线和承接。"
      : actionable[0]
      ? `盯 ${actionable[0].name}${actionable[0].trigger_condition ? `：${actionable[0].trigger_condition}` : " 的确认信号"}。`
      : focusSectors[0]
        ? `看 ${focusSectors[0]} 前排强度。`
        : "等待更清晰信号。",
    focusSectors[1]
      ? `主线池：${focusSectors.slice(0, 2).join("、")}。`
      : "只围绕最强主线。",
    !allowTrading ? "保守观察。" : temp >= 50 ? "做回流确认，不做无量冲高。" : "只看最强确认。",
  ];

  const watchItems = [
    watch[0]
      ? `${watch[0].name}：量能、回流、承接。`
      : focusSectors[0]
        ? `${focusSectors[0]}：确认前不追。`
        : "观察新主线聚焦。",
    watch[1]
      ? `${watch[1].name}：观察队列。`
      : "新热点先看板块扩散。",
    observe.length > 0 ? `后台观察 ${observe.length} 只。` : "暂无弱候选。",
  ];

  const avoid = [
    board?.avoid_rules?.[0] ?? "回避没有主线归属、没有触发条件的个股。",
    board?.avoid_rules?.[1] ?? "回避后排补涨和尾盘情绪化拉升。",
    temp < 40 ? "回避逆势加仓和高频换股。" : "回避脱离计划的追涨杀跌。",
  ];

  return { priority, watch: watchItems, avoid };
}
