import type {
  InternalOptions,
  RequestInternal,
  ResponseInternal,
  User,
} from "../../types.js"
import type { Cookie, SessionStore } from "../utils/cookie.js"
import { getLoggedInUser } from "../utils/session.js"
import {
  assertInternalOptionsWebAuthn,
  inferWebAuthnOptions,
  getAuthenticationResponse,
  getRegistrationResponse,
} from "../utils/webauthn-utils.js"

/**
 * Returns authentication or registration options for a WebAuthn flow
 * depending on the parameters provided.
 */
export async function webAuthnOptions(
  request: RequestInternal,
  options: InternalOptions,
  sessionStore: SessionStore,
  cookies: Cookie[]
  // @ts-expect-error issue with returning from a switch case
): Promise<ResponseInternal> {
  // Return an error if the adapter is missing or if the provider
  // is not a webauthn provider.
  const narrowOptions = assertInternalOptionsWebAuthn(options)
  const { provider } = narrowOptions

  // Extract the action from the query parameters
  const { action } = (request.query ?? {}) as Record<string, unknown>

  // Action must be either "register", "authenticate", or undefined
  if (
    action !== "register" &&
    action !== "authenticate" &&
    typeof action !== "undefined"
  ) {
    return {
      status: 400,
      body: { error: "Invalid action" },
      cookies,
      headers: {
        "Content-Type": "application/json",
      },
    }
  }

  // Get the user info from the session
  const sessionUser = await getLoggedInUser(options, sessionStore)

  // Extract user info from request
  // If session user exists, we don't need to call getUserInfo
  const getUserInfoResponse = sessionUser
    ? {
        user: sessionUser,
        exists: true,
      }
    : await provider.getUserInfo(options, request)

  const userInfo = getUserInfoResponse?.user

  // Make a decision on what kind of webauthn options to return
  const decision = inferWebAuthnOptions(
    action,
    !!sessionUser,
    getUserInfoResponse
  )

  switch (decision) {
    case "authenticate":
      return getAuthenticationResponse(
        narrowOptions,
        request,
        userInfo,
        cookies
      )
    case "register":
      if (typeof userInfo?.email === "string") {
        return getRegistrationResponse(
          narrowOptions,
          request,
          userInfo as User & { email: string },
          cookies
        )
      }
      break
    default:
      return {
        status: 400,
        body: { error: "Invalid request" },
        cookies,
        headers: {
          "Content-Type": "application/json",
        },
      }
  }
}
