// useAuth.tsx
import { AuthContext, type UserInfo } from '@/contexts/AuthContext';
import type { GenerateTokenResponse } from '@/features/authentication/api/identityAPI';
import axios from 'axios';
import { useEffect, useState, type ReactNode } from 'react';
import { jwtDecode } from 'jwt-decode';
import type { GUID } from '@/types/commonTypes';

export interface AccessTokenJwtPayload {
  iss: string;
  nbf: number;
  iat: number;
  exp: number;
  scope: string[];
  amr: string[];
  client_id: string;
  sub: GUID;
  auth_time: number;
  idp: string;
  picture: string;
  given_name: string;
  family_name: string;
  email: string;
  name: string;
  phone_number: string;
  session: GUID;
  jti: string;
}

export const ACCESS_TOKEN_KEY: 'access_token' = 'access_token' as const;
export const REFRESH_TOKEN_KEY: 'refresh_token' = 'refresh_token' as const;
export const EXPIRES_IN_KEY: 'expires_in' = 'expires_in' as const;

let inMemoryToken: string | null = null;
let expiresAt = 0;

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
  const [authFinished, setAuthFinished] = useState<boolean>(false);
  const [accessToken, setAccessToken] = useState<string | null>(
    sessionStorage.getItem(ACCESS_TOKEN_KEY),
  );

  // Initialize from sessionStorage (page reload)
  useEffect(() => {
    const handleAndSetToken = async () => {
      const token = sessionStorage.getItem(ACCESS_TOKEN_KEY);

      if (token) {
        setUserInfoFromToken(token);
        await refreshAccessToken();
        inMemoryToken = token;
        expiresAt = Number(sessionStorage.getItem(EXPIRES_IN_KEY) || 0);
        setAccessToken(token);

        setAuthFinished(true);
      }

      setAuthFinished(true);
    };

    handleAndSetToken();
  }, []);

  // Background refresh
  useEffect(() => {
    const handleRefreshTokenIfNeeded = async () => {
      if (!inMemoryToken) return;
      if (Date.now() < expiresAt - 60_000) return; // still valid (buffer)

      await refreshAccessToken();
      setAccessToken(inMemoryToken);
    };

    const interval = setInterval(async () => {
      await handleRefreshTokenIfNeeded();
    }, 30_000);

    return () => clearInterval(interval);
  }, []);

  function login(tokens: {
    access_token: string;
    refresh_token: string;
    expires_in: number;
  }) {
    setUserInfoFromToken(tokens.access_token);
    inMemoryToken = tokens.access_token;
    expiresAt = Date.now() + tokens.expires_in * 1000;

    sessionStorage.setItem(ACCESS_TOKEN_KEY, tokens.access_token);
    sessionStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
    sessionStorage.setItem(EXPIRES_IN_KEY, String(expiresAt));

    setAccessToken(tokens.access_token);
  }

  function logout() {
    inMemoryToken = null;
    expiresAt = 0;
    sessionStorage.clear();
    setAccessToken(null);
  }

  function getToken() {
    return inMemoryToken;
  }

  async function refreshAccessToken() {
    const refreshToken = sessionStorage.getItem(REFRESH_TOKEN_KEY);
    if (!refreshToken) {
      logout();
      return;
    }

    try {
      const activeRefreshSession: string | null =
        sessionStorage.getItem('active-refresh');

      if (
        !activeRefreshSession ||
        (activeRefreshSession && JSON.parse(activeRefreshSession) === false)
      ) {
        sessionStorage.setItem('active-refresh', JSON.stringify(true));

        const result = await axios.post<GenerateTokenResponse>(
          import.meta.env.VITE_IDENTITY_URL,
          new URLSearchParams({
            grant_type: 'refresh_token',
            refresh_token: refreshToken,
            client_id: import.meta.env.VITE_IDENTITY_CLIENT_ID, // from your token payload
          }),
          {
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
            },
          },
        );

        if (result.data.access_token) {
          inMemoryToken = result.data.access_token;
          expiresAt = Date.now() + result.data.expires_in * 1000;

          sessionStorage.setItem(ACCESS_TOKEN_KEY, inMemoryToken as string);
          sessionStorage.setItem(REFRESH_TOKEN_KEY, result.data.refresh_token);
          sessionStorage.setItem(EXPIRES_IN_KEY, String(expiresAt));

          setAccessToken(inMemoryToken);
        } else {
          logout();
        }

        sessionStorage.setItem('active-refresh', JSON.stringify(false));
      }
    } catch {
      logout();
    }
  }

  function setUserInfoFromToken(token: string) {
    const accessTokenPayload = jwtDecode<AccessTokenJwtPayload>(token);

    const userInfo: UserInfo = {
      picture: accessTokenPayload.picture,
      firstName: accessTokenPayload.given_name,
      lastName: accessTokenPayload.family_name,
      fullName: accessTokenPayload.name,
      email: accessTokenPayload.email,
      phoneNumber: accessTokenPayload.phone_number,
      userID: accessTokenPayload.sub,
    };

    setUserInfo(userInfo);
  }

  return (
    <AuthContext.Provider
      value={{
        accessToken,
        getToken,
        login,
        logout,
        authFinished,
        userInfo: userInfo as UserInfo,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};
