"use client";

import clsx from "clsx";
import { CSSProperties, useEffect, useMemo, useState } from "react";
import type { Locale } from "@/lib/i18n";
import { ProFeaturePaywall } from "@/components/dashboard/ProFeaturePaywall";
import { getFutureModalView } from "@/lib/dashboard-utils";
import { getModelView, getProbabilityView } from "@/lib/model-utils";
import { getTodayPaceView } from "@/lib/pace-utils";
import { dashboardClient } from "@/lib/dashboard-client";
import { getWeatherSummary } from "@/lib/weather-summary-utils";
import {
  normalizeObservationSourceCode,
  normalizeObservationSourceLabel,
} from "@/lib/source-labels";
import type {
  CityDetail,
  ForecastModalMode,
  LoadingState,
  MarketScan,
  ProAccessState,
} from "@/lib/dashboard-types";
import { FutureForecastForwardView } from "./FutureForecastForwardView";
import {
  FutureModelForecastPanel,
  FutureProbabilityPanel,
  FutureTemperaturePathChart,
} from "./FutureForecastModalPanels";
import { FutureForecastTodayDecisionBrief } from "./FutureForecastTodayDecisionBrief";
import { FutureForecastTodayEvidenceGrid } from "./FutureForecastTodayEvidenceGrid";
import { FutureForecastModalHeader } from "./FutureForecastModalHeader";
import {
  FutureRefreshLock,
  FutureSyncStatusStrip,
  type FutureSyncStatusItem,
} from "./FutureForecastModalStatus";
import {
  FutureAnchorStatusCard,
  FuturePaceCard,
  FuturePaceLoadingCard,
  type FuturePaceSignalItem,
} from "./FutureForecastTodayCards";
import {
  TODAY_MARKET_SCAN_AUTO_REFRESH_MS,
  clamp,
  formatBucketLabel,
  formatMarketPercent,
  getTrendMetricVisual,
  localizedList,
  localizedText,
  parseBucketBoundaries,
  parseClockMinutes,
  parseLeadingNumber,
} from "./FutureForecastModal.utils";

type FutureForecastModalControls = {
  closeFutureModal: () => void;
  forecastModalMode: ForecastModalMode | null;
  futureModalDate: string | null;
  loadingState: LoadingState;
  openFutureModal: (dateStr: string, forceRefresh?: boolean) => Promise<void>;
  openTodayModal: (forceRefresh?: boolean) => Promise<void>;
};

