import { useState, useEffect, useCallback, useRef } from "react";
import { authClient } from "~/lib/auth-client";
import type { getDraftPicksForSeason } from "~/models/draft-pick";
import type { findSeasonWithTeamsAndLeague } from "~/models/season";
import type { getSeasonTimers } from "~/models/draft-timer";
import type { getSeasonAutodraftSettings } from "~/models/autodraft-settings";
import type { AutodraftStatusEntry, QueueItem } from "~/hooks/useDraftRoomState";

type DraftPicks = Awaited<ReturnType<typeof getDraftPicksForSeason>>;
type Season = NonNullable<Awaited<ReturnType<typeof findSeasonWithTeamsAndLeague>>>;
type Timers = Awaited<ReturnType<typeof getSeasonTimers>>;
type AutodraftSettings = Awaited<ReturnType<typeof getSeasonAutodraftSettings>>;

interface UseDraftAuthRecoveryParams {
  reconnectCount: number;
  revalidate: () => void;
  revalidatorState: "idle" | "loading" | "submitting";
  currentUserId: string | null | undefined;
  userAutodraftSettings: { isEnabled: boolean; mode: "next_pick" | "while_on"; queueOnly: boolean } | null;
  // Loader data for revalidation sync
  draftPicks: DraftPicks;
  season: Season;
  userQueue: QueueItem[];
  timers: Timers;
  autodraftSettings: AutodraftSettings;
  // State setters to sync after revalidation
  setPicks: (picks: DraftPicks) => void;
  setCurrentPick: (pick: number) => void;
  setIsPaused: (paused: boolean) => void;
  setIsDraftComplete: (complete: boolean) => void;
  setQueue: (queue: QueueItem[]) => void;
  setTeamTimers: (fn: (prev: Record<string, number>) => Record<string, number>) => void;
  setAutodraftStatus: (fn: () => Record<string, AutodraftStatusEntry>) => void;
  setIsSyncing: (value: boolean) => void;
}


