"use client";

import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  dashboardClient,
  getCityRevision,
  toCitySummary,
} from "@/lib/dashboard-client";
import { markAnalyticsOnce, trackAppEvent } from "@/lib/app-analytics";
import {
  CityDetail,
  CityListItem,
  CitySummary,
  DashboardState,
  HistoryPoint,
  HistoryState,
  LoadingState,
  MarketScan,
  ProAccessState,
} from "@/lib/dashboard-types";

interface DashboardStoreValue extends DashboardState {
  closeFutureModal: () => void;
  closeHistory: () => void;
  closePanel: () => void;
  ensureCityDetail: (
    cityName: string,
    force?: boolean,
    depth?: "panel" | "full",
  ) => Promise<CityDetail>;
  futureModalDate: string | null;
  loadCities: () => Promise<void>;
  openFutureModal: (dateStr: string, forceRefresh?: boolean) => Promise<void>;
  openHistory: () => Promise<void>;
  openTodayModal: (forceRefresh?: boolean) => Promise<void>;
  registerMapStopMotion: (stopMotion: () => void) => void;
  refreshAll: () => Promise<void>;
  refreshProAccess: () => Promise<void>;
  refreshSelectedCity: () => Promise<void>;
  selectedMarketScan: MarketScan | null;
  selectedDetail: CityDetail | null;
  selectCity: (cityName: string) => Promise<void>;
  setMapInteractionActive: (active: boolean) => void;
  setForecastDate: (dateStr: string | null) => void;
  marketScanByCityName: Record<string, MarketScan>;
}

const DashboardStoreContext = createContext<DashboardStoreValue | null>(null);

function getInitialLoadingState(): LoadingState {
  return {
    cities: false,
    cityDetail: false,
    history: false,
    refresh: false,
    marketScan: false,
  };
}

function getInitialHistoryState(): HistoryState {
  return {
    dataByCity: {},
    error: null,
    isOpen: false,
    loading: false,
  };
}

function getInitialProAccessState(): ProAccessState {
  return {
    loading: true,
    authenticated: false,
    userId: null,
    subscriptionActive: false,
    subscriptionPlanCode: null,
    subscriptionExpiresAt: null,
    points: 0,
    error: null,
  };
}

function getMarketScanCacheKey(cityName: string, targetDate?: string | null) {
  const normalizedDate = String(targetDate || "").trim() || "local";
  return `${cityName}::${normalizedDate}`;
}

const SELECTED_CITY_STORAGE_KEY = "polyWeather_selected_city_v1";
const BACKGROUND_SUMMARY_REFRESH_MS = 30_000;
const EAGER_CITY_SUMMARIES_DISABLED =
  process.env.NEXT_PUBLIC_POLYWEATHER_DISABLE_EAGER_SUMMARIES === "true";
const EAGER_SUMMARY_PRIORITY_CONCURRENCY = 3;
const EAGER_SUMMARY_BACKGROUND_CONCURRENCY = 2;
const EAGER_SUMMARY_BACKGROUND_IDLE_TIMEOUT_MS = 1200;
const EAGER_SUMMARY_PRIORITY_CITY_ORDER = [
  "hong kong",
  "taipei",
  "shanghai",
  "beijing",
  "wuhan",
  "shenzhen",
  "chengdu",
  "chongqing",
  "seoul",
  "busan",
  "tokyo",
  "singapore",
  "kuala lumpur",
  "jakarta",
  "tel aviv",
  "jeddah",
  "istanbul",
  "moscow",
  "helsinki",
  "warsaw",
  "amsterdam",
  "london",
  "paris",
  "milan",
  "madrid",
] as const;
type CityDetailDepth = "panel" | "full";

function countAvailableModels(
  detail?: CityDetail | null,
  targetDate?: string | null,
): number {
  if (!detail) return 0;
  const date = String(targetDate || detail.local_date || "").trim();
  const dailyModels = detail.multi_model_daily?.[date]?.models;
  const models = dailyModels && typeof dailyModels === "object"
    ? dailyModels
    : detail.multi_model || {};
  return Object.values(models).filter((value) =>
    Number.isFinite(Number(value)),
  ).length;
}

