import {
  IPasskeyAuthenticateOptions,
  IPasskeyCreationOptions,
  PasskeyPlusOptions,
} from "./types";
import {
  startRegistration,
  startAuthentication,
} from "@simplewebauthn/browser";

export class PasskeyPlus {
  private baseUrl: string;

  constructor(options: PasskeyPlusOptions) {
    const { tenantDomain, appId } = options;
    this.baseUrl = `https://${tenantDomain}/api/v1/passkey-plus-public/${appId}`;
  }

  async register(
    transactionID: string,
    opts?: IPasskeyCreationOptions
  ): Promise<string> {
    const publicKey = await this.getRegistrationOptions(transactionID);

    if (opts?.authenticatorAttachment) {
      publicKey.authenticatorSelection = {
        ...publicKey.authenticatorSelection,
        authenticatorAttachment: opts.authenticatorAttachment,
      };
    }

    const attestationResponse = await startRegistration({
      optionsJSON: publicKey,
    });

    const res = await fetch(
      `${this.baseUrl}/transaction/${transactionID}/register`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(attestationResponse),
      }
    );

    const responseJson = await res.json();
    const { nonce } = responseJson.data;
    return nonce;
  }

  async authenticate(
    transactionId: string,
    opts?: IPasskeyAuthenticateOptions
  ): Promise<string> {
    const publicKey = await this.getAuthenticationOptions(transactionId);

    const assertionResponse = await startAuthentication({
      optionsJSON: publicKey,
    });

    const res = await fetch(
      `${this.baseUrl}/transaction/${transactionId}/authenticate`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(assertionResponse),
      }
    );

    if (!res.ok) {
      const error = await res.json();
      throw new Error(
        `Authentication request failed: ${error.message || res.statusText}`
      );
    }

    const responseJson = await res.json();
    const { nonce } = responseJson.data;
    return nonce;
  }

  async canAuthenticateWithPasskey(): Promise<boolean> {
    return !!window.PublicKeyCredential;
  }

  async canRegisterPasskey(): Promise<boolean> {
    return (
      !!window.PublicKeyCredential &&
      typeof navigator.credentials.create === "function"
    );
  }

  async canUseConditionalMediation(): Promise<boolean> {
    return typeof window?.PublicKeyCredential
      ?.isConditionalMediationAvailable === "function"
      ? await window.PublicKeyCredential.isConditionalMediationAvailable()
      : false;
  }

  private async getRegistrationOptions(transactionID: string) {
    const res = await fetch(
      `${this.baseUrl}/transaction/${transactionID}/registration-options`
    );

    if (!res.ok) {
      const error = await res.json();
      throw new Error(
        `Registration options request failed: ${error.message || res.statusText}`
      );
    }

    const responseJson = await res.json();
    return responseJson.data;
  }

  private async getAuthenticationOptions(transactionID: string) {
    const res = await fetch(
      `${this.baseUrl}/transaction/${transactionID}/authentication-options`
    );

    if (!res.ok) {
      const error = await res.json();
      throw new Error(
        `Authentication options request failed: ${error.message || res.statusText}`
      );
    }

    const responseJson = await res.json();
    return responseJson.data;
  }
}
