/**
 * Authentication store for OAuth-based login.
 *
 * Manages access tokens, refresh tokens, and basic user info.
 * The backend uses OAuth (Google/Discord) - no username/password.
 */
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

import { config } from '@/config'

export interface User {
  id: string
  displayName: string
  avatarUrl: string | null
  hasStarterDeck: boolean
}

export interface AuthTokens {
  accessToken: string
  refreshToken: string
  expiresAt: number
}

export const useAuthStore = defineStore('auth', () => {
  // State
  const user = ref<User | null>(null)
  const accessToken = ref<string | null>(null)
  const refreshToken = ref<string | null>(null)
  const expiresAt = ref<number | null>(null)
  const isLoading = ref(false)
  const error = ref<string | null>(null)

  // Getters
  const isAuthenticated = computed(() => !!accessToken.value)

  const isTokenExpired = computed(() => {
    if (!expiresAt.value) return true
    // Consider expired 30 seconds before actual expiry for safety
    return Date.now() >= expiresAt.value - 30000
  })

  /**
   * Set tokens after OAuth callback.
   *
   * Called from AuthCallbackPage after extracting tokens from URL fragment.
   */
  function setTokens(tokens: AuthTokens): void {
    accessToken.value = tokens.accessToken
    refreshToken.value = tokens.refreshToken
    expiresAt.value = tokens.expiresAt
  }

  /**
   * Set user data after fetching profile.
   */
  function setUser(userData: User): void {
    user.value = userData
  }

  /**
   * Refresh the access token using the refresh token.
   *
   * Returns true if refresh succeeded, false otherwise.
   * On failure, user is logged out.
   */
  async function refreshAccessToken(): Promise<boolean> {
    if (!refreshToken.value) {
      logout()
      return false
    }

    isLoading.value = true
    error.value = null

    try {
      const response = await fetch(`${config.apiBaseUrl}/api/auth/refresh`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refresh_token: refreshToken.value }),
      })

      if (!response.ok) {
        throw new Error('Token refresh failed')
      }

      const data = await response.json()
      accessToken.value = data.access_token
      // Refresh token may or may not be rotated
      if (data.refresh_token) {
        refreshToken.value = data.refresh_token
      }
      // Calculate expiry (backend should send expires_in in seconds)
      if (data.expires_in) {
        expiresAt.value = Date.now() + data.expires_in * 1000
      }

      return true
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Token refresh failed'
      logout()
      return false
    } finally {
      isLoading.value = false
    }
  }

  /**
   * Get a valid access token, refreshing if needed.
   *
   * Returns null if not authenticated or refresh fails.
   */
  async function getValidToken(): Promise<string | null> {
    if (!accessToken.value) return null

    if (isTokenExpired.value) {
      const success = await refreshAccessToken()
      if (!success) return null
    }

    return accessToken.value
  }

  /**
   * Log out the user.
   *
   * Optionally calls the backend to revoke the refresh token.
   */
  async function logout(revokeOnServer = true): Promise<void> {
    if (revokeOnServer && refreshToken.value) {
      try {
        await fetch(`${config.apiBaseUrl}/api/auth/logout`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${accessToken.value}`,
          },
        })
      } catch {
        // Ignore errors - we're logging out anyway
      }
    }

    // Clear state
    user.value = null
    accessToken.value = null
    refreshToken.value = null
    expiresAt.value = null
    error.value = null
  }

  /**
   * Initialize auth state on app startup.
   *
   * Tokens are persisted via pinia-plugin-persistedstate.
   * If we have tokens, validate them by refreshing.
   */
  async function init(): Promise<void> {
    if (accessToken.value && refreshToken.value) {
      // Try to refresh to validate tokens
      if (isTokenExpired.value) {
        await refreshAccessToken()
      }
    }
  }

  /**
   * Get OAuth login URL for a provider.
   *
   * Includes the redirect_uri so the backend knows where to send
   * the user after OAuth completes.
   */
  function getOAuthUrl(provider: 'google' | 'discord'): string {
    const redirectUri = encodeURIComponent(config.oauthRedirectUri)
    return `${config.apiBaseUrl}/api/auth/${provider}?redirect_uri=${redirectUri}`
  }

  return {
    // State
    user,
    accessToken,
    refreshToken,
    expiresAt,
    isLoading,
    error,
    // Getters
    isAuthenticated,
    isTokenExpired,
    // Actions
    setTokens,
    setUser,
    refreshAccessToken,
    getValidToken,
    logout,
    init,
    getOAuthUrl,
  }
}, {
  persist: {
    pick: ['accessToken', 'refreshToken', 'expiresAt', 'user'],
  },
})
