import {useCallback, useRef} from 'react'

import {
  GeneratedTokenData,
  UserTokenGenerateUserErrors,
} from '@shopify/shop-minis-platform'

import {useHandleAction} from '../../internal/useHandleAction'
import {useShopActions} from '../../internal/useShopActions'

interface UseGenerateUserTokenReturns {
  /**
   * Generates a temporary token for the user.
   * Tokens are cached in memory and reused if still valid (with a 5-minute expiry buffer).
   * A new token is automatically generated when the cached token is expired or missing.
   */
  generateUserToken: () => Promise<{
    data: GeneratedTokenData
    userErrors?: UserTokenGenerateUserErrors[]
  }>
}

interface CachedTokenResponse {
  data: GeneratedTokenData
  userErrors?: UserTokenGenerateUserErrors[]
}

export function useGenerateUserToken(): UseGenerateUserTokenReturns {
  const {generateUserToken: generateUserTokenAction} = useShopActions()
  const wrappedGenerateToken = useHandleAction(generateUserTokenAction)

  const cachedResponse = useRef<CachedTokenResponse | null>(null)
  const pendingRequest = useRef<Promise<CachedTokenResponse> | null>(null)

  const isTokenValid = useCallback(
    (response: CachedTokenResponse | null): boolean => {
      if (!response?.data?.token || !response.data.expiresAt) {
        return false
      }

      try {
        const expiryTime = new Date(response.data.expiresAt).getTime()
        const now = Date.now()
        // 5 minutes buffer to ensure token doesn't expire mid-request
        const bufferTime = 5 * 60 * 1000

        return expiryTime - bufferTime > now
      } catch {
        // If date parsing fails, consider token invalid
        return false
      }
    },
    []
  )

  const generateUserToken =
    useCallback(async (): Promise<CachedTokenResponse> => {
      // Check if cached token exists and is still valid
      if (cachedResponse.current && isTokenValid(cachedResponse.current)) {
        return cachedResponse.current
      }

      // If there's already a pending request, return the same promise
      if (pendingRequest.current) {
        return pendingRequest.current
      }

      // Create new request and store the promise
      pendingRequest.current = (async () => {
        try {
          const response = await wrappedGenerateToken()

          // Only cache if we got a valid token
          if (response.data?.token && response.data?.expiresAt) {
            cachedResponse.current = response
          }

          return response
        } catch (error) {
          // Clear cache on error to ensure fresh token on next attempt
          cachedResponse.current = null
          throw error
        } finally {
          // Clear pending request after completion (success or failure)
          pendingRequest.current = null
        }
      })()

      return pendingRequest.current
    }, [wrappedGenerateToken, isTokenValid])

  return {
    generateUserToken,
  }
}
