"use client";

import { createContext, useContext, useEffect, useState, useCallback, useMemo } from "react";
import { createClient } from "@/lib/supabase/client";
import type { User } from "@supabase/supabase-js";
import type { Profile, Team } from "@/lib/supabase/types";
import type { Week } from "@/lib/types";
import { weekLockDB } from "@/lib/db";

interface AuthContextType {
  user: User | null;
  profile: Profile | null;
  team: Team | null;
  teamId: string | null;
  isAdmin: boolean;
  isLoading: boolean;
  allTeams: Team[]; // For admins to see all teams
  lockedWeeks: Set<Week>; // Which weeks are currently locked
  signOut: () => Promise<void>;
  refreshProfile: () => Promise<void>;
  refreshLockedWeeks: () => Promise<void>;
  fetchAllTeams: () => Promise<Team[]>;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  profile: null,
  team: null,
  teamId: null,
  isAdmin: false,
  isLoading: true,
  allTeams: [],
  lockedWeeks: new Set<Week>(),
  signOut: async () => { },
  refreshProfile: async () => { },
  refreshLockedWeeks: async () => { },
  fetchAllTeams: async () => [],
});

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}

interface AuthProviderProps {
  children: React.ReactNode;
}

export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);
  const [profile, setProfile] = useState<Profile | null>(null);
  const [team, setTeam] = useState<Team | null>(null);
  const [allTeams, setAllTeams] = useState<Team[]>([]);
  const [lockedWeeks, setLockedWeeks] = useState<Set<Week>>(new Set());
  const [isLoading, setIsLoading] = useState(true);

  // Use useMemo to ensure stable supabase client reference across renders
  const supabase = useMemo(() => createClient(), []);

  const fetchProfile = useCallback(async (userId: string): Promise<Profile | null> => {
    const { data: profileData, error: profileError } = await supabase
      .from("profiles")
      .select("*")
      .eq("id", userId)
      .single();

    if (profileError) {
      console.error("Error fetching profile:", profileError);
      return null;
    }

    return profileData;
  }, [supabase]);

  const fetchTeam = useCallback(async (teamId: string) => {
    const { data: teamData, error: teamError } = await supabase
      .from("teams")
      .select("*")
      .eq("id", teamId)
      .single();

    if (teamError) {
      console.error("Error fetching team:", teamError);
      return null;
    }

    return teamData;
  }, [supabase]);

  const fetchAllTeams = useCallback(async (): Promise<Team[]> => {
    const { data: teamsData, error: teamsError } = await supabase
      .from("teams")
      .select("*")
      .order("name");

    if (teamsError) {
      console.error("Error fetching all teams:", teamsError);
      return [];
    }

    setAllTeams(teamsData || []);
    return teamsData || [];
  }, [supabase]);

  const refreshLockedWeeks = useCallback(async () => {
    const locks = await weekLockDB.getAll();
    const lockedSet = new Set<Week>();
    locks.forEach((lock) => {
      if (lock.is_locked && (lock.week === 1 || lock.week === 2 || lock.week === 3)) {
        lockedSet.add(lock.week as Week);
      }
    });
    setLockedWeeks(lockedSet);
  }, []);

  const refreshProfile = useCallback(async () => {
    if (!user) return;

    const profileData = await fetchProfile(user.id);
    if (profileData) {
      setProfile(profileData);
      const teamData = await fetchTeam(profileData.team_id);
      setTeam(teamData);

      // If admin, fetch all teams
      if (profileData.is_admin) {
        await fetchAllTeams();
      }
    }
  }, [user, fetchProfile, fetchTeam, fetchAllTeams]);

  useEffect(() => {
    // Get initial session - use getUser() for server-verified auth
    const initializeAuth = async () => {
      console.log("[Auth] Starting initializeAuth...");
      try {
        // getUser() verifies the session with the server, unlike getSession() 
        // which only reads from local storage and may contain stale data
        console.log("[Auth] Calling supabase.auth.getUser()...");

        // Add timeout to prevent infinite hanging
        const timeoutPromise = new Promise<never>((_, reject) =>
          setTimeout(() => reject(new Error("Auth check timed out after 5 seconds")), 5000)
        );

        let user = null;
        let userError = null;

        try {
          const result = await Promise.race([supabase.auth.getUser(), timeoutPromise]);
          user = result.data.user;
          userError = result.error;
        } catch (timeoutError) {
          console.error("[Auth] getUser() timed out, proceeding without auth");
          setIsLoading(false);
          return;
        }

        console.log("[Auth] getUser() returned:", { user: user?.email, error: userError?.message });

        if (userError) {
          // Session is invalid or expired - clear it to allow proper redirect
          console.warn("[Auth] Session invalid, clearing:", userError.message);
          await supabase.auth.signOut();
          setIsLoading(false);
          return;
        }

        if (user) {
          console.log("[Auth] User found, setting user state...");
          setUser(user);

          // Fetch profile and team
          console.log("[Auth] Fetching profile...");
          const profileData = await fetchProfile(user.id);
          console.log("[Auth] Profile fetched:", profileData?.display_name);

          if (profileData) {
            setProfile(profileData);
            console.log("[Auth] Fetching team...");
            const teamData = await fetchTeam(profileData.team_id);
            console.log("[Auth] Team fetched:", teamData);
            setTeam(teamData);

            // If admin, fetch all teams
            if (profileData.is_admin) {
              console.log("[Auth] User is admin, fetching all teams...");
              await fetchAllTeams();
            }
          }

          // Fetch locked weeks for all users
          await refreshLockedWeeks();
        } else {
          console.log("[Auth] No user found, staying logged out");
        }
      } catch (error) {
        console.error("[Auth] Error initializing auth:", error);
      } finally {
        console.log("[Auth] Setting isLoading to false");
        setIsLoading(false);
      }
    };

    initializeAuth();

    // Listen for auth changes
    // IMPORTANT: Avoid async calls directly in the callback as it causes a deadlock bug in supabase-js
    // See: https://github.com/supabase/supabase-js/issues/issue-with-onAuthStateChange-deadlock
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {
        console.log("[Auth] onAuthStateChange:", event);

        if (event === "SIGNED_IN" && session?.user) {
          setUser(session.user);

          // Defer async work to avoid deadlock
          setTimeout(async () => {
            const profileData = await fetchProfile(session.user.id);
            if (profileData) {
              setProfile(profileData);
              const teamData = await fetchTeam(profileData.team_id);
              setTeam(teamData);

              if (profileData.is_admin) {
                await fetchAllTeams();
              }
            }
            await refreshLockedWeeks();
            setIsLoading(false);
          }, 0);
        } else if (event === "SIGNED_OUT") {
          setUser(null);
          setProfile(null);
          setTeam(null);
          setAllTeams([]);
          setIsLoading(false);
        } else if (event === "INITIAL_SESSION") {
          // Don't set loading false here, let initializeAuth handle it
        } else {
          setIsLoading(false);
        }
      }
    );

    return () => {
      subscription.unsubscribe();
    };
  }, [supabase, fetchProfile, fetchTeam, fetchAllTeams, refreshLockedWeeks]);

  const signOut = async () => {
    await supabase.auth.signOut();
    setUser(null);
    setProfile(null);
    setTeam(null);
    setAllTeams([]);
  };

  const value: AuthContextType = {
    user,
    profile,
    team,
    teamId: profile?.team_id ?? null,
    isAdmin: profile?.is_admin ?? false,
    isLoading,
    allTeams,
    lockedWeeks,
    signOut,
    refreshProfile,
    refreshLockedWeeks,
    fetchAllTeams,
  };

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