function countForecastDays(detail?: CityDetail | null): number {
  const daily = detail?.forecast?.daily;
  return Array.isArray(daily) ? daily.length : 0;
}

function hasSparseModelCoverage(
  detail?: CityDetail | null,
  targetDate?: string | null,
): boolean {
  return countAvailableModels(detail, targetDate) <= 1;
}

function hasSparseDetailCoverage(
  detail?: CityDetail | null,
  targetDate?: string | null,
): boolean {
  if (!detail) return true;
  return (
    hasSparseModelCoverage(detail, targetDate) || countForecastDays(detail) <= 1
  );
}

function normalizeDetailDepth(detail?: CityDetail | null): CityDetailDepth {
  return detail?.detail_depth === "panel" ? "panel" : "full";
}

function detailSatisfiesDepth(
  detail: CityDetail | null | undefined,
  depth: CityDetailDepth,
) {
  if (!detail) return false;
  if (depth === "panel") return true;
  return normalizeDetailDepth(detail) === "full";
}

function getStoredSelectedCityName(cities: CityListItem[]) {
  if (typeof window === "undefined") return null;
  const stored = String(
    window.localStorage.getItem(SELECTED_CITY_STORAGE_KEY) || "",
  )
    .trim()
    .toLowerCase();
  if (!stored) return null;
  if (!cities.some((city) => city.name === stored)) return null;
  return stored;
}

function getPriorityPreloadCities(cities: CityListItem[]) {
  const citySet = new Set(cities.map((city) => city.name));
  return EAGER_SUMMARY_PRIORITY_CITY_ORDER.filter((cityName) =>
    citySet.has(cityName)
  );
}

function scheduleWhenBrowserIdle(callback: () => void) {
  if (typeof window === "undefined") {
    callback();
    return () => {};
  }

  const browserWindow = window as Window & {
    requestIdleCallback?: (
      cb: IdleRequestCallback,
      options?: IdleRequestOptions,
    ) => number;
    cancelIdleCallback?: (handle: number) => void;
  };

  if (typeof browserWindow.requestIdleCallback === "function") {
    const handle = browserWindow.requestIdleCallback(
      () => callback(),
      { timeout: EAGER_SUMMARY_BACKGROUND_IDLE_TIMEOUT_MS },
    );
    return () => {
      if (typeof browserWindow.cancelIdleCallback === "function") {
        browserWindow.cancelIdleCallback(handle);
      }
    };
  }

  const timeoutId = window.setTimeout(callback, 400);
  return () => {
    window.clearTimeout(timeoutId);
  };
}

