// WebAuthn utility functions for passkey authentication
import { base64urlToBuffer, bufferToBase64url, getCsrfToken } from './webauthn_utils'
import type {
  WebAuthnChallengeData,
  WebAuthnAuthChallengeData,
  WebAuthnRegistrationResult,
  WebAuthnAuthenticationResult,
  ErrorResponse
} from './types/webauthn'
import type { LiveViewHook } from './types/liveview'

const WebAuthn = {
  // Registration flow
  async register(challengeData: WebAuthnChallengeData, credentialName: string): Promise<WebAuthnRegistrationResult> {
    // Convert challenge and user ID to ArrayBuffer
    const challenge = base64urlToBuffer(challengeData.challenge)
    const userId = base64urlToBuffer(challengeData.user.id)

    const publicKeyCredentialCreationOptions: PublicKeyCredentialCreationOptions = {
      challenge,
      rp: challengeData.rp,
      user: {
        id: userId,
        name: challengeData.user.name,
        displayName: challengeData.user.displayName
      },
      pubKeyCredParams: challengeData.pubKeyCredParams as PublicKeyCredentialParameters[],
      authenticatorSelection: challengeData.authenticatorSelection,
      timeout: challengeData.timeout,
      attestation: (challengeData.attestation as AttestationConveyancePreference) || 'none'
    }

    try {
      const credential = await navigator.credentials.create({
        publicKey: publicKeyCredentialCreationOptions
      }) as PublicKeyCredential

      if (!credential.response || !(credential.response instanceof AuthenticatorAttestationResponse)) {
        throw new Error('Invalid credential response')
      }

      return {
        name: credentialName,
        attestation: {
          id: credential.id,
          rawId: bufferToBase64url(credential.rawId),
          type: credential.type,
          response: {
            clientDataJSON: bufferToBase64url(credential.response.clientDataJSON),
            attestationObject: bufferToBase64url(credential.response.attestationObject)
          }
        }
      }
    } catch (error) {
      console.error('WebAuthn registration error:', error)
      throw error
    }
  },

  // Authentication flow
  async authenticate(challengeData: WebAuthnAuthChallengeData): Promise<WebAuthnAuthenticationResult> {
    const challenge = base64urlToBuffer(challengeData.challenge)

    const allowCredentials: PublicKeyCredentialDescriptor[] = challengeData.allowCredentials.map(cred => ({
      type: cred.type as PublicKeyCredentialType,
      id: base64urlToBuffer(cred.id),
      transports: cred.transports as AuthenticatorTransport[]
    }))

    const publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions = {
      challenge,
      rpId: challengeData.rpId,
      allowCredentials,
      timeout: challengeData.timeout,
      userVerification: challengeData.userVerification as UserVerificationRequirement
    }

    try {
      const assertion = await navigator.credentials.get({
        publicKey: publicKeyCredentialRequestOptions
      }) as PublicKeyCredential

      if (!assertion.response || !(assertion.response instanceof AuthenticatorAssertionResponse)) {
        throw new Error('Invalid assertion response')
      }

      return {
        assertion: {
          id: assertion.id,
          rawId: bufferToBase64url(assertion.rawId),
          type: assertion.type,
          response: {
            clientDataJSON: bufferToBase64url(assertion.response.clientDataJSON),
            authenticatorData: bufferToBase64url(assertion.response.authenticatorData),
            signature: bufferToBase64url(assertion.response.signature),
            userHandle: assertion.response.userHandle ?
              bufferToBase64url(assertion.response.userHandle) : null
          }
        }
      }
    } catch (error) {
      console.error('WebAuthn authentication error:', error)
      throw error
    }
  }
}

// LiveView Hook for Registration (in credential management page)
export const WebAuthnRegister: LiveViewHook = {
  mounted() {
    this.handleEvent?.("start_registration", async ({ name }: { name: string }) => {
      try {
        // Fetch challenge from server
        const response = await fetch('/api/webauthn/registration/challenge', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': getCsrfToken()
          }
        })

        if (!response.ok) {
          const error: ErrorResponse = await response.json()
          this.pushEvent?.("registration_failed", { error: error.error || 'Failed to get registration challenge' })
          return
        }

        const challengeData: WebAuthnChallengeData = await response.json()

        // Perform WebAuthn registration
        const registrationData = await WebAuthn.register(challengeData, name)

        // Send to server
        const registerResponse = await fetch('/api/webauthn/register', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': getCsrfToken()
          },
          body: JSON.stringify(registrationData)
        })

        if (registerResponse.ok) {
          this.pushEvent?.("registration_success", {})
        } else {
          const error: ErrorResponse = await registerResponse.json()
          this.pushEvent?.("registration_failed", { error: error.error || 'Failed to register passkey' })
        }
      } catch (error) {
        console.error('Registration error:', error)
        this.pushEvent?.("registration_failed", {
          error: (error as Error).name === 'NotAllowedError'
            ? 'Registration was cancelled or timed out'
            : (error as Error).message || 'Failed to register passkey'
        })
      }
    })
  }
}

// LiveView Hook for Authentication (on login page)
export const WebAuthnLogin: LiveViewHook = {
  mounted() {
    this.handleEvent?.("start_authentication", async ({ email }: { email: string }) => {
      try {
        // Fetch challenge from server
        const response = await fetch('/api/webauthn/authentication/challenge', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': getCsrfToken()
          },
          body: JSON.stringify({ email })
        })

        if (!response.ok) {
          const error: ErrorResponse = await response.json()
          this.pushEvent?.("authentication_failed", {
            error: error.error || 'No passkeys found for this email'
          })
          return
        }

        const challengeData: WebAuthnAuthChallengeData = await response.json()

        // Perform WebAuthn authentication
        const authData = await WebAuthn.authenticate(challengeData)

        // Send to server
        const authResponse = await fetch('/api/webauthn/authenticate', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': getCsrfToken()
          },
          body: JSON.stringify(authData)
        })

        if (authResponse.ok) {
          const result = await authResponse.json()
          // Redirect to the provided URL
          window.location.href = result.redirect
        } else {
          this.pushEvent?.("authentication_failed", { error: 'Authentication failed' })
        }
      } catch (error) {
        console.error('Authentication error:', error)
        this.pushEvent?.("authentication_failed", {
          error: (error as Error).name === 'NotAllowedError'
            ? 'Authentication was cancelled or timed out'
            : (error as Error).message || 'Failed to authenticate'
        })
      }
    })
  }
}