export function useDraftAuthRecovery({
  reconnectCount,
  revalidate,
  revalidatorState,
  currentUserId,
  userAutodraftSettings,
  draftPicks,
  season,
  userQueue,
  timers,
  autodraftSettings,
  setPicks,
  setCurrentPick,
  setIsPaused,
  setIsDraftComplete,
  setQueue,
  setTeamTimers,
  setAutodraftStatus,
  setIsSyncing,
}: UseDraftAuthRecoveryParams) {
  const [authDegraded, setAuthDegraded] = useState(false);

  const [userAutodraft, setUserAutodraft] = useState({
    isEnabled: userAutodraftSettings?.isEnabled || false,
    mode: (userAutodraftSettings?.mode || "next_pick") as "next_pick" | "while_on",
    queueOnly: userAutodraftSettings?.queueOnly || false,
  });
  const userAutodraftRef = useRef(userAutodraft);
  useEffect(() => {
    userAutodraftRef.current = userAutodraft;
  }, [userAutodraft]);

  // Re-fetch loader data after each reconnect so stale picks/timers are refreshed.
  const revalidationRetryRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    if (reconnectCount > 0) {
      revalidate();
      if (revalidationRetryRef.current) {
        clearTimeout(revalidationRetryRef.current);
      }
      revalidationRetryRef.current = setTimeout(() => {
        revalidate();
        revalidationRetryRef.current = null;
      }, 3000);
    }
    return () => {
      if (revalidationRetryRef.current) {
        clearTimeout(revalidationRetryRef.current);
      }
    };
  }, [reconnectCount, revalidate]);

  // Re-check session when the tab becomes visible — cookie-based sessions don't
  // need manual token refresh, but a long-idle tab may have an expired session.
  useEffect(() => {
    if (authDegraded) return;

    let aborted = false;
    let inFlight = false;

    const handleVisibilityChange = async () => {
      if (document.visibilityState !== "visible" || inFlight) return;
      inFlight = true;
      try {
        const { data: session } = await authClient.getSession();
        if (aborted) return;
        if (session) {
          if (!currentUserId) revalidate();
        } else {
          setAuthDegraded(true);
        }
      } catch {
        if (!aborted) setAuthDegraded(true);
      } finally {
        inFlight = false;
      }
    };

    document.addEventListener("visibilitychange", handleVisibilityChange);
    return () => {
      aborted = true;
      document.removeEventListener("visibilitychange", handleVisibilityChange);
    };
  }, [currentUserId, authDegraded, revalidate]);

  // Periodically ping the session endpoint to keep it alive during long drafts.
  useEffect(() => {
    if (authDegraded) return;
    const FIFTEEN_MINUTES = 15 * 60 * 1000;
    const id = setInterval(async () => {
      const { data: session } = await authClient.getSession();
      if (!session) setAuthDegraded(true);
    }, FIFTEEN_MINUTES);
    return () => clearInterval(id);
  }, [authDegraded]);

  // Track revalidation lifecycle to sync local state from fresh loader data.
  const revalidatorStateRef = useRef(revalidatorState);
  const isRevalidatingRef = useRef(false);
  const pendingPicksDuringRevalidationRef = useRef<DraftPicks>([]);
  const draftPicksAtRevalidationStartRef = useRef(draftPicks);
  const userQueueAtRevalidationStartRef = useRef(userQueue);
  const pendingQueueMutationsRef = useRef(0);

  useEffect(() => {
    const prev = revalidatorStateRef.current;
    revalidatorStateRef.current = revalidatorState;

    if (prev !== "loading" && revalidatorState === "loading") {
      isRevalidatingRef.current = true;
      pendingPicksDuringRevalidationRef.current = [];
      draftPicksAtRevalidationStartRef.current = draftPicks;
      userQueueAtRevalidationStartRef.current = userQueue;
    } else if (prev === "loading" && revalidatorState === "idle") {
      isRevalidatingRef.current = false;

      if (draftPicks === draftPicksAtRevalidationStartRef.current) {
        pendingPicksDuringRevalidationRef.current = [];
        return;
      }

      const dbPickIds = new Set(draftPicks.map((p) => p.id));
      const missedPicks = pendingPicksDuringRevalidationRef.current.filter(
        (p) => !dbPickIds.has(p.id)
      );
      pendingPicksDuringRevalidationRef.current = [];

      setPicks([...draftPicks, ...missedPicks]);
      setCurrentPick(season.currentPickNumber || 1);
      setIsPaused(season.draftPaused || false);
      setIsDraftComplete(
        season.status === "active" || season.status === "completed"
      );

      if (currentUserId && userQueue !== userQueueAtRevalidationStartRef.current) {
        setQueue(userQueue);
      }

      if (timers.length > 0) {
        setTeamTimers((currentTimers) => {
          const updated = { ...currentTimers };
          timers.forEach((timer) => {
            updated[timer.teamId] = timer.timeRemaining;
          });
          return updated;
        });
      }

      setAutodraftStatus(() => {
        const status: Record<string, AutodraftStatusEntry> = {};
        autodraftSettings.forEach((setting) => {
          status[setting.teamId] = {
            isEnabled: setting.isEnabled,
            mode: setting.mode,
            queueOnly: setting.queueOnly,
          };
        });
        return status;
      });
      setIsSyncing(false);
    }
  }, [revalidatorState, draftPicks, season, userQueue, timers, autodraftSettings, currentUserId,
    setPicks, setCurrentPick, setIsPaused, setIsDraftComplete, setQueue, setTeamTimers, setAutodraftStatus, setIsSyncing]);

  const authFetch = useCallback(async (url: string, init?: RequestInit): Promise<Response | null> => {
    const response = await fetch(url, init);
    if (response.status === 401) {
      setAuthDegraded(true);
      return null;
    }
    return response;
  }, []);

  return {
    authDegraded,
    authFetch,
    userAutodraft,
    setUserAutodraft,
    userAutodraftRef,
    isRevalidatingRef,
    pendingPicksDuringRevalidationRef,
    pendingQueueMutationsRef,
  };
}