export function DashboardStoreProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const initialCacheRef = useRef<ReturnType<
    typeof dashboardClient.readCityDetailCacheBundle
  > | null>(null);
  const [cities, setCities] = useState<CityListItem[]>([]);
  const [cityDetailsByName, setCityDetailsByName] = useState<
    Record<string, CityDetail>
  >({});
  const [citySummariesByName, setCitySummariesByName] = useState<
    Record<string, CitySummary>
  >({});
  const [cityDetailMetaByName, setCityDetailMetaByName] = useState<
    Record<string, { cachedAt: number; revision: string }>
  >({});
  const [marketScanByCityName, setMarketScanByCityName] = useState<
    Record<string, MarketScan>
  >({});
  const [selectedCity, setSelectedCity] = useState<string | null>(null);
  const [isPanelOpen, setIsPanelOpen] = useState(false);
  const [selectedForecastDate, setSelectedForecastDate] = useState<
    string | null
  >(null);
  const [futureModalDate, setFutureModalDate] = useState<string | null>(null);
  const [isMapInteracting, setIsMapInteracting] = useState(false);
  const [loadingState, setLoadingState] = useState<LoadingState>(
    getInitialLoadingState,
  );
  const [historyState, setHistoryState] = useState<HistoryState>(
    getInitialHistoryState,
  );
  const [proAccess, setProAccess] = useState<ProAccessState>(
    getInitialProAccessState,
  );
  const proAccessRef = useRef<ProAccessState>(getInitialProAccessState());

  const mapStopMotionRef = useRef<() => void>(() => {});
  const hydratedSelectionRef = useRef(false);
  const hydratedProCacheRef = useRef(false);
  const backgroundSummaryCheckAtRef = useRef<Record<string, number>>({});
  const summaryInflightByCityRef = useRef<Record<string, Promise<CitySummary>>>(
    {},
  );
  const citySummariesRef = useRef<Record<string, CitySummary>>({});
  const selectedCityRef = useRef<string | null>(null);
  const selectedDetail =
    selectedCity && proAccess.subscriptionActive
      ? cityDetailsByName[selectedCity] || null
      : null;
  const selectedMarketDate =
    futureModalDate ||
    selectedForecastDate ||
    selectedDetail?.local_date ||
    null;
  const selectedMarketScanKey = selectedCity
    ? getMarketScanCacheKey(selectedCity, selectedMarketDate)
    : null;
  const selectedMarketScan =
    selectedCity && proAccess.subscriptionActive
      ? marketScanByCityName[selectedMarketScanKey || ""] || null
      : null;

  useEffect(() => {
    if (proAccess.loading) return;
    if (!proAccess.authenticated || !proAccess.subscriptionActive) {
      dashboardClient.clearCityDetailCache();
      return;
    }
    dashboardClient.writeCityDetailCacheBundle(
      cityDetailsByName,
      cityDetailMetaByName,
    );
  }, [
    cityDetailMetaByName,
    cityDetailsByName,
    proAccess.authenticated,
    proAccess.loading,
    proAccess.subscriptionActive,
  ]);

  useEffect(() => {
    citySummariesRef.current = citySummariesByName;
  }, [citySummariesByName]);

  useEffect(() => {
    selectedCityRef.current = selectedCity;
  }, [selectedCity]);

  useEffect(() => {
    proAccessRef.current = proAccess;
  }, [proAccess]);

  useEffect(() => {
    if (proAccess.loading) return;
    if (!proAccess.authenticated || !proAccess.subscriptionActive) {
      hydratedProCacheRef.current = false;
      initialCacheRef.current = null;
      return;
    }
    if (hydratedProCacheRef.current) return;

    hydratedProCacheRef.current = true;
    const cached =
      initialCacheRef.current || dashboardClient.readCityDetailCacheBundle();
    initialCacheRef.current = cached;
    if (!Object.keys(cached.details).length) return;

    setCityDetailsByName(cached.details);
    setCityDetailMetaByName(cached.meta);
    setCitySummariesByName((current) => ({
      ...Object.fromEntries(
        Object.entries(cached.details).map(([cityName, detail]) => [
          cityName,
          toCitySummary(detail),
        ]),
      ),
      ...current,
    }));
  }, [proAccess.authenticated, proAccess.loading, proAccess.subscriptionActive]);

  useEffect(() => {
    if (proAccess.loading) return;
    if (proAccess.authenticated && proAccess.subscriptionActive) return;
    dashboardClient.clearCityDetailCache();
    setCityDetailsByName({});
    setCityDetailMetaByName({});
    setMarketScanByCityName({});
  }, [proAccess]);

  const scheduleBackgroundDetailRefresh = (
    cityName: string,
    cached: CityDetail,
    cachedMeta?: { cachedAt: number; revision: string },
  ) => {
    const nowTs = Date.now();
    const lastTs = backgroundSummaryCheckAtRef.current[cityName] || 0;
    if (nowTs - lastTs < BACKGROUND_SUMMARY_REFRESH_MS) {
      return;
    }
    backgroundSummaryCheckAtRef.current[cityName] = nowTs;

    void dashboardClient
      .getCitySummary(cityName)
      .then(async (summary) => {
        const revision = getCityRevision(summary);
        if (!revision || revision === cachedMeta?.revision) {
          return;
        }

        const latestDetail = await dashboardClient.getCityDetail(cityName, {
          force: false,
          depth: normalizeDetailDepth(cached),
        });
        const detail = latestDetail;

        setCityDetailsByName((current) => ({
          ...current,
          [cityName]: detail,
        }));
        setCitySummariesByName((current) => ({
          ...current,
          [cityName]: toCitySummary(detail),
        }));
        setCityDetailMetaByName((current) => ({
          ...current,
          [cityName]: {
            cachedAt: Date.now(),
            revision: getCityRevision(detail),
          },
        }));
      })
      .catch(() => {});
  };

  const ensureCityDetail = async (
    cityName: string,
    force = false,
    depth: CityDetailDepth = "panel",
  ) => {
    const cached = cityDetailsByName[cityName];
    const cachedMeta = cityDetailMetaByName[cityName];
    const hasRequestedDepth = detailSatisfiesDepth(cached, depth);
    const cachedIsSparse = hasSparseDetailCoverage(cached, cached?.local_date);
    if (
      !force &&
      cached &&
      hasRequestedDepth &&
      !cachedIsSparse &&
      dashboardClient.isCityDetailFresh(cachedMeta)
    ) {
      scheduleBackgroundDetailRefresh(cityName, cached, cachedMeta);
      return cached;
    }

    if (!force && cached && hasRequestedDepth) {
      try {
        const summary = await dashboardClient.getCitySummary(cityName);
        const revision = getCityRevision(summary);
        if (revision && revision === cachedMeta?.revision) {
          if (cachedIsSparse) {
            const latestDetail = await dashboardClient.getCityDetail(cityName, {
              force: true,
              depth,
            });
            const detail = latestDetail;
            setCityDetailsByName((current) => ({
              ...current,
              [cityName]: detail,
            }));
            setCitySummariesByName((current) => ({
              ...current,
              [cityName]: toCitySummary(detail),
            }));
            setCityDetailMetaByName((current) => ({
              ...current,
              [cityName]: {
                cachedAt: Date.now(),
                revision: getCityRevision(detail),
              },
            }));
            return detail;
          }
          setCityDetailMetaByName((current) => ({
            ...current,
            [cityName]: {
              cachedAt: Date.now(),
              revision,
            },
          }));
          return cached;
        }
      } catch {
        return cached;
      }
    }

    const latestDetail = await dashboardClient.getCityDetail(cityName, {
      force,
      depth,
    });
    const detail = latestDetail;
    setCityDetailsByName((current) => ({
      ...current,
      [cityName]: detail,
    }));
    setCitySummariesByName((current) => ({
      ...current,
      [cityName]: toCitySummary(detail),
    }));
    setCityDetailMetaByName((current) => ({
      ...current,
      [cityName]: {
        cachedAt: Date.now(),
        revision: getCityRevision(detail),
      },
    }));
    return detail;
  };

  useEffect(() => {
    if (proAccess.loading) return;
    if (!selectedCity) return;
    if (!isPanelOpen) return;
    if (!proAccess.authenticated || !proAccess.subscriptionActive) return;
    if (cityDetailsByName[selectedCity]) return;

    let cancelled = false;
    setLoadingState((current) => ({ ...current, cityDetail: true }));
    void ensureCityDetail(selectedCity, false, "panel")
      .then((detail) => {
        if (cancelled) return;
        setSelectedForecastDate(detail.local_date);
      })
      .catch(() => {})
      .finally(() => {
        if (cancelled) return;
        setLoadingState((current) => ({ ...current, cityDetail: false }));
      });

    return () => {
      cancelled = true;
    };
  }, [
    cityDetailsByName,
    ensureCityDetail,
    isPanelOpen,
    proAccess.authenticated,
    proAccess.loading,
    proAccess.subscriptionActive,
    selectedCity,
  ]);

  const ensureCityMarketScan = async (
    cityName: string,
    force = false,
    marketSlug?: string | null,
    targetDate?: string | null,
  ) => {
    const cacheKey = getMarketScanCacheKey(cityName, targetDate);
    const cached = marketScanByCityName[cacheKey];
    if (!force && cached && !marketSlug) {
      return cached;
    }

    const latestScan = await dashboardClient.getCityMarketScan(cityName, {
      force,
      marketSlug,
      targetDate,
    });
    if (latestScan) {
      setMarketScanByCityName((current) => ({
        ...current,
        [cacheKey]: latestScan,
      }));
    }
    return latestScan;
  };

  const loadCities = async () => {
    setLoadingState((current) => ({ ...current, cities: true }));
    try {
      const nextCities = await dashboardClient.getCities();
      setCities(nextCities);
    } finally {
      setLoadingState((current) => ({ ...current, cities: false }));
    }
  };

  const refreshProAccess = async () => {
    setProAccess((current) => ({
      ...current,
      loading: true,
      error: null,
    }));
    try {
      const response = await fetch("/api/auth/me", {
        cache: "no-store",
        headers: {
          Accept: "application/json",
        },
      });
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      const payload = (await response.json()) as {
        authenticated?: boolean;
        user_id?: string | null;
        subscription_active?: boolean | null;
        subscription_plan_code?: string | null;
        subscription_expires_at?: string | null;
        points?: number;
      };
      setProAccess({
        loading: false,
        authenticated: Boolean(payload.authenticated),
        userId: payload.user_id ?? null,
        subscriptionActive: payload.subscription_active === true,
        subscriptionPlanCode: payload.subscription_plan_code ?? null,
        subscriptionExpiresAt: payload.subscription_expires_at ?? null,
        points: payload.points ?? 0,
        error: null,
      });
    } catch (error) {
      setProAccess({
        loading: false,
        authenticated: false,
        userId: null,
        subscriptionActive: false,
        subscriptionPlanCode: null,
        subscriptionExpiresAt: null,
        points: 0,
        error: String(error),
      });
    }
  };

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

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

  const ensureCitySummary = async (cityName: string, force = false) => {
    const existing = citySummariesRef.current[cityName];
    if (!force && existing) {
      return existing;
    }

    const inflight = summaryInflightByCityRef.current[cityName];
    if (inflight) {
      const settled = await inflight;
      if (!force) {
        return settled;
      }
    }

    const request = dashboardClient
      .getCitySummary(cityName, { force })
      .then((summary) => {
        setCitySummariesByName((current) => {
          const currentSummary = current[cityName];
          const currentRevision = getCityRevision(currentSummary);
          const nextRevision = getCityRevision(summary);
          if (
            currentSummary &&
            currentRevision &&
            nextRevision &&
            currentRevision === nextRevision
          ) {
            return current;
          }
          const next = {
            ...current,
            [cityName]: summary,
          };
          citySummariesRef.current = next;
          return next;
        });
        return summary;
      })
      .finally(() => {
        if (summaryInflightByCityRef.current[cityName] === request) {
          delete summaryInflightByCityRef.current[cityName];
        }
      });

    summaryInflightByCityRef.current[cityName] = request;
    return request;
  };

  useEffect(() => {
    if (proAccess.loading || !proAccess.authenticated || !proAccess.userId) {
      return;
    }
    if (
      markAnalyticsOnce(`dashboard-active:${proAccess.userId}`, "session")
    ) {
      trackAppEvent("dashboard_active", {
        subscription_active: proAccess.subscriptionActive,
        subscription_plan_code: proAccess.subscriptionPlanCode,
      });
    }

    const isTrialPlan = /trial/i.test(
      String(proAccess.subscriptionPlanCode || ""),
    );
    if (
      isTrialPlan &&
      markAnalyticsOnce(`signup-completed:${proAccess.userId}`, "local")
    ) {
      trackAppEvent("signup_completed", {
        source: "auth_me_trial",
        subscription_plan_code: proAccess.subscriptionPlanCode,
      });
    }
  }, [
    proAccess.authenticated,
    proAccess.loading,
    proAccess.subscriptionActive,
    proAccess.subscriptionPlanCode,
    proAccess.userId,
  ]);

  useEffect(() => {
    if (EAGER_CITY_SUMMARIES_DISABLED) return;
    if (!cities.length) return;
    if (loadingState.refresh) return;

    const priorityQueue: string[] = [];
    const backgroundQueue: string[] = [];
    const queued = new Set<string>();
    const enqueue = (cityName: string | null, queue: string[]) => {
      if (!cityName || queued.has(cityName) || citySummariesRef.current[cityName]) {
        return;
      }
      queued.add(cityName);
      queue.push(cityName);
    };

    enqueue(getStoredSelectedCityName(cities), priorityQueue);
    getPriorityPreloadCities(cities).forEach((cityName) => {
      enqueue(cityName, priorityQueue);
    });
    cities.forEach((city) => {
      enqueue(city.name, backgroundQueue);
    });

    if (!priorityQueue.length && !backgroundQueue.length) return;

    let active = true;
    let cancelIdleSchedule = () => {};
    const runQueue = async (queue: string[], concurrency: number) => {
      if (!queue.length) return;
      let cursor = 0;
      const worker = async () => {
        while (active && cursor < queue.length) {
          const cityName = queue[cursor];
          cursor += 1;
          if (citySummariesRef.current[cityName]) continue;

          try {
            await ensureCitySummary(cityName);
          } catch {}
        }
      };

      await Promise.all(
        Array.from({ length: Math.min(concurrency, queue.length) }, () =>
          worker()
        ),
      );
    };

    void (async () => {
      await runQueue(priorityQueue, EAGER_SUMMARY_PRIORITY_CONCURRENCY);
      if (!active) return;
      if (isMapInteracting) return;
      cancelIdleSchedule = scheduleWhenBrowserIdle(() => {
        if (!active || isMapInteracting) return;
        void runQueue(
          backgroundQueue,
          EAGER_SUMMARY_BACKGROUND_CONCURRENCY,
        );
      });
    })();

    return () => {
      active = false;
      cancelIdleSchedule();
    };
  }, [cities, isMapInteracting, loadingState.refresh]);

  const selectCity = async (cityName: string) => {
    setSelectedCity(cityName);
    setIsPanelOpen(true);
    setSelectedForecastDate(null);
    setFutureModalDate(null);

    const summaryPromise = !citySummariesRef.current[cityName]
      ? ensureCitySummary(cityName).catch(() => null)
      : Promise.resolve(citySummariesRef.current[cityName]);

    if (proAccessRef.current.loading) {
      setLoadingState((current) => ({ ...current, cityDetail: true }));
      try {
        await summaryPromise;
      } catch {
      } finally {
        setLoadingState((current) => ({ ...current, cityDetail: false }));
      }
      return;
    }

    const access = proAccessRef.current;
    if (!access.authenticated || !access.subscriptionActive) {
      setLoadingState((current) => ({ ...current, cityDetail: true }));
      try {
        await summaryPromise;
      } catch {
      } finally {
        setLoadingState((current) => ({ ...current, cityDetail: false }));
      }
      return;
    }

    const cachedDetail = cityDetailsByName[cityName];
    const needsDetailRefresh = hasSparseDetailCoverage(
      cachedDetail,
      cachedDetail?.local_date,
    );
    setLoadingState((current) => ({ ...current, cityDetail: true }));
    try {
      await summaryPromise;
    } catch {
    }

    void ensureCityDetail(cityName, needsDetailRefresh, "panel")
      .then((detail) => {
        if (selectedCityRef.current !== cityName) return;
        setSelectedForecastDate(detail.local_date);
        if (access.authenticated && access.subscriptionActive) {
          // 预热市场数据，不做 await 阻塞，后台静默拉取
          void ensureCityMarketScan(
            cityName,
            false,
            null,
            detail.local_date,
          ).catch(() => {});
        }
      })
      .finally(() => {
        if (selectedCityRef.current !== cityName) return;
        setLoadingState((current) => ({ ...current, cityDetail: false }));
      });
  };

  useEffect(() => {
    if (typeof window === "undefined") return;
    if (selectedCity) {
      window.localStorage.setItem(SELECTED_CITY_STORAGE_KEY, selectedCity);
    } else {
      window.localStorage.removeItem(SELECTED_CITY_STORAGE_KEY);
    }
  }, [selectedCity]);

  useEffect(() => {
    if (hydratedSelectionRef.current) return;
    if (!cities.length) return;
    if (selectedCity) {
      hydratedSelectionRef.current = true;
      return;
    }
    if (typeof window === "undefined") return;

    hydratedSelectionRef.current = true;
    const stored = String(
      window.localStorage.getItem(SELECTED_CITY_STORAGE_KEY) || "",
    )
      .trim()
      .toLowerCase();
    if (!stored) return;
    if (!cities.some((city) => city.name === stored)) return;
    void selectCity(stored);
  }, [cities, selectedCity, selectCity]);

  const refreshSelectedCity = async () => {
    if (!selectedCity) return;
    setLoadingState((current) => ({ ...current, refresh: true }));
    try {
      const detail = await ensureCityDetail(selectedCity, true, "panel");
      setSelectedForecastDate(detail.local_date);
    } finally {
      setLoadingState((current) => ({ ...current, refresh: false }));
    }
  };

  const refreshAll = async () => {
    dashboardClient.clearCityDetailCache();
    setCityDetailsByName({});
    setCityDetailMetaByName({});
    if (selectedCity) {
      const access = proAccessRef.current;
      setLoadingState((current) => ({ ...current, refresh: true }));
      try {
        if (access.authenticated && access.subscriptionActive) {
          const latestDetail = await dashboardClient.getCityDetail(selectedCity, {
            force: true,
            depth: "panel",
          });
          const detail = latestDetail;
          setCityDetailsByName({ [selectedCity]: detail });
          setCitySummariesByName((current) => ({
            ...current,
            [selectedCity]: toCitySummary(detail),
          }));
          setCityDetailMetaByName({
            [selectedCity]: {
              cachedAt: Date.now(),
              revision: getCityRevision(detail),
            },
          });
          setSelectedForecastDate(detail.local_date);
        } else {
          const summary = await ensureCitySummary(selectedCity, true);
          setCitySummariesByName((current) => ({
            ...current,
            [selectedCity]: summary,
          }));
        }
      } finally {
        setLoadingState((current) => ({ ...current, refresh: false }));
      }
    }
  };

  const openHistory = async () => {
    if (!selectedCity) return;
    if (!proAccess.subscriptionActive) {
      setHistoryState((current) => ({
        ...current,
        error: null,
        isOpen: true,
        loading: false,
      }));
      return;
    }
    setHistoryState((current) => ({
      ...current,
      error: null,
      isOpen: true,
      loading: true,
    }));
    try {
      const history = await dashboardClient.getHistory(selectedCity);
      setHistoryState((current) => ({
        ...current,
        dataByCity: {
          ...current.dataByCity,
          [selectedCity]: history,
        },
        loading: false,
      }));
    } catch (error) {
      setHistoryState((current) => ({
        ...current,
        error: String(error),
        loading: false,
      }));
    }
  };

  const value = useMemo<DashboardStoreValue>(
    () => ({
      cities,
      cityDetailsByName,
      citySummariesByName,
      closeFutureModal: () => setFutureModalDate(null),
      closeHistory: () =>
        setHistoryState((current) => ({ ...current, isOpen: false })),
      closePanel: () => {
        setIsPanelOpen(false);
      },
      ensureCityDetail,
      futureModalDate,
      historyState,
      isPanelOpen,
      loadCities,
      loadingState,
      proAccess,
      openFutureModal: async (dateStr: string, forceRefresh = false) => {
        mapStopMotionRef.current();
        if (!selectedCity || !proAccess.subscriptionActive) return;
        const cachedDetail = cityDetailsByName[selectedCity];
        const hasFullCachedDetail =
          detailSatisfiesDepth(cachedDetail, "full") &&
          !hasSparseDetailCoverage(cachedDetail, dateStr);

        if (!hasFullCachedDetail || forceRefresh) {
          setLoadingState((current) => ({
            ...current,
            refresh: true,
          }));
          try {
            await ensureCityDetail(selectedCity, true, "full");
          } catch {
          } finally {
            setLoadingState((current) => ({
              ...current,
              refresh: false,
            }));
          }
        }

        setFutureModalDate(dateStr);
        const cacheKey = getMarketScanCacheKey(selectedCity, dateStr);
        setLoadingState((current) => ({ ...current, marketScan: true }));
        void ensureCityMarketScan(
          selectedCity,
          forceRefresh || !marketScanByCityName[cacheKey],
          null,
          dateStr,
        )
          .catch(() => {})
          .finally(() => {
            setLoadingState((current) => ({ ...current, marketScan: false }));
          });
      },
      openHistory,
      openTodayModal: async (forceRefresh?: boolean) => {
        if (!selectedCity) {
          return;
        }

        mapStopMotionRef.current();
        const cachedDetail = cityDetailsByName[selectedCity];
        const hasFullCachedDetail =
          detailSatisfiesDepth(cachedDetail, "full") &&
          !hasSparseDetailCoverage(cachedDetail, cachedDetail?.local_date);
        if (hasFullCachedDetail && cachedDetail?.local_date) {
          setSelectedForecastDate(cachedDetail.local_date);
          setFutureModalDate(cachedDetail.local_date);
        }
        if (!proAccess.subscriptionActive) return;
        const needsDetailRefresh =
          forceRefresh ||
          !detailSatisfiesDepth(cachedDetail, "full") ||
          hasSparseDetailCoverage(cachedDetail, cachedDetail?.local_date);

        setLoadingState((current) => ({
          ...current,
          refresh: needsDetailRefresh,
          marketScan: true,
        }));

        try {
          const detail = await ensureCityDetail(
            selectedCity,
            needsDetailRefresh,
            "full",
          );
          setSelectedForecastDate(detail.local_date);
          setFutureModalDate(detail.local_date);

          const marketKey = getMarketScanCacheKey(selectedCity, detail.local_date);
          try {
            await ensureCityMarketScan(
              selectedCity,
              forceRefresh || !marketScanByCityName[marketKey],
              null,
              detail.local_date,
            );
          } catch {}
        } catch {
          if (cachedDetail?.local_date) {
            setSelectedForecastDate(cachedDetail.local_date);
            setFutureModalDate(cachedDetail.local_date);
          }
        } finally {
          setLoadingState((current) => ({
            ...current,
            refresh: false,
            marketScan: false,
          }));
        }
      },
      registerMapStopMotion: (stopMotion: () => void) => {
        mapStopMotionRef.current = stopMotion;
      },
      refreshAll,
      refreshProAccess,
      refreshSelectedCity,
      selectedMarketScan,
      selectedCity,
      selectedDetail,
      selectedForecastDate,
      selectCity,
      setMapInteractionActive: setIsMapInteracting,
      setForecastDate: (dateStr: string | null) =>
        setSelectedForecastDate(dateStr),
      marketScanByCityName,
    }),
    [
      cities,
      cityDetailsByName,
      citySummariesByName,
      futureModalDate,
      historyState,
      isPanelOpen,
      loadingState,
      proAccess,
      marketScanByCityName,
      selectedMarketScan,
      selectedCity,
      selectedDetail,
      selectedForecastDate,
    ],
  );

  return (
    <DashboardStoreContext.Provider value={value}>
      {children}
    </DashboardStoreContext.Provider>
  );
}

export function useDashboardStore() {
  const context = useContext(DashboardStoreContext);
  if (!context) {
    throw new Error(
      "useDashboardStore must be used within DashboardStoreProvider",
    );
  }
  return context;
}

export function useCityData(name?: string | null) {
  const store = useDashboardStore();
  const key = name || store.selectedCity;
  return {
    data: key ? store.cityDetailsByName[key] || null : null,
    isLoading:
      store.loadingState.cityDetail &&
      Boolean(key) &&
      store.selectedCity === key,
  };
}

export function useHistoryData(name?: string | null) {
  const store = useDashboardStore();
  const key = name || store.selectedCity;
  return {
    data: key
      ? store.historyState.dataByCity[key] || ([] as HistoryPoint[])
      : [],
    error: store.historyState.error,
    isLoading: store.historyState.loading,
    isOpen: store.historyState.isOpen,
  };
}
