import { useQueryClient } from "@tanstack/react-query";
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react";
import toast from "react-hot-toast";
import { clearAccessToken, getAccessToken } from "@/auth-state";
import { authServiceClient, shortcutServiceClient, userServiceClient } from "@/service";
import { userKeys } from "@/hooks/useUserQueries";
import { memoKeys } from "@/hooks/useMemoQueries";
import type { Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
import type { User, UserSetting_GeneralSetting, UserSetting_WebhooksSetting } from "@/types/proto/api/v1/user_service_pb";

interface AuthState {
  currentUser: User | undefined;
  userGeneralSetting: UserSetting_GeneralSetting | undefined;
  userWebhooksSetting: UserSetting_WebhooksSetting | undefined;
  shortcuts: Shortcut[];
  isInitialized: boolean;
  isLoading: boolean;
}

interface AuthContextValue extends AuthState {
  initialize: () => Promise<void>;
  logout: () => Promise<void>;
  refetchSettings: () => Promise<void>;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const queryClient = useQueryClient();
  const [state, setState] = useState<AuthState>({
    currentUser: undefined,
    userGeneralSetting: undefined,
    userWebhooksSetting: undefined,
    shortcuts: [],
    isInitialized: false,
    isLoading: true,
  });

  const fetchUserSettings = useCallback(async (userName: string) => {
    const [{ settings }, { shortcuts }] = await Promise.all([
      userServiceClient.listUserSettings({ parent: userName }),
      shortcutServiceClient.listShortcuts({ parent: userName }),
    ]);

    const generalSetting = settings.find((s) => s.value.case === "generalSetting");
    const webhooksSetting = settings.find((s) => s.value.case === "webhooksSetting");

    return {
      userGeneralSetting: generalSetting?.value.case === "generalSetting" ? generalSetting.value.value : undefined,
      userWebhooksSetting: webhooksSetting?.value.case === "webhooksSetting" ? webhooksSetting.value.value : undefined,
      shortcuts,
    };
  }, []);

  const initialize = useCallback(async () => {
    setState((prev) => ({ ...prev, isLoading: true }));
    try {
      // Check if we have a token/session before making API call.
      // - Prefer Clerk session (SSO)
      // - Fallback to legacy memo access token store
      const legacyToken = getAccessToken();
      const hasToken = !!(legacyToken);

      if (!hasToken) {
        // No token means user is not logged in - skip API call
        clearAccessToken();
        setState({
          currentUser: undefined,
          userGeneralSetting: undefined,
          userWebhooksSetting: undefined,
          shortcuts: [],
          isInitialized: true,
          isLoading: false,
        });
        return;
      }

      // We hit our new backend me endpoint manually because authServiceClient might still be rigid
      // Or we can just use fetch. We'll use fetch to make it completely explicit for the new API.
      const res = await fetch("/api/v1/auth/me", {
        headers: {
          Authorization: `Bearer ${legacyToken}`,
        },
      });

      if (!res.ok) {
        throw new Error("401 Unauthorized");
      }

      const currentUser = await res.json();

      // Normalize: ensure currentUser has a name field (for compatibility with proto types)
      if (!currentUser.name) {
        currentUser.name = `users/${currentUser.id}`;
      }

      // Skip fetchUserSettings for now — it requires proto-based API that hasn't been migrated
      // TODO: Implement /api/v1/users/{id}/settings endpoint
      const settings = { userGeneralSetting: undefined, userWebhooksSetting: undefined, shortcuts: [] };

      setState({
        currentUser,
        ...settings,
        isInitialized: true,
        isLoading: false,
      });

      // Pre-populate React Query cache
      queryClient.setQueryData(userKeys.currentUser(), currentUser);
      queryClient.setQueryData(userKeys.detail(currentUser.name), currentUser);
      // Invalidate memo queries so they refetch with the valid auth token.
      queryClient.invalidateQueries({ queryKey: memoKeys.all });
    } catch (error) {
      // Silently handle 401/403 - user is just not logged in
      const errorMessage = error instanceof Error ? error.message : String(error);
      const isUnauthorized = errorMessage.includes("401") ||
        errorMessage.includes("403") ||
        errorMessage.includes("Unauthorized") ||
        errorMessage.includes("Missing authentication token");

      if (!isUnauthorized) {
        // Only log/show errors for actual network issues
        console.error("Failed to initialize auth:", error);
        if (errorMessage.includes("NetworkError") || errorMessage.includes("kết nối")) {
          toast.error(errorMessage);
        }
      }

      clearAccessToken();
      setState({
        currentUser: undefined,
        userGeneralSetting: undefined,
        userWebhooksSetting: undefined,
        shortcuts: [],
        isInitialized: true,
        isLoading: false,
      });
    }
  }, [fetchUserSettings, queryClient]);

  const logout = useCallback(async () => {
    try {
      // In a real local JWT, backend might not have a stateful logout unless invalidating refresh token.
      // But we just clear it client side.
    } catch (error) {
      console.error("[AuthContext] Failed to sign out:", error);
    } finally {
      clearAccessToken();
      setState({
        currentUser: undefined,
        userGeneralSetting: undefined,
        userWebhooksSetting: undefined,
        shortcuts: [],
        isInitialized: true,
        isLoading: false,
      });
      queryClient.clear();
    }
  }, [queryClient]);

  const refetchSettings = useCallback(async () => {
    // Use functional setState to get current user without including state in dependencies
    setState((prev) => {
      if (!prev.currentUser) return prev;

      // Fetch settings asynchronously
      fetchUserSettings(prev.currentUser.name).then((settings) => {
        setState((current) => ({ ...current, ...settings }));
      });

      return prev;
    });
  }, [fetchUserSettings]);

  // Memoize context value to prevent unnecessary re-renders of consumers
  const value = useMemo(
    () => ({
      ...state,
      initialize,
      logout,
      refetchSettings,
    }),
    [state, initialize, logout, refetchSettings],
  );

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

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

// Convenience hook for just the current user
export function useCurrentUserFromAuth() {
  const { currentUser } = useAuth();
  return currentUser;
}
