// server/utils/cidaas.ts

/**
 * Cidaas API Client for OAuth2/OIDC integration
 *
 * Provides functions to interact with Cidaas endpoints:
 * - Token exchange (authorization code → access/ID tokens)
 * - UserInfo fetch
 * - User registration
 */

import type { H3Error } from 'h3'

/**
 * Cidaas Token Response
 */
export interface CidaasTokenResponse {
  access_token: string
  token_type: string
  expires_in: number
  refresh_token?: string
  id_token: string // JWT with user identity
  scope: string
}

/**
 * Cidaas UserInfo Response
 */
export interface CidaasUserInfo {
  sub: string // Unique user ID (experimenta_id)
  email: string
  email_verified: boolean
  given_name?: string
  family_name?: string
  name?: string
  phone_number?: string
  updated_at?: number
}

/**
 * Cidaas Registration Request
 */
export interface CidaasRegistrationRequest {
  email: string
  password: string
  given_name: string
  family_name: string
  locale?: string
}

/**
 * Exchange authorization code for access/ID tokens
 *
 * @param code - Authorization code from callback
 * @param codeVerifier - PKCE code verifier
 * @returns Token response
 * @throws H3Error if exchange fails
 */
export async function exchangeCodeForToken(
  code: string,
  codeVerifier: string
): Promise<CidaasTokenResponse> {
  const config = useRuntimeConfig()

  // Prepare token request
  const params = new URLSearchParams({
    grant_type: 'authorization_code',
    code,
    redirect_uri: config.cidaas.redirectUri,
    client_id: config.cidaas.clientId,
    client_secret: config.cidaas.clientSecret,
    code_verifier: codeVerifier, // PKCE proof
  })

  try {
    const response = await fetch(config.cidaas.tokenUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params.toString(),
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}))
      console.error('Cidaas token exchange failed:', errorData)

      throw createError({
        statusCode: response.status,
        statusMessage: 'Token exchange failed',
        data: errorData,
      })
    }

    const tokens: CidaasTokenResponse = await response.json()
    return tokens
  } catch (error) {
    console.error('Token exchange error:', error)

    if ((error as H3Error).statusCode) {
      throw error // Re-throw H3Error
    }

    throw createError({
      statusCode: 500,
      statusMessage: 'Failed to exchange authorization code',
    })
  }
}

/**
 * Fetch user info from Cidaas UserInfo endpoint
 *
 * @param accessToken - OAuth2 access token
 * @returns User profile data
 * @throws H3Error if fetch fails
 */
export async function fetchUserInfo(accessToken: string): Promise<CidaasUserInfo> {
  const config = useRuntimeConfig()

  try {
    const response = await fetch(config.cidaas.userinfoUrl, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    })

    if (!response.ok) {
      console.error('Cidaas UserInfo fetch failed:', response.status)

      throw createError({
        statusCode: response.status,
        statusMessage: 'Failed to fetch user info',
      })
    }

    const userInfo: CidaasUserInfo = await response.json()
    return userInfo
  } catch (error) {
    console.error('UserInfo fetch error:', error)

    if ((error as H3Error).statusCode) {
      throw error
    }

    throw createError({
      statusCode: 500,
      statusMessage: 'Failed to fetch user information',
    })
  }
}

/**
 * Register new user via Cidaas Registration API
 *
 * @param data - Registration data
 * @returns Success indicator (user must verify email before login)
 * @throws H3Error if registration fails
 */
export async function registerUser(
  data: CidaasRegistrationRequest
): Promise<{ success: boolean; message: string }> {
  const config = useRuntimeConfig()

  // Cidaas registration endpoint (adjust based on actual API)
  const registrationUrl = `${config.cidaas.issuer}/users-srv/register`

  try {
    const response = await fetch(registrationUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: data.email,
        password: data.password,
        given_name: data.given_name,
        family_name: data.family_name,
        locale: data.locale || 'de',
      }),
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}))
      console.error('Cidaas registration failed:', errorData)

      // Handle specific errors
      if (response.status === 409) {
        throw createError({
          statusCode: 409,
          statusMessage: 'Email already registered',
        })
      }

      throw createError({
        statusCode: response.status,
        statusMessage: 'Registration failed',
        data: errorData,
      })
    }

    return {
      success: true,
      message: 'Registration successful. Please verify your email.',
    }
  } catch (error) {
    console.error('Registration error:', error)

    if ((error as H3Error).statusCode) {
      throw error
    }

    throw createError({
      statusCode: 500,
      statusMessage: 'Failed to register user',
    })
  }
}

/**
 * Login with username and password (Resource Owner Password Credentials Flow)
 *
 * @param email - User email address
 * @param password - User password
 * @returns Token response with access_token and id_token
 * @throws H3Error if login fails
 */
export async function loginWithPassword(
  email: string,
  password: string
): Promise<CidaasTokenResponse> {
  const config = useRuntimeConfig()

  // Prepare token request with password grant
  const params = new URLSearchParams({
    grant_type: 'password',
    username: email, // Cidaas uses 'username' field for email
    password,
    client_id: config.cidaas.clientId,
    client_secret: config.cidaas.clientSecret,
    scope: 'openid profile email', // Request OIDC scopes
  })

  try {
    const response = await fetch(config.cidaas.tokenUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params.toString(),
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}))
      console.error('Cidaas password login failed:', errorData)

      // Handle specific errors
      if (response.status === 401) {
        throw createError({
          statusCode: 401,
          statusMessage: 'Invalid email or password',
        })
      }

      throw createError({
        statusCode: response.status,
        statusMessage: 'Login failed',
        data: errorData,
      })
    }

    const tokens: CidaasTokenResponse = await response.json()
    return tokens
  } catch (error) {
    console.error('Password login error:', error)

    if ((error as H3Error).statusCode) {
      throw error // Re-throw H3Error
    }

    throw createError({
      statusCode: 500,
      statusMessage: 'Failed to authenticate with Cidaas',
    })
  }
}

/**
 * Refresh access token using refresh token
 *
 * @param refreshToken - Refresh token from previous login
 * @returns New token response
 * @throws H3Error if refresh fails
 */
export async function refreshAccessToken(refreshToken: string): Promise<CidaasTokenResponse> {
  const config = useRuntimeConfig()

  const params = new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: refreshToken,
    client_id: config.cidaas.clientId,
    client_secret: config.cidaas.clientSecret,
  })

  try {
    const response = await fetch(config.cidaas.tokenUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params.toString(),
    })

    if (!response.ok) {
      throw createError({
        statusCode: response.status,
        statusMessage: 'Token refresh failed',
      })
    }

    const tokens: CidaasTokenResponse = await response.json()
    return tokens
  } catch (error) {
    console.error('Token refresh error:', error)

    if ((error as H3Error).statusCode) {
      throw error
    }

    throw createError({
      statusCode: 500,
      statusMessage: 'Failed to refresh token',
    })
  }
}
