/**
 * Profile management composable.
 *
 * Provides a higher-level API for profile operations including:
 * - Profile fetching and updates
 * - Linked account management (link/unlink)
 * - Session count tracking
 * - Loading and error state management
 *
 * Wraps the user store with additional functionality and cleaner error handling.
 */
import { ref, computed, readonly } from 'vue'

import { useUserStore } from '@/stores/user'
import { apiClient } from '@/api/client'
import { ApiError } from '@/api/types'
import { config } from '@/config'

/** OAuth provider types */
export type OAuthProvider = 'google' | 'discord'

/** Linked account information from the backend */
export interface LinkedAccount {
  provider: OAuthProvider
  providerUserId: string
  email: string | null
  linkedAt: string
}

/** Session information from the backend */
export interface SessionInfo {
  activeSessionCount: number
}

/** Result of link/unlink operations */
export interface LinkOperationResult {
  success: boolean
  error?: string
}

/** Result of profile update operations */
export interface UpdateProfileResult {
  success: boolean
  error?: string
}

/**
 * Profile management composable.
 *
 * Provides methods for viewing and updating user profile, managing linked
 * OAuth accounts, and viewing session information.
 *
 * @example
 * ```vue
 * <script setup lang="ts">
 * import { useProfile } from '@/composables/useProfile'
 *
 * const {
 *   profile,
 *   linkedAccounts,
 *   isLoading,
 *   fetchProfile,
 *   updateDisplayName,
 *   initiateLink,
 *   unlinkAccount,
 * } = useProfile()
 *
 * onMounted(() => fetchProfile())
 * </script>
 * ```
 */
export function useProfile() {
  const userStore = useUserStore()

  // Local state for operations not in store
  const sessionCount = ref<number | null>(null)
  const isLoadingLink = ref(false)
  const isLoadingUnlink = ref(false)
  const isLoadingSession = ref(false)
  const isUpdatingName = ref(false)
  const localError = ref<string | null>(null)

  // Computed from store
  const profile = computed(() => userStore.profile)
  const displayName = computed(() => userStore.displayName)
  const avatarUrl = computed(() => userStore.avatarUrl)
  const linkedAccounts = computed(() => userStore.linkedAccounts)
  const isLoading = computed(
    () =>
      userStore.isLoading ||
      isLoadingLink.value ||
      isLoadingUnlink.value ||
      isLoadingSession.value ||
      isUpdatingName.value
  )
  const error = computed(() => localError.value || userStore.error)

  /**
   * Fetch the user profile from the API.
   *
   * Updates the user store with fresh profile data including linked accounts.
   *
   * @returns Whether fetch succeeded
   */
  async function fetchProfile(): Promise<boolean> {
    localError.value = null
    return userStore.fetchProfile()
  }

  /**
   * Update the user's display name.
   *
   * @param newName - New display name (must be non-empty)
   * @returns Result indicating success/failure
   */
  async function updateDisplayName(newName: string): Promise<UpdateProfileResult> {
    if (!newName.trim()) {
      return { success: false, error: 'Display name cannot be empty' }
    }

    isUpdatingName.value = true
    localError.value = null

    try {
      const success = await userStore.updateDisplayName(newName.trim())
      if (!success) {
        return { success: false, error: userStore.error || 'Failed to update display name' }
      }
      return { success: true }
    } catch (e) {
      const errorMessage = e instanceof Error ? e.message : 'Failed to update display name'
      localError.value = errorMessage
      return { success: false, error: errorMessage }
    } finally {
      isUpdatingName.value = false
    }
  }

  /**
   * Initiate linking an OAuth provider.
   *
   * Redirects the user to the backend OAuth link endpoint. After OAuth,
   * the user is redirected back to /auth/link/callback with success/error.
   *
   * @param provider - OAuth provider to link ('google' or 'discord')
   */
  function initiateLink(provider: OAuthProvider): void {
    localError.value = null
    const redirectUri = encodeURIComponent(`${window.location.origin}/auth/link/callback`)
    window.location.href = `${config.apiBaseUrl}/api/auth/link/${provider}?redirect_uri=${redirectUri}`
  }

  /**
   * Unlink an OAuth provider from the account.
   *
   * Cannot unlink the last remaining provider (would lock user out).
   *
   * @param provider - OAuth provider to unlink
   * @returns Result indicating success/failure
   */
  async function unlinkAccount(provider: OAuthProvider): Promise<LinkOperationResult> {
    // Prevent unlinking last provider
    if (linkedAccounts.value.length <= 1) {
      return {
        success: false,
        error: 'Cannot unlink the only linked account. Link another provider first.',
      }
    }

    isLoadingUnlink.value = true
    localError.value = null

    try {
      await apiClient.delete(`/api/users/me/link/${provider}`)

      // Refresh profile to get updated linked accounts
      await userStore.fetchProfile()

      return { success: true }
    } catch (e) {
      let errorMessage = 'Failed to unlink account'

      if (e instanceof ApiError) {
        if (e.status === 400 || e.status === 409) {
          errorMessage = e.detail || 'Cannot unlink this account'
        } else {
          errorMessage = e.detail || e.message
        }
      } else if (e instanceof Error) {
        errorMessage = e.message
      }

      localError.value = errorMessage
      return { success: false, error: errorMessage }
    } finally {
      isLoadingUnlink.value = false
    }
  }

  /**
   * Fetch the number of active sessions.
   *
   * @returns Number of active sessions or null on error
   */
  async function fetchSessionCount(): Promise<number | null> {
    isLoadingSession.value = true
    localError.value = null

    try {
      const response = await apiClient.get<SessionInfo>('/api/users/me/sessions')
      sessionCount.value = response.activeSessionCount
      return response.activeSessionCount
    } catch (e) {
      // Session count is not critical - just log and return null
      console.warn('Failed to fetch session count:', e)
      return null
    } finally {
      isLoadingSession.value = false
    }
  }

  /**
   * Check if a provider is currently linked.
   *
   * @param provider - OAuth provider to check
   * @returns Whether the provider is linked
   */
  function isProviderLinked(provider: OAuthProvider): boolean {
    return linkedAccounts.value.some((acc) => acc.provider === provider)
  }

  /**
   * Get the linked account for a provider.
   *
   * @param provider - OAuth provider
   * @returns Linked account or undefined
   */
  function getLinkedAccount(provider: OAuthProvider): LinkedAccount | undefined {
    return linkedAccounts.value.find((acc) => acc.provider === provider) as
      | LinkedAccount
      | undefined
  }

  /**
   * Clear any error state.
   */
  function clearError(): void {
    localError.value = null
  }

  return {
    // State (readonly to prevent external mutation)
    profile,
    displayName,
    avatarUrl,
    linkedAccounts,
    sessionCount: readonly(sessionCount),
    isLoading,
    isUpdatingName: readonly(isUpdatingName),
    isLoadingLink: readonly(isLoadingLink),
    isLoadingUnlink: readonly(isLoadingUnlink),
    isLoadingSession: readonly(isLoadingSession),
    error,

    // Actions
    fetchProfile,
    updateDisplayName,
    initiateLink,
    unlinkAccount,
    fetchSessionCount,
    isProviderLinked,
    getLinkedAccount,
    clearError,
  }
}

export type UseProfile = ReturnType<typeof useProfile>
