package api

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"

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

// Handler holds all API handlers
type Handler struct {
	config   *config.Config
	scorer   *heuristics.Scorer
	webauthn *stepup.WebAuthnService
	pow      *stepup.PoWService
}

// NewHandler creates a new API handler
func NewHandler(cfg *config.Config) *Handler {
	return &Handler{
		config:   cfg,
		scorer:   heuristics.NewScorer(cfg),
		webauthn: stepup.NewWebAuthnService(&cfg.StepUp.WebAuthn),
		pow:      stepup.NewPoWService(&cfg.StepUp.PoW),
	}
}

// TickRequest represents metrics sent from client SDK
type TickRequest struct {
	SessionID        string    `json:"session_id"`
	Score            float64   `json:"score,omitempty"`
	DwellRatio       float64   `json:"dwell_ratio"`
	ScrollDepth      float64   `json:"scroll_depth"`
	Clicks           int       `json:"clicks"`
	MouseMoves       int       `json:"mouse_moves"`
	KeyStrokes       int       `json:"key_strokes,omitempty"`
	TouchEvents      int       `json:"touch_events,omitempty"`
	MouseVelocities  []float64 `json:"mouse_velocities,omitempty"`
	ScrollVelocities []float64 `json:"scroll_velocities,omitempty"`
	ClickIntervals   []float64 `json:"click_intervals,omitempty"`
	Timestamp        int64     `json:"ts"`
}

// TickResponse returns the computed score and action
type TickResponse struct {
	SessionID    string  `json:"session_id"`
	Score        float64 `json:"score"`
	Action       string  `json:"action"`
	StepUpMethod string  `json:"step_up_method,omitempty"`
	Message      string  `json:"message,omitempty"`
}

// HandleTick receives tick data from client SDK
func (h *Handler) HandleTick(c *gin.Context) {
	var req TickRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	// Generate session ID if not provided
	if req.SessionID == "" {
		req.SessionID = uuid.New().String()
	}

	// Get or create session
	session := h.scorer.GetOrCreateSession(req.SessionID)

	// Update metrics
	totalTime := time.Since(session.StartTime).Seconds()
	session.VisibleTime = req.DwellRatio * totalTime
	session.MaxScrollPercent = req.ScrollDepth / 100.0 // Convert from percentage
	session.ClickCount = req.Clicks
	session.MouseMoves = req.MouseMoves
	session.KeyStrokes = req.KeyStrokes
	session.TouchEvents = req.TouchEvents

	if len(req.MouseVelocities) > 0 {
		session.MouseVelocities = append(session.MouseVelocities, req.MouseVelocities...)
	}
	if len(req.ScrollVelocities) > 0 {
		session.ScrollVelocities = append(session.ScrollVelocities, req.ScrollVelocities...)
	}
	if len(req.ClickIntervals) > 0 {
		session.ClickIntervals = append(session.ClickIntervals, req.ClickIntervals...)
	}

	// Calculate score
	score := h.scorer.CalculateScore(req.SessionID)

	// Determine action
	var action, stepUpMethod, message string
	if score >= h.config.Thresholds.ScorePass {
		action = "allow"
		message = "Human behavior detected"
	} else if score >= h.config.Thresholds.ScoreChallenge {
		action = "allow"
		message = "Acceptable behavior"
	} else {
		action = "challenge"
		stepUpMethod = h.config.StepUp.Primary
		message = "Additional verification required"
	}

	c.JSON(http.StatusOK, TickResponse{
		SessionID:    req.SessionID,
		Score:        score,
		Action:       action,
		StepUpMethod: stepUpMethod,
		Message:      message,
	})
}

// HandleEvaluate evaluates a session for a specific path
func (h *Handler) HandleEvaluate(c *gin.Context) {
	sessionID := c.Query("session_id")
	path := c.Query("path")

	if sessionID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "session_id required"})
		return
	}

	if path == "" {
		path = "default"
	}

	// TODO: Load path configs from ai-access.json
	pathConfigs := map[string]config.PathConfig{}

	result := h.scorer.EvaluateRequest(sessionID, path, pathConfigs)

	c.JSON(http.StatusOK, result)
}

