import { Buffer } from 'node:buffer';
import { env } from '$env/dynamic/private';
import { error, json } from '@sveltejs/kit';
import type { GenerateRegistrationOptionsOpts } from '@simplewebauthn/server';
import { generateRegistrationOptions } from '@simplewebauthn/server';
import { errorResponse } from '$lib/server/utilities';
import { getAuthSessionIdFromCookie, resolveUserId } from '$lib/server/auth/utilities';
import { listAuthenticatorsForUser } from '$lib/server/data/authentication/authenticator';
import { createChallenge } from '$lib/server/data/authentication/challenge';
import { findUserByIdentifier } from '$lib/server/data/authentication/user';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async function handler({ url, cookies, locals: { database } }) {
  const sessionId = getAuthSessionIdFromCookie(cookies);

  if (!sessionId) {
    throw error(403, 'Not authorized');
  }

  const userId = resolveUserId(cookies);

  if (!userId) {
    return errorResponse(401, 'Not authenticated');
  }

  const user = await findUserByIdentifier(database, userId);
  const authenticators = await listAuthenticatorsForUser(database, user);
  const options = await generateRegistrationOptions({
    rpName: env.FIDO_NAME || 'Kiosk',
    rpID: url.hostname,

    userID: user.id,
    userName: user.email,
    userDisplayName: user.name || user.email,

    timeout: 60_000,
    attestationType: 'none',

    /**
     * Passing in a user's list of already-registered authenticator IDs here prevents users from
     * registering the same device multiple times. The authenticator will simply throw an error in
     * the browser if it's asked to perform registration when one of these ID's already resides
     * on it.
     */
    excludeCredentials: authenticators.map(({ identifier, transports }) => ({
      id: Buffer.from(identifier, 'base64url'),
      type: 'public-key',
      transports
    })),

    /**
     * The optional authenticatorSelection property allows for specifying more the types of
     * authenticators that users to can use for registration
     */
    authenticatorSelection: {
      residentKey: 'required',
      userVerification: 'preferred'
    },

    /**
     * Support the two most common algorithms: ES256, and RS256
     */
    supportedAlgorithmIDs: [-7, -257]
  } satisfies GenerateRegistrationOptionsOpts);
  const timeout = options.timeout || 60_000;

  /**
   * The server needs to temporarily remember this value for verification, so don't lose it until
   * after you verify an authenticator response.
   */
  await createChallenge(database, {
    challenge: options.challenge,
    expires_at: new Date(+new Date() + timeout),
    session_identifier: sessionId,
  });

  return json(options);
};