package auth

import (
	"encoding/json"

	"git.mstar.dev/mstar/goutils/sliceutils"
	"github.com/go-webauthn/webauthn/webauthn"
	"github.com/rs/zerolog/log"

	"git.mstar.dev/mstar/linstrom/storage-new/models"
)

// A fake user struct for implementing the webauthn.User interface
// on top of the storage system
type fakeUser struct {
	actualUser *models.User
}

// Ensure that fakeUser always implements webauthn.User
var _ webauthn.User = &fakeUser{}

// WebAuthnID provides the user handle of the user account. A user handle is an opaque byte sequence with a maximum
// size of 64 bytes, and is not meant to be displayed to the user.
//
// To ensure secure operation, authentication and authorization decisions MUST be made on the basis of this id
// member, not the displayName nor name members. See Section 6.1 of [RFC8266].
//
// It's recommended this value is completely random and uses the entire 64 bytes.
//
// Specification: §5.4.3. User Account Parameters for Credential Generation (https://w3c.github.io/webauthn/#dom-publickeycredentialuserentity-id)
func (fakeuser *fakeUser) WebAuthnID() []byte {
	return fakeuser.actualUser.PasskeyId
}

// WebAuthnName provides the name attribute of the user account during registration and is a human-palatable name for the user
// account, intended only for display. For example, "Alex Müller" or "田中倫". The Relying Party SHOULD let the user
// choose this, and SHOULD NOT restrict the choice more than necessary.
//
// Specification: §5.4.3. User Account Parameters for Credential Generation (https://w3c.github.io/webauthn/#dictdef-publickeycredentialuserentity)
func (fakeuser *fakeUser) WebAuthnName() string {
	return fakeuser.actualUser.DisplayName
}

// WebAuthnDisplayName provides the name attribute of the user account during registration and is a human-palatable
// name for the user account, intended only for display. For example, "Alex Müller" or "田中倫". The Relying Party
// SHOULD let the user choose this, and SHOULD NOT restrict the choice more than necessary.
//
// Specification: §5.4.3. User Account Parameters for Credential Generation (https://www.w3.org/TR/webauthn/#dom-publickeycredentialuserentity-displayname)
func (fakeuser *fakeUser) WebAuthnDisplayName() string {
	return fakeuser.actualUser.DisplayName
}

// WebAuthnCredentials provides the list of Credential objects owned by the user.
func (fakeuser *fakeUser) WebAuthnCredentials() []webauthn.Credential {
	// Basically just convert the relevant entries from the user to a credential
	return sliceutils.Map(sliceutils.Filter(
		fakeuser.actualUser.AuthMethods,
		func(t models.UserAuthMethod) bool {
			return t.AuthMethod == models.AuthMethodPasskey ||
				t.AuthMethod == models.AuthMethodPasskey2fa
		}), func(t models.UserAuthMethod) webauthn.Credential {
		var c webauthn.Credential
		if err := json.Unmarshal(t.Token, &c); err != nil {
			// TODO: Is there any better way to handle the error here?
			log.Error().Err(err).Msg("Failed to unmarshal webauthn credential")
		}
		return c
	})
}
