/**
 * Fetches duties and calendar events for the current month. Handles loading, error,
 * access denied, and retry after ACCESS_DENIED. Replaces loadMonth() from webapp/js/main.js.
 */

"use client";

import { useEffect, useRef, useCallback } from "react";
import { useAppStore } from "@/store/app-store";
import { fetchDuties, fetchCalendarEvents, AccessDeniedError } from "@/lib/api";
import {
  firstDayOfMonth,
  lastDayOfMonth,
  getMonday,
  localDateString,
  dutyOverlapsLocalRange,
} from "@/lib/date-utils";
import { RETRY_AFTER_ACCESS_DENIED_MS, RETRY_AFTER_ERROR_MS, MAX_GENERAL_RETRIES } from "@/lib/constants";
import { logger } from "@/lib/logger";
import { translate } from "@/i18n/messages";

export interface UseMonthDataOptions {
  /** Telegram init data string for API auth. When undefined, no fetch (unless isLocalhost). */
  initDataRaw: string | undefined;
  /** When true, fetch runs for the current month. When false, no fetch (e.g. access not allowed). */
  enabled: boolean;
}

/**
 * Fetches duties and calendar events for the displayed month when enabled.
 * When pendingMonth is set (user clicked next/prev), loads that month without clearing
 * current data; on success updates currentMonth and data in one batch (no empty-frame flicker).
 * Cancels in-flight request when month changes or component unmounts.
 * On ACCESS_DENIED, shows access denied and retries once after RETRY_AFTER_ACCESS_DENIED_MS.
 * Returns retry() to manually trigger a reload.
 */
export function useMonthData(options: UseMonthDataOptions): { retry: () => void } {
  const { initDataRaw, enabled } = options;

  const currentMonth = useAppStore((s) => s.currentMonth);
  const pendingMonth = useAppStore((s) => s.pendingMonth);
  const lang = useAppStore((s) => s.lang);

  const abortRef = useRef<AbortController | null>(null);
  const retryTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const initDataRetriedRef = useRef(false);
  const generalRetryCountRef = useRef(0);
  const mountedRef = useRef(true);
  const optionsRef = useRef({ initDataRaw, enabled, lang });
  optionsRef.current = { initDataRaw, enabled, lang };

  const loadRef = useRef<() => void>(() => {});

  useEffect(() => {
    return () => {
      mountedRef.current = false;
    };
  }, []);

  const load = useCallback(() => {
    const { initDataRaw: initDataRawOpt, enabled: enabledOpt, lang: langOpt } = optionsRef.current;
    if (!enabledOpt) {
      useAppStore.getState().batchUpdate({ pendingMonth: null });
      return;
    }
    const initData = initDataRawOpt ?? "";
    if (!initData && typeof window !== "undefined") {
      const h = window.location.hostname;
      if (h !== "localhost" && h !== "127.0.0.1" && h !== "") {
        useAppStore.getState().batchUpdate({ pendingMonth: null });
        return;
      }
    }

    const store = useAppStore.getState();
    const pending = store.pendingMonth;
    const monthToLoad = pending ?? store.currentMonth;
    const monthKey = `${monthToLoad.getFullYear()}-${String(monthToLoad.getMonth() + 1).padStart(2, "0")}`;
    const dataForMonthKey = store.dataForMonthKey;
    const isDeferredSwitch = pending !== null;
    const isNewMonth = !isDeferredSwitch && dataForMonthKey !== monthKey;

    if (abortRef.current) abortRef.current.abort();
    abortRef.current = new AbortController();
    const signal = abortRef.current.signal;

    store.batchUpdate({
      accessDenied: false,
      accessDeniedDetail: null,
      loading: true,
      error: null,
      ...(isNewMonth
        ? { duties: [], calendarEvents: [], dataForMonthKey: null }
        : {}),
    });

    const first = firstDayOfMonth(monthToLoad);
    const start = getMonday(first);
    const gridEnd = new Date(start);
    gridEnd.setDate(gridEnd.getDate() + 41);
    const from = localDateString(start);
    const to = localDateString(gridEnd);

    const run = async () => {
      try {
        logger.debug("Loading month", from, to);
        const [duties, events] = await Promise.all([
          fetchDuties(from, to, initData, langOpt, signal),
          fetchCalendarEvents(from, to, initData, langOpt, signal),
        ]);

        const last = lastDayOfMonth(monthToLoad);
        const firstKey = localDateString(first);
        const lastKey = localDateString(last);
        const dutiesInMonth = duties.filter((d) =>
          dutyOverlapsLocalRange(d, firstKey, lastKey)
        );

        const storeAfter = useAppStore.getState();
        const switchedMonth =
          storeAfter.currentMonth.getFullYear() !== monthToLoad.getFullYear() ||
          storeAfter.currentMonth.getMonth() !== monthToLoad.getMonth();

        useAppStore.getState().batchUpdate({
          ...(switchedMonth
            ? { currentMonth: new Date(monthToLoad.getFullYear(), monthToLoad.getMonth(), 1) }
            : {}),
          pendingMonth: null,
          duties: dutiesInMonth,
          calendarEvents: events,
          dataForMonthKey: monthKey,
          loading: false,
          error: null,
        });
      } catch (e) {
        if ((e as Error).name === "AbortError") {
          useAppStore.getState().batchUpdate({
            loading: false,
            pendingMonth: null,
          });
          return;
        }
        if (e instanceof AccessDeniedError) {
          logger.warn("Access denied in loadMonth", e.serverDetail);
          useAppStore.getState().batchUpdate({
            accessDenied: true,
            accessDeniedDetail: e.serverDetail ?? null,
            loading: false,
            pendingMonth: null,
          });
          if (!initDataRetriedRef.current) {
            initDataRetriedRef.current = true;
            const timeoutId = setTimeout(() => {
              if (mountedRef.current) loadRef.current();
            }, RETRY_AFTER_ACCESS_DENIED_MS);
            retryTimeoutRef.current = timeoutId;
          }
          return;
        }
        logger.error("Load month failed", e);
        if (generalRetryCountRef.current < MAX_GENERAL_RETRIES && mountedRef.current) {
          generalRetryCountRef.current++;
          const timeoutId = setTimeout(() => {
            if (mountedRef.current) loadRef.current();
          }, RETRY_AFTER_ERROR_MS);
          retryTimeoutRef.current = timeoutId;
          return;
        }
        useAppStore.getState().batchUpdate({
          error: translate(langOpt, "error_generic"),
          loading: false,
          pendingMonth: null,
        });
      }
    };

    run();
  }, []);

  loadRef.current = load;

  useEffect(() => {
    if (!enabled) return;
    initDataRetriedRef.current = false;
    generalRetryCountRef.current = 0;
    load();
    return () => {
      if (retryTimeoutRef.current) {
        clearTimeout(retryTimeoutRef.current);
        retryTimeoutRef.current = null;
      }
      if (abortRef.current) abortRef.current.abort();
      abortRef.current = null;
    };
  }, [enabled, load, currentMonth, pendingMonth, lang, initDataRaw]);

  useEffect(() => {
    if (!enabled) return;
    const handleVisibility = () => {
      if (document.visibilityState !== "visible") return;
      const { duties, loading: isLoading, error: hasError } = useAppStore.getState();
      if (duties.length === 0 && !isLoading && !hasError) {
        generalRetryCountRef.current = 0;
        loadRef.current();
      }
    };
    document.addEventListener("visibilitychange", handleVisibility);
    return () => document.removeEventListener("visibilitychange", handleVisibility);
  }, [enabled]);

  return { retry: load };
}
