import {useMutation, UseMutationResult, useQuery, useQueryClient} from '@tanstack/react-query'
import {GoogleSignin} from '@react-native-google-signin/google-signin'
import {User} from '@/models/IUser'
import * as SecureStore from 'expo-secure-store';

//region Mutations & queries

/**
 * ---------------------------------------------------------------------------------------------------------------------
 *                                          MUTATIONS & QUERIES
 * ---------------------------------------------------------------------------------------------------------------------
 */

export function useSignOut(): UseMutationResult<void, Error, void, void> {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: signOut,
    onSettled: () => queryClient.invalidateQueries({queryKey: ['currentUser']}),
  })
}

export function useSignInWithGoogle(): UseMutationResult<User | null, Error, void, void> {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: signInWithGoogle,
    onSettled: () => queryClient.invalidateQueries({queryKey: ['currentUser']}),
  })
}

export function useSignInWithEmail(): UseMutationResult<User | null, Error, SignInWithEmailParams, void> {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: signInWithEmail,
    onSettled: () => queryClient.invalidateQueries({queryKey: ['currentUser']}),
  })
}

export function useGetCurrentUser() {
  return useQuery({
    queryKey: ['currentUser'],
    queryFn: getCurrentUser,
    gcTime: Infinity,
    staleTime: Infinity,
  })
}

//endregion

//region API functions

/**
 * ---------------------------------------------------------------------------------------------------------------------
 *                                          API functions
 * ---------------------------------------------------------------------------------------------------------------------
 */

GoogleSignin.configure({webClientId: process.env.EXPO_PUBLIC_WEB_CLIENT_ID})

async function signInWithGoogle(): Promise<User | null> {
  // Check if your device supports Google Play
  await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true})

  // Get the users ID token
  const signInResult = await GoogleSignin.signIn()

  // Retrieve the ID Token
  const idToken = signInResult.data?.idToken

  if (!idToken) {
    throw new Error('No ID token found')
  }
  // Retrieve a JWT which can be authenticated on the backend.
  // TODO Change This IP to the domain where you host your backend.
  const response = await fetch('http://10.0.2.2:3000/api/auth/google', {
    headers: {
      Authorization: `Bearer ${idToken}`,
    },
  })

  if (!response.ok) return null

  const {token} = await ((await response.json()) as Promise<{token: string}>)

  await SecureStore.setItemAsync('token', token)

  return getUserFromToken(token)
}

interface SignInWithEmailParams {
  email: string
  password: string
}

async function signInWithEmail(params: SignInWithEmailParams): Promise<User | null> {
  // TODO Change This IP to the domain where you host your backend.
  const response = await fetch('http://10.0.2.2:3000/api/auth', {
    method: 'POST',
    body: JSON.stringify({
      username: params.email,
      password: params.password,
    }),
  })

  if (!response.ok) return null

  const {token} = await ((await response.json()) as Promise<{token: string}>)

  await SecureStore.setItemAsync('token', token)

  return getUserFromToken(token)
}


async function signOut(): Promise<void> {
  await GoogleSignin.signOut()
  await SecureStore.deleteItemAsync('token')
}

/**
 * Retrieve the current user from the token stored in the SecureStore.
 */
async function getCurrentUser(): Promise<User | null> {
  const token = await SecureStore.getItemAsync('token')
  if (!token) return null
  return getUserFromToken(token)
}

/**
 * Decode a token, get the payload and parse it as a User object.
 * @param token The token to decode.
 */
function getUserFromToken(token: string): User {
  const payload = token.split('.')[1]
  const decoded = atob(payload)
  return JSON.parse(decoded) as User
}

/**
 * Retrieve the JWT from the SecureStore.
 * This should be cached to reduce io operations.
 */
export function getToken(): Promise<string | null> {
  return SecureStore.getItemAsync('token')
}

//endregion
