const RESOURCES = {
  teams: 'https://teams.optovia.ru',
  exchange: 'https://exchange.optovia.ru',
  orders: 'https://orders.optovia.ru',
  kyc: 'https://kyc.optovia.ru',
  billing: 'https://billing.optovia.ru'
} as const

type ResourceKey = keyof typeof RESOURCES
type ResourceUrl = typeof RESOURCES[ResourceKey]

interface TokenInfo {
  token: string
  expiresAt: number
}

interface RefreshResponse {
  tokens: Partial<Record<ResourceKey, TokenInfo>>
}

// Buffer before expiry to trigger refresh (60 seconds)
const EXPIRY_BUFFER_MS = 60 * 1000

/**
 * Composable for managing Logto access tokens and ID token.
 * Uses useState for SSR→client sync and auto-refreshes when tokens expire.
 */
export const useLogtoTokens = () => {
  const tokens = useState<Partial<Record<ResourceKey, TokenInfo>>>('logto-tokens', () => ({}))
  const idToken = useState<string | null>('logto-id-token', () => null)
  const isRefreshing = ref(false)
  let refreshPromise: Promise<void> | null = null

  /**
   * Get organization ID from Logto user (first organization)
   */
  const getOrganizationId = (): string | undefined => {
    if (import.meta.server) {
      const nuxtApp = useNuxtApp()
      const context = nuxtApp.ssrContext?.event.context as {
        logtoUser?: { organizations?: string[] }
        logtoOrgId?: string
      } | undefined
      return context?.logtoOrgId || context?.logtoUser?.organizations?.[0]
    }
    const orgId = useState<string | null>('logto-org-id', () => null)
    return orgId.value || undefined
  }

  /**
   * Initialize tokens on SSR - fetches all tokens from Logto client
   */
  const initTokens = async () => {
    if (import.meta.server) {
      const nuxtApp = useNuxtApp()
      const client = nuxtApp.ssrContext?.event.context.logtoClient
      const organizationId = getOrganizationId()

      if (client) {
        const results: Partial<Record<ResourceKey, TokenInfo>> = {}

        await Promise.all(
          (Object.entries(RESOURCES) as [ResourceKey, string][]).map(async ([key, resource]) => {
            try {
              // Pass organizationId to get organization-scoped token
              const token = await client.getAccessToken(resource, organizationId)
              if (token) {
                results[key] = {
                  token,
                  expiresAt: decodeTokenExpiry(token)
                }
              }
            }
            catch {
              // Token not available
            }
          })
        )

        tokens.value = results

        // Also fetch ID token for SSR
        try {
          const token = await client.getIdToken()
          if (token) {
            idToken.value = token
          }
        }
        catch {
          // ID token not available
        }
      }
    }
  }

  /**
   * Decode JWT to get expiry timestamp
   */
  function decodeTokenExpiry(token: string): number {
    try {
      const payload = token.split('.')[1]
      if (payload) {
        const json = atob(payload.replace(/-/g, '+').replace(/_/g, '/'))
        const decoded = JSON.parse(json)
        if (decoded.exp) {
          return decoded.exp * 1000
        }
      }
    }
    catch {
      // ignore
    }
    return Date.now() + 3600 * 1000
  }

  /**
   * Check if token is expired or about to expire
   */
  const isTokenExpired = (tokenInfo: TokenInfo | undefined): boolean => {
    if (!tokenInfo) return true
    return tokenInfo.expiresAt <= Date.now() + EXPIRY_BUFFER_MS
  }

  /**
   * Refresh all tokens from server
   */
  const refreshTokens = async (): Promise<void> => {
    // Deduplicate concurrent refresh calls
    if (refreshPromise) {
      return refreshPromise
    }

    isRefreshing.value = true

    refreshPromise = (async () => {
      try {
        const response = await $fetch<RefreshResponse>('/api/auth/refresh', {
          method: 'POST'
        })
        tokens.value = response.tokens
      }
      finally {
        isRefreshing.value = false
        refreshPromise = null
      }
    })()

    return refreshPromise
  }

  /**
   * Get access token for a resource URL.
   * Auto-refreshes if token is expired.
   */
  const getToken = async (resourceUrl: ResourceUrl): Promise<string> => {
    // Find resource key by URL
    const entry = Object.entries(RESOURCES).find(([, url]) => url === resourceUrl)
    if (!entry) {
      throw new Error(`Unknown resource: ${resourceUrl}`)
    }
    const key = entry[0] as ResourceKey

    const tokenInfo = tokens.value[key]

    // If expired, refresh all tokens
    if (isTokenExpired(tokenInfo)) {
      await refreshTokens()
    }

    const refreshedToken = tokens.value[key]
    if (!refreshedToken?.token) {
      throw new Error(`No token available for ${resourceUrl}`)
    }

    return refreshedToken.token
  }

  /**
   * Get token by resource key (teams, exchange, orders, kyc)
   */
  const getTokenByKey = async (key: ResourceKey): Promise<string> => {
    const tokenInfo = tokens.value[key]

    if (isTokenExpired(tokenInfo)) {
      await refreshTokens()
    }

    const refreshedToken = tokens.value[key]
    if (!refreshedToken?.token) {
      throw new Error(`No token available for ${key}`)
    }

    return refreshedToken.token
  }

  return {
    tokens: readonly(tokens),
    idToken: readonly(idToken),
    isRefreshing: readonly(isRefreshing),
    initTokens,
    getToken,
    getTokenByKey,
    refreshTokens
  }
}
