import { createContext, useContext, useEffect, useRef, useState, useCallback, ReactNode } from "react";
import { Session, User } from "@supabase/supabase-js";
import { supabase } from "@/integrations/supabase/client";

export const ACTIVE_LOGIN_SESSION_KEY = "ny-denki-active-login";

export type AppRole = "admin" | "manager" | "employee";

interface AuthContextType {
  session: Session | null;
  user: User | null;
  profile: any | null;
  role: AppRole | null;
  loading: boolean;
  signOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>({
  session: null,
  user: null,
  profile: null,
  role: null,
  loading: true,
  signOut: async () => {},
});

const markActiveLoginSession = () => {
  try {
    localStorage.setItem(ACTIVE_LOGIN_SESSION_KEY, "1");
  } catch {
    // ignore storage issues
  }
};

const clearActiveLoginSession = () => {
  try {
    localStorage.removeItem(ACTIVE_LOGIN_SESSION_KEY);
  } catch {
    // ignore storage issues
  }
};

export const useAuth = () => useContext(AuthContext);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [session, setSession] = useState<Session | null>(null);
  const [user, setUser] = useState<User | null>(null);
  const [profile, setProfile] = useState<any | null>(null);
  const [role, setRole] = useState<AppRole | null>(null);
  const [loading, setLoading] = useState(true);
  const profileFetchInFlight = useRef<string | null>(null);
  const lastFetchedUserRef = useRef<string | null>(null);
  const refreshDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const fetchProfile = useCallback(async (userId: string) => {
    if (profileFetchInFlight.current === userId) return;
    profileFetchInFlight.current = userId;

    try {
      const [profileRes, roleRes] = await Promise.all([
        supabase
          .from("profiles")
          .select("*")
          .eq("user_id", userId)
          .maybeSingle(),
        supabase
          .from("user_roles")
          .select("role")
          .eq("user_id", userId)
          .maybeSingle(),
      ]);

      const fetchedProfile = profileRes.data ?? null;
      const profileEmail: string | null = (fetchedProfile as any)?.email ?? null;
      const isActive: boolean = (fetchedProfile as any)?.is_active ?? true;
      const isSuperAdmin = profileEmail === "nydenki17@gmail.com";

      // Block disabled accounts (Super Admin always allowed)
      if (!isActive && !isSuperAdmin) {
        console.warn("[Auth] account disabled, signing out:", userId);
        clearActiveLoginSession();
        setProfile(null);
        setRole(null);
        setSession(null);
        setUser(null);
        try {
          if (typeof window !== "undefined") {
            const { toast } = await import("sonner");
            toast.error("このアカウントは無効です。管理者にお問い合わせください。");
          }
        } catch {}
        void supabase.auth.signOut();
        return;
      }

      setProfile(fetchedProfile);
      const nextRole = (roleRes.data?.role as AppRole | undefined) ?? "employee";
      setRole(nextRole);
      lastFetchedUserRef.current = userId;

      // Report current app build to backend (used by admin dashboard to detect old apps)
      try {
        const { reportAppVersion } = await import("@/lib/app-version");
        void reportAppVersion(userId);
        // Also refresh the latest build cache so blockIfOldApp() can detect outdated bundles
        const { refreshLatestBuildDateCache } = await import("@/components/AppUpdateRequiredDialog");
        void refreshLatestBuildDateCache();
      } catch {
        // ignore
      }
    } catch (error) {
      console.error("[Auth] fetchProfile failed:", error);
      // Even on error, fall back to employee so UI doesn't stay stuck
      setRole((prev) => prev ?? "employee");
    } finally {
      profileFetchInFlight.current = null;
    }
  }, []);

