import React, { createContext, useContext, useEffect, useState } from "react";
import * as AuthSession from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import { Platform } from "react-native";
import { Storage } from "./storage";
import { keycloakConfig } from "./keycloakConfig";
import { ActivityIndicator } from "react-native";

// Only call maybeCompleteAuthSession on web platform
if (Platform.OS === "web") {
  WebBrowser.maybeCompleteAuthSession();
}

const TOKEN_KEY = "kc_access_token";
const REFRESH_KEY = "kc_refresh_token";
const ID_TOKEN_KEY = "kc_id_token";

const AuthContext = createContext<any>(null);

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [discovery, setDiscovery] =
    useState<AuthSession.DiscoveryDocument | null>(null);
  const [accessToken, setAccessToken] = useState<string | null>(null);
  const [idToken, setIdToken] = useState<string | null>(null);
  const [refreshToken, setRefreshToken] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [isLoaded, setIsLoaded] = useState(false);

  const redirectUri = AuthSession.makeRedirectUri({
    scheme: keycloakConfig.scheme,
    path: Platform.OS === "web" ? "" : "(auth)/index",
  });

  useEffect(() => {
    const loadDiscovery = async () => {
      const doc = await AuthSession.fetchDiscoveryAsync(
        `${keycloakConfig.url}/realms/${keycloakConfig.realm}`
      );
      setDiscovery(doc);
    };
    loadDiscovery();
  }, []);

  useEffect(() => {
    const restore = async () => {
      const storedAccess = await Storage.getItem(TOKEN_KEY);
      const storedId = await Storage.getItem(ID_TOKEN_KEY);
      const storedRefresh = await Storage.getItem(REFRESH_KEY);

      if (storedAccess && storedRefresh) {
        setAccessToken(storedAccess);
        setIdToken(storedId);
        setRefreshToken(storedRefresh);
      }

      setLoading(false);
      setIsLoaded(true);
    };

    restore();
  }, []);

  const login = async () => {
    if (!discovery) return;

    const authRequest = new AuthSession.AuthRequest({
      responseType: AuthSession.ResponseType.Code,
      clientId: keycloakConfig.clientId,
      redirectUri,
      scopes: ["openid", "profile", "email", "offline_access"],
      usePKCE: true,
      prompt: AuthSession.Prompt.Login,
    });

    await authRequest.makeAuthUrlAsync(discovery);

    const result = await authRequest.promptAsync(discovery);

    if (result.type === "success") {
      const tokenResponse = await AuthSession.exchangeCodeAsync(
        {
          clientId: keycloakConfig.clientId,
          code: result.params.code,
          redirectUri,
          extraParams: {
            code_verifier: authRequest.codeVerifier || "",
          },
        },
        discovery
      );

      const { accessToken, idToken, refreshToken } = tokenResponse;

      setAccessToken(accessToken);
      setIdToken(idToken ?? null);
      setRefreshToken(refreshToken ?? null);

      await Storage.setItem(TOKEN_KEY, accessToken || "");
      if (idToken) await Storage.setItem(ID_TOKEN_KEY, idToken);
      if (refreshToken) await Storage.setItem(REFRESH_KEY, refreshToken);
    }
  };

  const refresh = async () => {
    if (!discovery || !refreshToken) return;

    const newToken = await AuthSession.refreshAsync(
      {
        clientId: keycloakConfig.clientId,
        refreshToken,
      },
      discovery
    );

    setAccessToken(newToken.accessToken || null);
    setIdToken(newToken.idToken || null);
    setRefreshToken(newToken.refreshToken || null);

    await Storage.setItem(TOKEN_KEY, newToken.accessToken || "");
    if (newToken.idToken) await Storage.setItem(ID_TOKEN_KEY, newToken.idToken);
    if (newToken.refreshToken)
      await Storage.setItem(REFRESH_KEY, newToken.refreshToken);
  };

  const logout = async () => {
    if (!discovery || !accessToken) return;

    const clientId = keycloakConfig.clientId;
    const logOutRedirectUri = AuthSession.makeRedirectUri({
      scheme: keycloakConfig.scheme,
      path: Platform.OS === "web" ? "" : "home",
    });
    const logoutUrl = `${
      discovery.endSessionEndpoint
    }?client_id=${clientId}&post_logout_redirect_uri=${encodeURIComponent(
      logOutRedirectUri
    )}&id_token_hint=${idToken}`;

    const result = await WebBrowser.openAuthSessionAsync(
      logoutUrl,
      logOutRedirectUri
    );

    if (result.type === "success") {
      setAccessToken(null);
      setIdToken(null);
      setRefreshToken(null);

      await Storage.deleteItem(TOKEN_KEY);
      await Storage.deleteItem(ID_TOKEN_KEY);
      await Storage.deleteItem(REFRESH_KEY);
    }
  };

  if (!discovery || loading) return <ActivityIndicator />;

  return (
    <AuthContext.Provider
      value={{
        accessToken,
        idToken,
        isLoaded,
        refreshToken,
        login,
        logout,
        refresh,
        isAuthenticated: !!accessToken,
        loading,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

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