export function FutureForecastModalContent({
  modal,
  proAccess,
  locale,
  t,
  detail,
  dateStr,
}: {
  modal: FutureForecastModalControls;
  proAccess: ProAccessState;
  locale: Locale;
  t: (key: string, params?: Record<string, string | number>) => string;
  detail: CityDetail;
  dateStr: string;
}) {
  const isPro = proAccess.subscriptionActive;
  const isProLoading = proAccess.loading;
  const hasModalContext = true;
  const [showDeferredTodaySections, setShowDeferredTodaySections] =
    useState(false);
  const [freshMarketScan, setFreshMarketScan] = useState<MarketScan | null>(
    null,
  );

  useEffect(() => {
    if (!hasModalContext) {
      setShowDeferredTodaySections(false);
      return;
    }
    setShowDeferredTodaySections(false);
    if (typeof window === "undefined") {
      setShowDeferredTodaySections(true);
      return;
    }

    let cancelled = false;
    let timeoutId: ReturnType<typeof setTimeout> | null = null;
    let idleId: number | null = null;
    const reveal = () => {
      if (!cancelled) {
        setShowDeferredTodaySections(true);
      }
    };

    if ("requestIdleCallback" in window) {
      idleId = window.requestIdleCallback(reveal, { timeout: 600 });
    } else {
      timeoutId = setTimeout(reveal, 120);
    }

    return () => {
      cancelled = true;
      if (idleId != null && "cancelIdleCallback" in window) {
        window.cancelIdleCallback(idleId);
      }
      if (timeoutId != null) {
        clearTimeout(timeoutId);
      }
    };
  }, [dateStr, detail, hasModalContext]);

  const isToday =
    modal.forecastModalMode === "today" ||
    (modal.forecastModalMode == null && dateStr === detail?.local_date);
  const detailDepth = detail?.detail_depth || "full";
  const isFullDetailReady = detailDepth === "full";
  const isStructureSyncing =
    modal.loadingState.futureDeep || !isFullDetailReady;
  const isAnyLayerSyncing = isStructureSyncing;
  const isTodayBlockingRefresh = isToday && isStructureSyncing;
  const activeMarketScan = freshMarketScan || detail?.market_scan || null;

  useEffect(() => {
    setFreshMarketScan(null);
    if (!hasModalContext || !isToday || !isFullDetailReady || !isPro) return;
    const cityName = String(detail?.name || detail?.display_name || "").trim();
    if (!cityName || !dateStr) return;

    let cancelled = false;
    let intervalId: ReturnType<typeof setInterval> | null = null;

    const refreshMarketScan = () => {
      dashboardClient
        .getCityMarketScan(cityName, {
          force: false,
          lite: false,
          marketSlug: detail?.market_scan?.primary_market?.slug || null,
          targetDate: dateStr,
        })
        .then((payload) => {
          if (cancelled) return;
          setFreshMarketScan(payload.market_scan || null);
        })
        .catch(() => {
          if (!cancelled) {
            setFreshMarketScan(null);
          }
        });
    };

    refreshMarketScan();
    intervalId = setInterval(() => {
      if (
        typeof document !== "undefined" &&
        document.visibilityState === "hidden"
      ) {
        return;
      }
      refreshMarketScan();
    }, TODAY_MARKET_SCAN_AUTO_REFRESH_MS);

    return () => {
      cancelled = true;
      if (intervalId != null) {
        clearInterval(intervalId);
      }
    };
  }, [
    dateStr,
    detail?.display_name,
    detail?.local_date,
    detail?.market_scan?.primary_market?.slug,
    detail?.name,
    detail?.updated_at,
    hasModalContext,
    isFullDetailReady,
    isPro,
    isToday,
  ]);

  const view = getFutureModalView(detail, dateStr, locale);
  const scorePosition = `${50 + view.front.score / 2}%`;
  const barStyle = {
    "--score-position": scorePosition,
  } as CSSProperties & { "--score-position": string };
  const weatherSummary = getWeatherSummary(detail, locale);
  const paceView = useMemo(
    () =>
      isToday && showDeferredTodaySections
        ? getTodayPaceView(detail, locale)
        : null,
    [detail, isToday, locale, showDeferredTodaySections],
  );
  const probabilityView = useMemo(
    () => getProbabilityView(detail, dateStr),
    [dateStr, detail],
  );
  const modelView = useMemo(
    () => getModelView(detail, dateStr),
    [dateStr, detail],
  );
  const probabilityEngineKey = String(probabilityView?.engine || "")
    .trim()
    .toLowerCase();
  const probabilityCalibrationMode = String(
    probabilityView?.calibrationMode || "",
  )
    .trim()
    .toLowerCase();
  const hasLgbmProbability = useMemo(
    () =>
      Object.keys(modelView?.models || {}).some((name) =>
        String(name || "")
          .toLowerCase()
          .replace(/[\s_/-]/g, "")
          .includes("lgbm"),
      ),
    [modelView],
  );
  const hasEmosProbability =
    probabilityEngineKey === "emos" ||
    probabilityCalibrationMode.includes("emos");
  const probabilityEngineLabel = hasLgbmProbability
    ? locale === "en-US"
      ? "LGBM"
      : "LGBM"
    : hasEmosProbability
      ? "EMOS"
      : locale === "en-US"
        ? "Calibrated model"
        : "校准模型";
  const probabilityTitle = hasLgbmProbability
    ? locale === "en-US"
      ? "LGBM-Calibrated Probability"
      : "LGBM 校准概率"
    : hasEmosProbability
      ? locale === "en-US"
        ? "EMOS-Calibrated Probability"
        : "EMOS 校准概率"
      : locale === "en-US"
        ? "Calibrated Model Probability"
        : "校准模型概率";
  const topProbabilityBucket = useMemo(() => {
    const buckets = Array.isArray(probabilityView?.probabilities)
      ? probabilityView.probabilities
      : [];
    return [...buckets]
      .filter((bucket) => Number.isFinite(Number(bucket?.probability)))
      .sort((a, b) => Number(b?.probability) - Number(a?.probability))[0];
  }, [probabilityView]);
  const modelSpreadView = useMemo(() => {
    const values = Object.values(modelView?.models || {})
      .map((value) => Number(value))
      .filter((value) => Number.isFinite(value));
    if (!values.length) return null;
    const min = Math.min(...values);
    const max = Math.max(...values);
    const spread = max - min;
    return {
      count: values.length,
      max,
      min,
      spread,
    };
  }, [modelView]);
  const boundaryRiskView = useMemo(() => {
    if (!showDeferredTodaySections) return null;
    if (!isToday || !paceView) return null;
    const selectedBucket = topProbabilityBucket || null;
    const bounds = parseBucketBoundaries(selectedBucket);
    if (!bounds) return null;
    const projected =
      paceView.paceAdjustedHigh ??
      (detail.deb?.prediction != null ? Number(detail.deb.prediction) : null);
    if (projected == null || !Number.isFinite(projected)) return null;

    const distances = [bounds.lower, bounds.upper]
      .filter(
        (value): value is number => value != null && Number.isFinite(value),
      )
      .map((value) => ({
        boundary: value,
        gap: Math.abs(projected - value),
      }))
      .sort((a, b) => a.gap - b.gap);
    if (!distances.length) return null;

    const nearest = distances[0];
    const tone =
      nearest.gap <= 0.4 ? "amber" : nearest.gap <= 0.8 ? "blue" : "cyan";
    const status =
      nearest.gap <= 0.4
        ? locale === "en-US"
          ? "High boundary risk"
          : "边界风险高"
        : nearest.gap <= 0.8
          ? locale === "en-US"
            ? "Watch boundary"
            : "边界需观察"
          : locale === "en-US"
            ? "Boundary buffer"
            : "边界缓冲";
    const note =
      locale === "en-US"
        ? `${projected.toFixed(1)}${detail.temp_symbol} is ${nearest.gap.toFixed(1)}${detail.temp_symbol} from the nearest boundary ${nearest.boundary.toFixed(1)}°C.`
        : `${projected.toFixed(1)}${detail.temp_symbol} 距最近边界 ${nearest.boundary.toFixed(1)}°C 还有 ${nearest.gap.toFixed(1)}${detail.temp_symbol}。`;
    return {
      label: locale === "en-US" ? "Boundary risk" : "边界风险",
      note,
      status,
      tone,
      value: `${nearest.gap.toFixed(1)}${detail.temp_symbol}`,
    };
  }, [
    detail.deb?.prediction,
    detail.temp_symbol,
    isToday,
    locale,
    paceView,
    showDeferredTodaySections,
    topProbabilityBucket,
  ]);
  const peakWindowStateView = useMemo(() => {
    if (!showDeferredTodaySections) return null;
    if (!isToday || !paceView) return null;
    const firstHour = Number(detail.peak?.first_h);
    const lastHour = Number(detail.peak?.last_h);
    if (
      !Number.isFinite(firstHour) ||
      !Number.isFinite(lastHour) ||
      firstHour < 0 ||
      lastHour < firstHour
    ) {
      return null;
    }
    const currentMinutes = parseClockMinutes(detail.local_time);
    const startMinutes = firstHour * 60;
    const endMinutes = (lastHour + 1) * 60;
    let status = locale === "en-US" ? "Awaiting peak" : "未进入峰值";
    let tone: "amber" | "blue" | "cyan" = "blue";
    if (currentMinutes != null && currentMinutes >= endMinutes) {
      status = locale === "en-US" ? "Past peak" : "已过峰值";
      tone = "cyan";
    } else if (currentMinutes != null && currentMinutes >= startMinutes) {
      status = locale === "en-US" ? "Peak window live" : "峰值窗口进行中";
      tone = "amber";
    }
    const note =
      locale === "en-US"
        ? `Primary peak window ${paceView.peakWindowText}.`
        : `核心峰值窗口 ${paceView.peakWindowText}。`;
    return {
      label: locale === "en-US" ? "Peak window" : "峰值窗口状态",
      note,
      status,
      tone,
      value: paceView.peakWindowText,
    };
  }, [
    detail.local_time,
    detail.peak?.first_h,
    detail.peak?.last_h,
    isToday,
    locale,
    paceView,
    showDeferredTodaySections,
  ]);
  const networkLeadView = useMemo(() => {
    if (!showDeferredTodaySections) return null;
    if (!isToday) return null;
    const delta = Number(detail.airport_vs_network_delta);
    const leadSignal = detail.network_lead_signal;
    if (!Number.isFinite(delta)) return null;
    const leaderLabel =
      String(leadSignal?.leader_station_label || "").trim() ||
      String(leadSignal?.leader_station_code || "").trim();
    const leaderSyncStatus = String(leadSignal?.leader_sync_status || "")
      .trim()
      .toLowerCase();
    const leaderSyncDelta = Number(
      leadSignal?.leader_time_delta_vs_anchor_minutes,
    );
    const syncNote =
      leaderSyncStatus === "near_realtime" || leaderSyncStatus === "lagged"
        ? Number.isFinite(leaderSyncDelta)
          ? locale === "en-US"
            ? ` Timing offset versus the airport anchor is about ${Math.round(leaderSyncDelta)} minutes.`
            : ` 与机场锚点存在约 ${Math.round(leaderSyncDelta)} 分钟时间差。`
          : locale === "en-US"
            ? " Nearby observations are not fully synchronized."
            : " 周边观测并非完全同步。"
        : leaderSyncStatus === "unknown"
          ? locale === "en-US"
            ? " Nearby station timing is not fully verified."
            : " 周边站观测时间尚未完全校验。"
          : "";
    const absDelta = Math.abs(delta);
    const status =
      delta <= -0.4
        ? locale === "en-US"
          ? "Airport trailing"
          : "机场落后"
        : delta >= 0.4
          ? locale === "en-US"
            ? "Airport leading"
            : "机场领先"
          : locale === "en-US"
            ? "Tracking network"
            : "与站网齐平";
    const tone = delta <= -0.4 ? "amber" : delta >= 0.4 ? "cyan" : "blue";
    const note =
      delta <= -0.4
        ? locale === "en-US"
          ? `Airport anchor is ${absDelta.toFixed(1)}${detail.temp_symbol} cooler than the nearby official network${leaderLabel ? `, led by ${leaderLabel}` : ""}.${syncNote}`
          : `机场主站当前比周边官方站网低 ${absDelta.toFixed(1)}${detail.temp_symbol}${leaderLabel ? `，领先点位是 ${leaderLabel}` : ""}。${syncNote}`
        : delta >= 0.4
          ? locale === "en-US"
            ? `Airport anchor is ${absDelta.toFixed(1)}${detail.temp_symbol} hotter than the nearby official network.${syncNote}`
            : `机场主站当前比周边官方站网高 ${absDelta.toFixed(1)}${detail.temp_symbol}。${syncNote}`
          : locale === "en-US"
            ? "Airport anchor and nearby official network are broadly aligned."
            : "机场主站与周边官方站网当前大体齐平。";
    return {
      label: locale === "en-US" ? "Airport vs network" : "机场 vs 周边站",
      note,
      status,
      tone,
      value: `${delta > 0 ? "+" : ""}${delta.toFixed(1)}${detail.temp_symbol}`,
    };
  }, [
    detail.airport_vs_network_delta,
    detail.network_lead_signal,
    detail.temp_symbol,
    isToday,
    locale,
    showDeferredTodaySections,
  ]);
  const paceSignalItems = useMemo(
    () =>
      [boundaryRiskView, peakWindowStateView, networkLeadView]
        .filter((item) => item != null)
        .map((item) => ({
          label: item.label,
          note: item.note,
          status: item.status,
          tone: item.tone,
          value: item.value,
        })) as FuturePaceSignalItem[],
    [boundaryRiskView, networkLeadView, peakWindowStateView],
  );
  const isNoaaSettlement =
    detail.current?.settlement_source === "noaa" ||
    detail.current?.settlement_source_label === "NOAA";
  const noaaStationCode = String(
    detail.current?.station_code || detail.risk?.icao || "NOAA",
  )
    .trim()
    .toUpperCase();
  const noaaStationName =
    String(detail.current?.station_name || "").trim() ||
    String(detail.risk?.airport || "").trim() ||
    noaaStationCode;
  const hottestBucketLabel = formatBucketLabel(topProbabilityBucket);
  const probabilitySummary = (() => {
    if (!topProbabilityBucket) {
      return locale === "en-US"
        ? "Probability mass is still too dispersed; avoid over-reading a single bracket."
        : "当前概率还比较分散，不要只盯单一区间。";
    }
    const bucketLabel = formatBucketLabel(topProbabilityBucket);
    const bucketProb = formatMarketPercent(topProbabilityBucket.probability);
    if (!isToday) {
      return locale === "en-US"
        ? `Target-day model probability reference puts the leading bucket at ${bucketLabel} (${bucketProb}). EMOS is reserved for intraday analysis after live anchor observations arrive.`
        : `目标日模型概率参考显示领先温度桶为 ${bucketLabel}（${bucketProb}）。EMOS 仅用于有实时锚点观测后的日内分析。`;
    }
    if (hasLgbmProbability) {
      return locale === "en-US"
        ? `LGBM-calibrated read puts the leading bucket at ${bucketLabel} (${bucketProb}). Treat this as the base case, not the final settlement.`
        : `LGBM 校准后领先温度桶为 ${bucketLabel}（${bucketProb}）。可作为基准情形，但不要直接等同于最终结算。`;
    }
    if (hasEmosProbability) {
      return locale === "en-US"
        ? `EMOS-calibrated probability puts the leading bucket at ${bucketLabel} (${bucketProb}). It is the primary calibrated probability layer, not the final settlement.`
        : `EMOS 校准概率显示领先温度桶为 ${bucketLabel}（${bucketProb}）。这是当前主概率层，但不要直接等同于最终结算。`;
    }
    return locale === "en-US"
      ? `Calibrated model probability puts the leading bucket at ${bucketLabel} (${bucketProb}). Treat this as the base case, not the final settlement.`
      : `校准模型概率显示领先温度桶为 ${bucketLabel}（${bucketProb}）。可作为基准情形，但不要直接等同于最终结算。`;
  })();
  const modelSummary = (() => {
    if (!modelSpreadView) {
      return locale === "en-US"
        ? "Model spread is unavailable right now."
        : "当前拿不到可用的模型分歧。";
    }
    const modelEntries = Object.entries(modelView?.models || {}).filter(
      ([, value]) =>
        value !== null && value !== undefined && Number.isFinite(Number(value)),
    );
    if (modelEntries.length === 1) {
      const [singleModelName, singleModelValue] = modelEntries[0];
      return locale === "en-US"
        ? `Only ${singleModelName} is available right now at ${Number(singleModelValue).toFixed(1)}${detail.temp_symbol}; multi-model spread is temporarily unavailable.`
        : `当前只收到 ${singleModelName} ${Number(singleModelValue).toFixed(1)}${detail.temp_symbol}，其他多模型暂未回传，所以这里先不判断模型分歧。`;
    }
    return locale === "en-US"
      ? `Model range runs from ${modelSpreadView.min.toFixed(1)}${detail.temp_symbol} to ${modelSpreadView.max.toFixed(1)}${detail.temp_symbol}; spread ${modelSpreadView.spread.toFixed(1)}${detail.temp_symbol}.`
      : `当前模型区间在 ${modelSpreadView.min.toFixed(1)}${detail.temp_symbol} 到 ${modelSpreadView.max.toFixed(1)}${detail.temp_symbol}，分歧 ${modelSpreadView.spread.toFixed(1)}${detail.temp_symbol}。`;
  })();
  const upperAirSignal = detail.vertical_profile_signal || {};
  const tafSignal = detail.taf?.signal || {};
  const upperAirCue = useMemo(() => {
    if (!showDeferredTodaySections) return null;
    if (!isToday || (!upperAirSignal.source && !tafSignal.available))
      return null;

    const setup = String(
      upperAirSignal.heating_setup || "neutral",
    ).toLowerCase();
    const tafSuppression = String(
      tafSignal.suppression_level || "low",
    ).toLowerCase();
    const tafDisruption = String(
      tafSignal.disruption_level || "low",
    ).toLowerCase();
    const reasons: string[] = [];
    let score = 0;

    if (setup === "supportive") {
      score += 2;
      reasons.push(
        locale === "en-US"
          ? "upper-air structure still supports daytime heating"
          : "高空结构仍偏支持白天冲高",
      );
    } else if (setup === "suppressed") {
      score -= 2;
      reasons.push(
        locale === "en-US"
          ? "upper-air structure still leans toward capping the peak"
          : "高空结构更偏向压住峰值",
      );
    }

    if (tafSuppression === "high") {
      score -= 2;
      reasons.push(
        locale === "en-US"
          ? "TAF flags meaningful cloud/rain suppression near the peak window"
          : "TAF 在峰值窗口提示云雨压温风险偏高",
      );
    } else if (tafSuppression === "medium") {
      score -= 1;
      reasons.push(
        locale === "en-US"
          ? "TAF keeps some cloud/rain suppression risk on the table"
          : "TAF 仍提示一定的云雨压温风险",
      );
    }

    if (tafDisruption === "high") {
      score -= 1;
      reasons.push(
        locale === "en-US"
          ? "TAF also suggests a noisier afternoon regime"
          : "TAF 还提示午后扰动偏强",
      );
    } else if (tafDisruption === "medium") {
      score -= 0.5;
      reasons.push(
        locale === "en-US"
          ? "TAF keeps some afternoon timing noise in play"
          : "TAF 提示午后仍可能有时段性扰动",
      );
    }

    if (score >= 1.5) {
      return {
        summary:
          locale === "en-US"
            ? "The combined upper-air and TAF read still leans warmer. Do not fade lower buckets too early."
            : "高空和 TAF 两层信号合并后仍偏暖侧，不宜过早做更低温区间。",
        note:
          locale === "en-US"
            ? `${reasons.slice(0, 2).join("; ")}.`
            : `${reasons.slice(0, 2).join("；")}。`,
        tone: "warm",
        value: locale === "en-US" ? "Lean warmer" : "偏暖侧",
      };
    }

    if (score <= -1.5) {
      return {
        summary:
          locale === "en-US"
            ? "The combined upper-air and TAF read leans more defensive. Be more careful chasing higher buckets."
            : "高空和 TAF 两层信号合并后更偏防守，追更高温区间要更谨慎。",
        note:
          locale === "en-US"
            ? `${reasons.slice(0, 2).join("; ")}.`
            : `${reasons.slice(0, 2).join("；")}。`,
        tone: "cold",
        value: locale === "en-US" ? "Lean cautious" : "偏谨慎",
      };
    }

    return {
      summary:
        locale === "en-US"
          ? "The combined upper-air and TAF read is mixed. Let surface structure decide before taking a side."
          : "高空和 TAF 两层信号目前偏混合，先看近地面结构变化，不急着站边。",
      note:
        locale === "en-US"
          ? `${reasons.slice(0, 2).join("; ") || "No clean edge from the upper-air layer alone"}.`
          : `${reasons.slice(0, 2).join("；") || "单看高空层还没有干净的交易边"}。`,
      tone: "",
      value: locale === "en-US" ? "Wait / confirm" : "先观察",
    };
  }, [
    tafSignal.available,
    tafSignal.disruption_level,
    tafSignal.suppression_level,
    isToday,
    locale,
    upperAirSignal.heating_setup,
    upperAirSignal.source,
    showDeferredTodaySections,
  ]);
  const topObservedTemp =
    detail.current?.max_so_far != null
      ? detail.current.max_so_far
      : detail.current?.temp;
  const currentTempText =
    detail.current?.temp != null
      ? `${detail.current.temp}${detail.temp_symbol}`
      : "--";
  const daylightProgress = (() => {
    const now = parseClockMinutes(detail.current?.obs_time);
    const sunrise = parseClockMinutes(detail.forecast?.sunrise);
    const sunset = parseClockMinutes(detail.forecast?.sunset);
    if (now == null || sunrise == null || sunset == null || sunset <= sunrise) {
      return null;
    }
    const percent = clamp(((now - sunrise) / (sunset - sunrise)) * 100, 0, 100);
    const phase =
      now < sunrise ? "夜间" : now > sunset ? "已日落" : "白昼进行中";
    return {
      phase,
      percent,
    };
  })();
  const displayedUpperAirSummary = showDeferredTodaySections
    ? upperAirCue?.summary || view.front.upperAirSummary
    : "";
  const displayedUpperAirMetrics = showDeferredTodaySections
    ? (view.front.upperAirMetrics || []).map((metric, index) =>
        index === 0 &&
        (metric.label === "Trade cue" || metric.label === "交易动作") &&
        upperAirCue
          ? {
              ...metric,
              note: upperAirCue.note,
              tone: upperAirCue.tone,
              value: upperAirCue.value,
            }
          : metric,
      )
    : [];
  const localizedAiCommentaryLines = useMemo(() => {
    if (!showDeferredTodaySections) return [] as string[];
    const commentary = detail.dynamic_commentary || {};
    const headline = String(
      locale === "en-US"
        ? commentary.headline_en || ""
        : commentary.headline_zh || "",
    ).trim();
    const bullets = (
      locale === "en-US" ? commentary.bullets_en : commentary.bullets_zh
    ) as string[] | null | undefined;
    const cleanedBullets = Array.isArray(bullets)
      ? bullets.map((item) => String(item || "").trim()).filter(Boolean)
      : [];
    return [headline, ...cleanedBullets].filter(Boolean).slice(0, 3);
  }, [detail.dynamic_commentary, locale, showDeferredTodaySections]);
  const todayTradeSummaryLines = useMemo(() => {
    if (!showDeferredTodaySections) return [] as string[];
    if (!isToday) return [] as string[];
    if (localizedAiCommentaryLines.length > 0) {
      return localizedAiCommentaryLines;
    }
    const lines: string[] = [];
    if (paceView) {
      const headline =
        paceView.biasTone === "warm"
          ? locale === "en-US"
            ? `Pace is running hot by ${paceView.deltaText}; the day high still leans above the base curve.`
            : `节奏偏热 ${paceView.deltaText}，日高仍偏向落在基础曲线之上。`
          : paceView.biasTone === "cold"
            ? locale === "en-US"
              ? `Pace is trailing by ${paceView.deltaText}; chasing higher buckets needs caution.`
              : `节奏落后 ${paceView.deltaText}，继续追更高温区间要更谨慎。`
            : locale === "en-US"
              ? "Pace is still on curve; the next move depends on the peak-window push."
              : "节奏目前贴着曲线走，下一步主要看峰值窗口还有没有上冲。";
      lines.push(headline);
    }
    if (boundaryRiskView) {
      lines.push(
        locale === "en-US"
          ? `${boundaryRiskView.label}: ${boundaryRiskView.note}`
          : `${boundaryRiskView.label}：${boundaryRiskView.note}`,
      );
    }
    if (networkLeadView) {
      lines.push(
        locale === "en-US"
          ? `${networkLeadView.label}: ${networkLeadView.note}`
          : `${networkLeadView.label}：${networkLeadView.note}`,
      );
    }
    return lines.slice(0, 3);
  }, [
    boundaryRiskView,
    isToday,
    locale,
    localizedAiCommentaryLines,
    networkLeadView,
    paceView,
    showDeferredTodaySections,
  ]);
  const intradayMeteorology = detail.intraday_meteorology || {};
  const meteorologySignals = Array.isArray(
    intradayMeteorology.signal_contributions,
  )
    ? intradayMeteorology.signal_contributions
    : [];
  const invalidationRules = localizedList(
    locale,
    intradayMeteorology.invalidation_rules,
    intradayMeteorology.invalidation_rules_en,
  );
  const confirmationRules = localizedList(
    locale,
    intradayMeteorology.confirmation_rules,
    intradayMeteorology.confirmation_rules_en,
  );
  const meteorologyHeadline =
    localizedText(
      locale,
      intradayMeteorology.headline,
      intradayMeteorology.headline_en,
    ) ||
    todayTradeSummaryLines[0] ||
    (locale === "en-US"
      ? "Intraday meteorology layers are still syncing; use the next observation as the anchor."
      : "关键日内气象层仍在同步，先以下一次观测作为判断锚点。");
  const baseCaseBucket =
    String(intradayMeteorology.base_case_bucket || "").trim() ||
    formatBucketLabel(topProbabilityBucket);
  const nextObservationTime =
    String(intradayMeteorology.next_observation_time || "").trim() || "--";
  const baseBucketNumber = parseLeadingNumber(baseCaseBucket);
  const referenceObservedTemp =
    topObservedTemp != null && Number.isFinite(Number(topObservedTemp))
      ? Number(topObservedTemp)
      : detail.current?.temp != null
        ? Number(detail.current.temp)
        : null;
  const gapToBaseBucket =
    baseBucketNumber != null && referenceObservedTemp != null
      ? Math.max(0, baseBucketNumber - referenceObservedTemp)
      : null;
  const pathStatus =
    gapToBaseBucket == null
      ? locale === "en-US"
        ? "Awaiting anchor"
        : "等待锚点"
      : gapToBaseBucket <= 0.05
        ? locale === "en-US"
          ? "Base path touched"
          : "基准路径已触达"
        : gapToBaseBucket <= 1.0
          ? locale === "en-US"
            ? "Base path open"
            : "基准路径开放"
          : locale === "en-US"
            ? "Needs peak push"
            : "需要峰值推动";
  const peakWindowText =
    String(intradayMeteorology.peak_window || "").trim() ||
    paceView?.peakWindowText ||
    "--";
  const settlementSourceCode = normalizeObservationSourceCode(
    detail.current?.settlement_source || "",
  );
  const settlementStationCode = String(
    detail.current?.station_code || detail.risk?.icao || "",
  )
    .trim()
    .toUpperCase();
  const settlementStationName =
    String(detail.current?.station_name || detail.risk?.airport || "").trim() ||
    settlementStationCode ||
    (locale === "en-US" ? "Anchor station" : "锚点站");
  const airportMetarAnchor =
    settlementSourceCode === "metar" ||
    Boolean(settlementStationCode && /^[A-Z]{4}$/.test(settlementStationCode));
  const anchorSourceLabel = airportMetarAnchor
    ? settlementStationCode
      ? `${settlementStationCode} METAR`
      : "METAR"
    : normalizeObservationSourceLabel(
        detail.current?.settlement_source_label ||
          detail.current?.settlement_source,
        locale === "en-US" ? "Official observation" : "官方观测",
      );
  const anchorRuleText = airportMetarAnchor
    ? locale === "en-US"
      ? `Airport contract anchor: use the ${anchorSourceLabel} reports. Third-party history pages are display-only when present.`
      : `机场合约锚点：以 ${anchorSourceLabel} 报文为准；第三方历史页只作为展示入口。`
    : locale === "en-US"
      ? `Official anchor: use ${anchorSourceLabel} observations for this contract.`
      : `官方锚点：该合约按 ${anchorSourceLabel} 观测口径判断。`;
  const nextObservationLabel = airportMetarAnchor
    ? locale === "en-US"
      ? "Next METAR watch"
      : "下一次 METAR 观察"
    : locale === "en-US"
      ? "Next anchor watch"
      : "下一次锚点观察";
  const gapToBaseText =
    gapToBaseBucket == null
      ? "--"
      : `${gapToBaseBucket.toFixed(1)}${detail.temp_symbol || "°C"}`;
  const syncStatusItems = [
    {
      key: "base",
      state: isAnyLayerSyncing ? "syncing" : "ready",
      label: isAnyLayerSyncing
        ? locale === "en-US"
          ? "Refreshing base analysis"
          : "正在刷新基础分析"
        : locale === "en-US"
          ? "Base analysis ready"
          : "基础分析已加载",
      note: isAnyLayerSyncing
        ? locale === "en-US"
          ? "Latest anchor readings and forecast curve are being rebuilt."
          : "正在重建最新锚点读数和预测曲线。"
        : locale === "en-US"
          ? "Forecast curve, anchor state, and the core intraday view are available."
          : "预测曲线、锚点状态和核心日内视图已经可用。",
    },
    {
      key: "market",
      state: isAnyLayerSyncing ? "syncing" : "ready",
      label: isAnyLayerSyncing
        ? locale === "en-US"
          ? "Refreshing probability layer"
          : "正在刷新概率层"
        : locale === "en-US"
          ? "Probability layer ready"
          : "概率层已加载",
      note: isAnyLayerSyncing
        ? locale === "en-US"
          ? "Model spread and calibrated buckets are updating."
          : "模型分歧和校准概率桶正在更新。"
        : locale === "en-US"
          ? `Probability buckets are derived from the ${probabilityEngineLabel} layer.`
          : `概率桶当前由 ${probabilityEngineLabel} 层推导。`,
    },
  ] satisfies FutureSyncStatusItem[];

  return (
    <div
      className="modal-overlay"
      role="dialog"
      aria-modal="true"
      aria-labelledby="future-modal-title"
      onClick={(event) => {
        if (event.target === event.currentTarget) {
          modal.closeFutureModal();
        }
      }}
    >
      {isProLoading ? (
        <div
          className="modal-content large"
          style={{ padding: "40px", textAlign: "center" }}
        >
          <div style={{ color: "var(--text-muted)" }}>
            {t("dashboard.loading")}
          </div>
        </div>
      ) : !isPro ? (
        <ProFeaturePaywall
          feature={isToday ? "today" : "future"}
          onClose={modal.closeFutureModal}
        />
      ) : (
        <div className="modal-content large future-modal">
          <FutureForecastModalHeader
            cityDisplayName={detail.display_name}
            dateStr={dateStr}
            isAnyLayerSyncing={isAnyLayerSyncing}
            isPro={isPro}
            isProLoading={isProLoading}
            isToday={isToday}
            locale={locale}
            onClose={modal.closeFutureModal}
            onRefresh={() => {
              if (isToday) {
                void modal.openTodayModal(true);
                return;
              }
              modal.openFutureModal(dateStr, true);
            }}
            t={t}
          />
          <div
            className={clsx(
              "modal-body future-modal-body",
              isTodayBlockingRefresh && "future-modal-body-refreshing",
            )}
          >
            {isTodayBlockingRefresh && <FutureRefreshLock locale={locale} />}
            {isToday && (
              <FutureForecastTodayDecisionBrief
                anchorRuleText={anchorRuleText}
                anchorSourceLabel={anchorSourceLabel}
                baseCaseBucket={baseCaseBucket}
                confidence={intradayMeteorology.confidence}
                displayName={detail.display_name}
                downsideBucket={intradayMeteorology.downside_bucket}
                gapToBaseText={gapToBaseText}
                locale={locale}
                meteorologyHeadline={meteorologyHeadline}
                nextObservationLabel={nextObservationLabel}
                nextObservationTime={nextObservationTime}
                pathStatus={pathStatus}
                settlementStationName={settlementStationName}
                upsideBucket={intradayMeteorology.upside_bucket}
              />
            )}
            <FutureSyncStatusStrip items={syncStatusItems} compact={isToday} />
            {isNoaaSettlement && (
              <div className="modal-callout modal-callout-info">
                {locale === "en-US"
                  ? `${detail.display_name} now settles against NOAA ${noaaStationCode} (${noaaStationName}). The market uses the highest rounded whole-degree Celsius reading in the Temp column after the day is finalized.`
                  : `${detail.display_name} 当前按 NOAA ${noaaStationCode}（${noaaStationName}）结算。市场最终采用该日 Temp 列完成质控后的最高整度摄氏值，不按小数温度结算。`}
              </div>
            )}
            {isToday ? (
              <div className="future-v2-layout">
                <aside className="future-v2-left">
                  <FutureAnchorStatusCard
                    locale={locale}
                    currentTempText={currentTempText}
                    weatherSummary={weatherSummary}
                    obsTime={detail.current?.obs_time}
                    daylightProgress={daylightProgress}
                    sunrise={detail.forecast?.sunrise}
                    sunset={detail.forecast?.sunset}
                    topObservedTemp={topObservedTemp}
                    tempSymbol={detail.temp_symbol}
                    gapToBaseBucket={gapToBaseBucket}
                    pathStatus={pathStatus}
                  />

                  {showDeferredTodaySections && paceView ? (
                    <FuturePaceCard
                      locale={locale}
                      paceView={paceView}
                      tempSymbol={detail.temp_symbol}
                      signalItems={paceSignalItems}
                    />
                  ) : isToday ? (
                    <FuturePaceLoadingCard locale={locale} />
                  ) : null}
                </aside>

                <main className="future-v2-right">
                  <section className="future-modal-section future-v2-main-chart">
                    <div className="modal-section-heading">
                      <div className="modal-section-kicker">
                        {locale === "en-US" ? "Primary view" : "主视图"}
                      </div>
                      <h3>
                        {locale === "en-US"
                          ? "Today's temperature path (anchor obs + models)"
                          : "今日气温路径（锚点观测 + 模型）"}
                      </h3>
                    </div>
                    <FutureTemperaturePathChart dateStr={dateStr} forceToday={isToday} />
                    <div className="future-v2-chart-thresholds">
                      <span>
                        {locale === "en-US" ? "Base" : "基准"} ·{" "}
                        {baseCaseBucket || "--"}
                      </span>
                      <span>
                        {locale === "en-US" ? "Upside" : "上修"} ·{" "}
                        {intradayMeteorology.upside_bucket || "--"}
                      </span>
                      <span>
                        {locale === "en-US" ? "Invalidates at" : "失效观察"} ·{" "}
                        {nextObservationTime}
                      </span>
                    </div>
                  </section>

                  <FutureForecastTodayEvidenceGrid
                    airportMetarAnchor={airportMetarAnchor}
                    confirmationRules={confirmationRules}
                    invalidationRules={invalidationRules}
                    locale={locale}
                    meteorologySignals={meteorologySignals}
                    modelSummary={modelSummary}
                  />

                  <div className="future-modal-grid">
                    <section className="future-modal-section">
                      <div className="modal-section-heading">
                        <div className="modal-section-kicker">
                          {locale === "en-US" ? "Probability read" : "概率判断"}
                        </div>
                        <h3>{probabilityTitle}</h3>
                      </div>
                      <div
                        className="future-text-block"
                        style={{ marginBottom: "12px" }}
                      >
                        {probabilitySummary}
                      </div>
                      <div style={{ position: "relative", minHeight: "120px" }}>
                        <FutureProbabilityPanel
                          detail={detail}
                          targetDate={dateStr}
                          marketScan={activeMarketScan}
                          hideTitle
                        />
                      </div>
                    </section>
                    <section className="future-modal-section">
                      <div className="modal-section-heading">
                        <div className="modal-section-kicker">
                          {locale === "en-US" ? "Model layer" : "模型层"}
                        </div>
                        <h3>
                          {locale === "en-US"
                            ? "Model Range & Spread"
                            : "模型区间与分歧"}
                        </h3>
                      </div>
                      <div
                        className="future-text-block"
                        style={{ marginBottom: "12px" }}
                      >
                        {modelSummary}
                      </div>
                      <FutureModelForecastPanel
                        detail={detail}
                        targetDate={dateStr}
                        hideTitle
                      />
                    </section>
                  </div>
                </main>
              </div>
            ) : (
              <FutureForecastForwardView
                dateStr={dateStr}
                detail={detail}
                t={t}
                view={view}
              />
            )}
          </div>
        </div>
      )}
    </div>
  );
}