// HandleWebAuthnChallenge creates a WebAuthn challenge
func (h *Handler) HandleWebAuthnChallenge(c *gin.Context) {
	if !h.webauthn.IsEnabled() {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "WebAuthn not enabled"})
		return
	}

	sessionID := c.Query("session_id")
	if sessionID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "session_id required"})
		return
	}

	challenge, err := h.webauthn.CreateChallenge(sessionID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create challenge"})
		return
	}

	c.JSON(http.StatusOK, challenge)
}

// HandleWebAuthnVerify verifies a WebAuthn assertion
func (h *Handler) HandleWebAuthnVerify(c *gin.Context) {
	if !h.webauthn.IsEnabled() {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "WebAuthn not enabled"})
		return
	}

	var req stepup.VerifyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	verified, err := h.webauthn.VerifyChallenge(&req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Verification failed"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"verified":   verified,
		"session_id": req.SessionID,
	})
}

// HandlePoWChallenge creates a Proof-of-Work challenge
func (h *Handler) HandlePoWChallenge(c *gin.Context) {
	if !h.pow.IsEnabled() {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "PoW not enabled"})
		return
	}

	sessionID := c.Query("session_id")
	if sessionID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "session_id required"})
		return
	}

	challenge, err := h.pow.CreateChallenge(sessionID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create challenge"})
		return
	}

	c.JSON(http.StatusOK, challenge)
}

// HandlePoWVerify verifies a Proof-of-Work solution
func (h *Handler) HandlePoWVerify(c *gin.Context) {
	if !h.pow.IsEnabled() {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "PoW not enabled"})
		return
	}

	var req stepup.PoWVerifyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	verified, err := h.pow.VerifyChallenge(&req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Verification failed"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"verified":   verified,
		"session_id": req.SessionID,
	})
}

// HandleGetConfig returns client-safe configuration
func (h *Handler) HandleGetConfig(c *gin.Context) {
	// Return only non-sensitive config for client SDK
	clientConfig := gin.H{
		"thresholds": h.config.Thresholds,
		"weights":    h.config.Weights,
		"tick": gin.H{
			"endpoint":    h.config.Tick.Endpoint,
			"interval_ms": h.config.Tick.IntervalMs,
		},
		"step_up": gin.H{
			"methods": h.config.StepUp.Methods,
			"primary": h.config.StepUp.Primary,
			"webauthn": gin.H{
				"enabled":           h.config.StepUp.WebAuthn.Enabled,
				"userVerification":  h.config.StepUp.WebAuthn.UserVerification,
				"timeout_ms":        h.config.StepUp.WebAuthn.TimeoutMs,
				"challenge_endpoint": h.config.StepUp.WebAuthn.ChallengeEndpoint,
			},
			"pow": gin.H{
				"enabled":         h.config.StepUp.PoW.Enabled,
				"difficulty":      h.config.StepUp.PoW.Difficulty,
				"max_duration_ms": h.config.StepUp.PoW.MaxDurationMs,
			},
		},
		"compliance": h.config.Compliance,
	}

	c.JSON(http.StatusOK, clientConfig)
}

// HandleHealth returns service health
func (h *Handler) HandleHealth(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":  "healthy",
		"service": "pca-heuristic-service",
		"version": "0.1.0",
	})
}

// StartCleanupRoutine starts background cleanup
func (h *Handler) StartCleanupRoutine() {
	go func() {
		ticker := time.NewTicker(5 * time.Minute)
		for range ticker.C {
			h.scorer.CleanupOldSessions(30 * time.Minute)
			h.webauthn.CleanupExpiredChallenges()
			h.pow.CleanupExpiredChallenges()
		}
	}()
}
