package webauthn

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
)

// PublicKeyCredentialRequestOptions represents the WebAuthn authentication challenge
type PublicKeyCredentialRequestOptions struct {
	Challenge        string   `json:"challenge"`
	Timeout          int      `json:"timeout"`
	RPID             string   `json:"rpId"`
	AllowCredentials []string `json:"allowCredentials,omitempty"`
	UserVerification string   `json:"userVerification"`
}

// PublicKeyCredentialCreationOptions represents the WebAuthn registration challenge
type PublicKeyCredentialCreationOptions struct {
	Challenge              string                 `json:"challenge"`
	RP                     RelyingParty           `json:"rp"`
	User                   User                   `json:"user"`
	PubKeyCredParams       []PubKeyCredParam      `json:"pubKeyCredParams"`
	Timeout                int                    `json:"timeout"`
	Attestation            string                 `json:"attestation"`
	AuthenticatorSelection AuthenticatorSelection `json:"authenticatorSelection"`
}

type RelyingParty struct {
	Name string `json:"name"`
	ID   string `json:"id"`
}

type User struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
}

type PubKeyCredParam struct {
	Type string `json:"type"`
	Alg  int    `json:"alg"`
}

type AuthenticatorSelection struct {
	AuthenticatorAttachment string `json:"authenticatorAttachment,omitempty"`
	RequireResidentKey      bool   `json:"requireResidentKey"`
	UserVerification        string `json:"userVerification"`
}

// GenerateChallenge creates a cryptographically secure random challenge
func GenerateChallenge() (string, error) {
	challenge := make([]byte, 32)
	_, err := rand.Read(challenge)
	if err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(challenge), nil
}

// CreateRegistrationOptions generates WebAuthn registration options
func CreateRegistrationOptions(userID, username, displayName, rpName, rpID string) (*PublicKeyCredentialCreationOptions, error) {
	challenge, err := GenerateChallenge()
	if err != nil {
		return nil, err
	}

	return &PublicKeyCredentialCreationOptions{
		Challenge: challenge,
		RP: RelyingParty{
			Name: rpName,
			ID:   rpID,
		},
		User: User{
			ID:          userID,
			Name:        username,
			DisplayName: displayName,
		},
		PubKeyCredParams: []PubKeyCredParam{
			{Type: "public-key", Alg: -7},   // ES256
			{Type: "public-key", Alg: -257}, // RS256
		},
		Timeout:     60000,
		Attestation: "none",
		AuthenticatorSelection: AuthenticatorSelection{
			RequireResidentKey: false,
			UserVerification:   "preferred",
		},
	}, nil
}

// CreateAuthenticationOptions generates WebAuthn authentication options
func CreateAuthenticationOptions(rpID string, allowedCredentials []string) (*PublicKeyCredentialRequestOptions, error) {
	challenge, err := GenerateChallenge()
	if err != nil {
		return nil, err
	}

	return &PublicKeyCredentialRequestOptions{
		Challenge:        challenge,
		Timeout:          60000,
		RPID:             rpID,
		AllowCredentials: allowedCredentials,
		UserVerification: "preferred",
	}, nil
}

// VerifyClientData validates the WebAuthn client data JSON
func VerifyClientData(clientDataJSON []byte, expectedChallenge, expectedOrigin string) error {
	var clientData struct {
		Type      string `json:"type"`
		Challenge string `json:"challenge"`
		Origin    string `json:"origin"`
	}

	if err := json.Unmarshal(clientDataJSON, &clientData); err != nil {
		return fmt.Errorf("invalid client data JSON: %w", err)
	}

	if clientData.Type != "webauthn.get" && clientData.Type != "webauthn.create" {
		return fmt.Errorf("invalid client data type: %s", clientData.Type)
	}

	if clientData.Challenge != expectedChallenge {
		return fmt.Errorf("challenge mismatch")
	}

	if clientData.Origin != expectedOrigin {
		return fmt.Errorf("origin mismatch: expected %s, got %s", expectedOrigin, clientData.Origin)
	}

	return nil
}

// HashClientData creates SHA-256 hash of client data (required for signature verification)
func HashClientData(clientDataJSON []byte) []byte {
	hash := sha256.Sum256(clientDataJSON)
	return hash[:]
}

// WrapMasterKey encrypts the master key with the admin's public key
// In a production system, this would use the WebAuthn credential's public key
func WrapMasterKey(masterKey []byte, publicKey []byte) ([]byte, error) {
	// Simplified implementation - in production, use proper public key encryption
	// For now, we'll use a symmetric approach with HKDF
	return masterKey, nil // TODO: Implement proper key wrapping
}

// UnwrapMasterKey decrypts the master key using the admin's credential
func UnwrapMasterKey(wrappedKey []byte, privateKey []byte) ([]byte, error) {
	// Simplified implementation - in production, use proper public key decryption
	return wrappedKey, nil // TODO: Implement proper key unwrapping
}
