import { ref } from 'vue'
import { formatMessage, messages } from '../lib/messages'
import type { AuthState, HealthState } from '../lib/models'
import { parseSpotifyUrl } from '../lib/spotifyUrl'
import type { CurrentUser, PlaylistCreateRequest } from '../lib/types'

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function parseCurrentUser(value: unknown): CurrentUser {
  if (!isRecord(value) || typeof value.display_name !== 'string') {
    throw new Error(messages.useApiInvalidCurrentUser)
  }
  return { display_name: value.display_name }
}

function parsePlaylistResponse(value: unknown): { url: string | null } {
  if (!isRecord(value) || typeof value.url !== 'string') {
    throw new Error(messages.useApiInvalidPlaylist)
  }
  return { url: parseSpotifyUrl(value.url) }
}

function loginFailed(): boolean {
  const url = new URL(window.location.href)
  const failed = url.searchParams.get('login') === 'error'
  if (failed) {
    url.searchParams.delete('login')
    window.history.replaceState({}, '', `${url.pathname}${url.search}${url.hash}`)
  }
  return failed
}

/** Manage authentication, service mode, and playlist API operations. */
export function useApi() {
  const auth = ref<AuthState>({ status: 'checking', user: null, message: null })
  const health = ref<HealthState>({ status: 'checking', mode: null, message: null })

  async function loadAuth(hadLoginError: boolean): Promise<void> {
    try {
      const response = await fetch('/api/auth/me', { credentials: 'same-origin' })
      if (response.status === 401) {
        if (hadLoginError) {
          auth.value = {
            status: 'failed',
            user: null,
            message: messages.useApiLoginFailed,
          }
        } else {
          auth.value = { status: 'anonymous', user: null, message: null }
        }
        return
      }
      if (!response.ok) throw new Error(messages.useApiAuthenticationCheckFailed)
      auth.value = {
        status: 'authenticated',
        user: parseCurrentUser(await response.json()),
        message: null,
      }
    } catch {
      let message: string = messages.useApiConnectionUnavailable
      if (hadLoginError) message = messages.useApiLoginFailed
      auth.value = {
        status: 'failed',
        user: null,
        message,
      }
    }
  }

  async function loadHealth(): Promise<void> {
    try {
      const response = await fetch('/api/health', { credentials: 'same-origin' })
      if (!response.ok) throw new Error(messages.useApiHealthCheckFailed)
      const body: unknown = await response.json()
      if (
        !isRecord(body) ||
        body.status !== 'ok' ||
        (body.mode !== 'demo' && body.mode !== 'live')
      ) {
        throw new Error(messages.useApiInvalidHealth)
      }
      health.value = { status: 'ready', mode: body.mode, message: null }
    } catch {
      health.value = {
        status: 'failed',
        mode: null,
        message: messages.useApiModeUnavailable,
      }
    }
  }

  async function bootstrap(): Promise<void> {
    const hadLoginError = loginFailed()
    await Promise.all([loadAuth(hadLoginError), loadHealth()])
  }

  async function logout(): Promise<void> {
    if (auth.value.status !== 'authenticated') return
    const user = auth.value.user
    auth.value = { status: 'logging_out', user, message: null }
    try {
      const response = await fetch('/api/auth/logout', {
        method: 'POST',
        credentials: 'same-origin',
      })
      if (!response.ok) throw new Error(messages.useApiLogoutFailed)
      auth.value = { status: 'anonymous', user: null, message: null }
    } catch {
      auth.value = {
        status: 'failed',
        user: null,
        message: messages.useApiSpotifyLogoutFailed,
      }
    }
  }

  async function createPlaylist(request: PlaylistCreateRequest): Promise<{ url: string | null }> {
    const response = await fetch('/api/playlists', {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(request),
    })
    if (!response.ok) {
      throw new Error(formatMessage('useApiPlaylistCreationFailed', { status: response.status }))
    }
    return parsePlaylistResponse(await response.json())
  }

  return { auth, health, bootstrap, logout, createPlaylist }
}