  // SINGLE initialization effect — runs once
  useEffect(() => {
    let isMounted = true;

    // Detect stored token early — used to avoid premature "no session" redirects on F5
    let hasStoredTokenAtBoot = false;
    try {
      for (let i = 0; i < localStorage.length; i++) {
        const k = localStorage.key(i);
        if (k && k.startsWith("sb-") && k.endsWith("-auth-token")) {
          hasStoredTokenAtBoot = true;
          break;
        }
      }
    } catch {}

    // Listener FIRST so we never miss a SIGNED_IN event
    const { data: { subscription } } = supabase.auth.onAuthStateChange((event, nextSession) => {
      if (!isMounted) return;

      if (event === "SIGNED_OUT" || !nextSession?.user) {
        // Ignore INITIAL_SESSION with null while we still have a stored token —
        // Supabase is just about to restore the session and emit it again.
        if (event === "INITIAL_SESSION" && hasStoredTokenAtBoot) {
          return;
        }
        clearActiveLoginSession();
        setSession(null);
        setUser(null);
        setProfile(null);
        setRole(null);
        setLoading(false);
        lastFetchedUserRef.current = null;
        return;
      }

      if (event === "SIGNED_IN") {
        markActiveLoginSession();
      }

      // Once we have a real session, stored-token guard is no longer needed
      hasStoredTokenAtBoot = false;

      setSession(nextSession);
      setUser(nextSession.user);
      setLoading(false);

      // Fetch profile only if user changed (avoid re-fetch on TOKEN_REFRESHED)
      if (lastFetchedUserRef.current !== nextSession.user.id) {
        // Defer to avoid blocking the auth callback
        setTimeout(() => {
          if (isMounted) void fetchProfile(nextSession.user.id);
        }, 0);
      }
    });

    // Restore session from storage. If a stored token exists, keep loading=true
    // until we actually resolve the session — avoids flicker that kicks the user
    // to /login on F5 while Supabase is still restoring the session.
    const initSession = async () => {
      // Quick check: any sb-* token in localStorage?
      let hasStoredToken = false;
      try {
        for (let i = 0; i < localStorage.length; i++) {
          const k = localStorage.key(i);
          if (k && k.startsWith("sb-") && k.endsWith("-auth-token")) {
            hasStoredToken = true;
            break;
          }
        }
      } catch {}

      if (!hasStoredToken) {
        if (isMounted) setLoading(false);
        return;
      }

      // Has stored token — wait up to 15s for getSession to resolve.
      // Do NOT prematurely flip loading=false: that would let ProtectedRoute
      // redirect to /login even though a valid session is about to be restored.
      try {
        const timeout = new Promise<{ data: { session: null } }>((resolve) =>
          setTimeout(() => resolve({ data: { session: null } }), 15000)
        );
        const result = await Promise.race([supabase.auth.getSession(), timeout]);
        if (!isMounted) return;

        const nextSession = (result as any)?.data?.session ?? null;
        if (nextSession?.user) {
          setSession(nextSession);
          setUser(nextSession.user);
          if (lastFetchedUserRef.current !== nextSession.user.id) {
            void fetchProfile(nextSession.user.id);
          }
        }
      } catch (error) {
        console.error("[Auth] getSession failed:", error);
      } finally {
        if (isMounted) setLoading(false);
      }
    };

    void initSession();

    return () => {
      isMounted = false;
      subscription.unsubscribe();
    };
  }, [fetchProfile]);

  // Refresh profile on focus / visibility (debounced)
  useEffect(() => {
    if (!user?.id) return;

    const debouncedRefresh = () => {
      if (refreshDebounceRef.current) clearTimeout(refreshDebounceRef.current);
      refreshDebounceRef.current = setTimeout(() => {
        void fetchProfile(user.id);
      }, 2000);
    };

    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible") debouncedRefresh();
    };

    window.addEventListener("focus", debouncedRefresh);
    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      window.removeEventListener("focus", debouncedRefresh);
      document.removeEventListener("visibilitychange", handleVisibilityChange);
      if (refreshDebounceRef.current) clearTimeout(refreshDebounceRef.current);
    };
  }, [user?.id, fetchProfile]);

  const signOut = async () => {
    clearActiveLoginSession();
    await supabase.auth.signOut();
    setSession(null);
    setUser(null);
    setProfile(null);
    setRole(null);
    if (typeof window !== "undefined") {
      window.location.replace("/login");
    }
  };

  return (
    <AuthContext.Provider value={{ session, user, profile, role, loading, signOut }}>
      {children}
    </AuthContext.Provider>
  );
}
