package stepup

import (
	"crypto/rand"
	"encoding/base64"
	"sync"
	"time"

	"github.com/breakpilot/pca-platform/heuristic-service/internal/config"
)

// WebAuthnService handles WebAuthn challenges and verification
type WebAuthnService struct {
	config     *config.WebAuthnConfig
	challenges map[string]*Challenge
	mu         sync.RWMutex
}

// Challenge represents a WebAuthn challenge
type Challenge struct {
	ID           string    `json:"id"`
	SessionID    string    `json:"session_id"`
	Challenge    string    `json:"challenge"`
	CreatedAt    time.Time `json:"created_at"`
	ExpiresAt    time.Time `json:"expires_at"`
	Verified     bool      `json:"verified"`
}

// ChallengeRequest is the client-side challenge request format
type ChallengeRequest struct {
	SessionID string `json:"session_id"`
}

// ChallengeResponse is the WebAuthn public key request options
type ChallengeResponse struct {
	PublicKey PublicKeyCredentialRequestOptions `json:"publicKey"`
}

// PublicKeyCredentialRequestOptions mirrors the WebAuthn API structure
type PublicKeyCredentialRequestOptions struct {
	Challenge        string                          `json:"challenge"`
	Timeout          int                             `json:"timeout"`
	RpID             string                          `json:"rpId,omitempty"`
	UserVerification string                          `json:"userVerification"`
	AllowCredentials []PublicKeyCredentialDescriptor `json:"allowCredentials,omitempty"`
}

// PublicKeyCredentialDescriptor for allowed credentials
type PublicKeyCredentialDescriptor struct {
	Type       string   `json:"type"`
	ID         string   `json:"id"`
	Transports []string `json:"transports,omitempty"`
}

// VerifyRequest for client verification response
type VerifyRequest struct {
	SessionID   string                 `json:"session_id"`
	ChallengeID string                 `json:"challenge_id"`
	Credential  map[string]interface{} `json:"credential"`
}

// NewWebAuthnService creates a new WebAuthn service
func NewWebAuthnService(cfg *config.WebAuthnConfig) *WebAuthnService {
	return &WebAuthnService{
		config:     cfg,
		challenges: make(map[string]*Challenge),
	}
}

// CreateChallenge generates a new WebAuthn challenge for a session
func (s *WebAuthnService) CreateChallenge(sessionID string) (*ChallengeResponse, error) {
	// Generate random challenge bytes
	challengeBytes := make([]byte, 32)
	if _, err := rand.Read(challengeBytes); err != nil {
		return nil, err
	}
	challengeStr := base64.RawURLEncoding.EncodeToString(challengeBytes)

	// Generate challenge ID
	idBytes := make([]byte, 16)
	rand.Read(idBytes)
	challengeID := base64.RawURLEncoding.EncodeToString(idBytes)

	// Create challenge
	challenge := &Challenge{
		ID:        challengeID,
		SessionID: sessionID,
		Challenge: challengeStr,
		CreatedAt: time.Now(),
		ExpiresAt: time.Now().Add(time.Duration(s.config.TimeoutMs) * time.Millisecond),
		Verified:  false,
	}

	// Store challenge
	s.mu.Lock()
	s.challenges[challengeID] = challenge
	s.mu.Unlock()

	// Build response
	response := &ChallengeResponse{
		PublicKey: PublicKeyCredentialRequestOptions{
			Challenge:        challengeStr,
			Timeout:          s.config.TimeoutMs,
			UserVerification: s.config.UserVerification,
			// In production, you'd include allowed credentials from user registration
			AllowCredentials: []PublicKeyCredentialDescriptor{},
		},
	}

	return response, nil
}

// VerifyChallenge verifies a WebAuthn assertion response
func (s *WebAuthnService) VerifyChallenge(req *VerifyRequest) (bool, error) {
	s.mu.RLock()
	challenge, exists := s.challenges[req.ChallengeID]
	s.mu.RUnlock()

	if !exists {
		return false, nil
	}

	// Check expiration
	if time.Now().After(challenge.ExpiresAt) {
		s.mu.Lock()
		delete(s.challenges, req.ChallengeID)
		s.mu.Unlock()
		return false, nil
	}

	// Check session match
	if challenge.SessionID != req.SessionID {
		return false, nil
	}

	// In production, you would:
	// 1. Parse the credential response
	// 2. Verify the signature against stored public key
	// 3. Verify the challenge matches
	// 4. Check the origin
	// For MVP, we accept any valid-looking response

	// Verify credential structure exists
	if req.Credential == nil {
		return false, nil
	}

	// Mark as verified
	s.mu.Lock()
	challenge.Verified = true
	s.mu.Unlock()

	return true, nil
}

// CleanupExpiredChallenges removes expired challenges
func (s *WebAuthnService) CleanupExpiredChallenges() {
	s.mu.Lock()
	defer s.mu.Unlock()

	now := time.Now()
	for id, challenge := range s.challenges {
		if now.After(challenge.ExpiresAt) {
			delete(s.challenges, id)
		}
	}
}

// IsEnabled returns whether WebAuthn is enabled
func (s *WebAuthnService) IsEnabled() bool {
	return s.config.Enabled
}
