package handlers

import (
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"veza-backend-api/internal/services"
)

// AnalyticsHandler gère les opérations d'analytics de lecture de tracks
type AnalyticsHandler struct {
	analyticsService *services.AnalyticsService
	commonHandler    *CommonHandler
}

// NewAnalyticsHandler crée un nouveau handler d'analytics
func NewAnalyticsHandler(analyticsService *services.AnalyticsService, logger *zap.Logger) *AnalyticsHandler {
	return &AnalyticsHandler{
		analyticsService: analyticsService,
		commonHandler:    NewCommonHandler(logger),
	}
}

// RecordPlayRequest représente la requête pour enregistrer une lecture
type RecordPlayRequest struct {
	Duration int    `json:"duration" binding:"required,min=1"`
	Device   string `json:"device,omitempty"`
}

// RecordPlay gère l'enregistrement d'une lecture de track
func (h *AnalyticsHandler) RecordPlay(c *gin.Context) {
	trackIDStr := c.Param("id")
	if trackIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "track id is required"})
		return
	}

	trackID, err := uuid.Parse(trackIDStr) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid track id"})
		return
	}

	var req RecordPlayRequest
	if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Récupérer user_id si authentifié (optionnel pour analytics anonymes)
	var userID *uuid.UUID
	if uid, ok := c.Get("user_id"); ok {
		if uidUUID, ok := uid.(uuid.UUID); ok {
			userID = &uidUUID
		}
	}

	// Récupérer IP address et device
	ipAddress := c.ClientIP()
	device := req.Device
	if device == "" {
		device = c.GetHeader("User-Agent")
	}

	err = h.analyticsService.RecordPlay(c.Request.Context(), trackID, userID, req.Duration, device, ipAddress)
	if err != nil {
		if err.Error() == "track not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "track not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "play recorded"})
}

// GetTrackStats gère la récupération des statistiques d'un track
func (h *AnalyticsHandler) GetTrackStats(c *gin.Context) {
	trackIDStr := c.Param("id")
	if trackIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "track id is required"})
		return
	}

	trackID, err := uuid.Parse(trackIDStr) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid track id"})
		return
	}

	stats, err := h.analyticsService.GetTrackStats(c.Request.Context(), trackID)
	if err != nil {
		if err.Error() == "track not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "track not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"stats": stats})
}

// GetTopTracks gère la récupération des tracks les plus écoutés
func (h *AnalyticsHandler) GetTopTracks(c *gin.Context) {
	// Parse limit
	limit := 10
	if limitStr := c.Query("limit"); limitStr != "" {
		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 {
			limit = l
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid limit (must be between 1 and 100)"})
			return
		}
	}

	// Parse start_date (optionnel)
	var startDate *time.Time
	if startDateStr := c.Query("start_date"); startDateStr != "" {
		parsed, err := time.Parse(time.RFC3339, startDateStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid start_date format (use RFC3339)"})
			return
		}
		startDate = &parsed
	}

	// Parse end_date (optionnel)
	var endDate *time.Time
	if endDateStr := c.Query("end_date"); endDateStr != "" {
		parsed, err := time.Parse(time.RFC3339, endDateStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid end_date format (use RFC3339)"})
			return
		}
		endDate = &parsed
	}

	topTracks, err := h.analyticsService.GetTopTracks(c.Request.Context(), limit, startDate, endDate)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"tracks": topTracks})
}

// GetPlaysOverTime gère la récupération des lectures sur une période
func (h *AnalyticsHandler) GetPlaysOverTime(c *gin.Context) {
	trackIDStr := c.Param("id")
	if trackIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "track id is required"})
		return
	}

	trackID, err := uuid.Parse(trackIDStr) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid track id"})
		return
	}

	// Parse start_date (optionnel, défaut: 30 jours)
	startDate := time.Now().AddDate(0, 0, -30)
	if startDateStr := c.Query("start_date"); startDateStr != "" {
		parsed, err := time.Parse(time.RFC3339, startDateStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid start_date format (use RFC3339)"})
			return
		}
		startDate = parsed
	}

	// Parse end_date (optionnel, défaut: maintenant)
	endDate := time.Now()
	if endDateStr := c.Query("end_date"); endDateStr != "" {
		parsed, err := time.Parse(time.RFC3339, endDateStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid end_date format (use RFC3339)"})
			return
		}
		endDate = parsed
	}

	// Parse interval (optionnel, défaut: day)
	interval := c.DefaultQuery("interval", "day")
	validIntervals := map[string]bool{"hour": true, "day": true, "week": true, "month": true}
	if !validIntervals[interval] {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid interval (must be: hour, day, week, month)"})
		return
	}

	points, err := h.analyticsService.GetPlaysOverTime(c.Request.Context(), trackID, startDate, endDate, interval)
	if err != nil {
		if err.Error() == "track not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "track not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"points": points})
}

// GetUserStats gère la récupération des statistiques d'un utilisateur
func (h *AnalyticsHandler) GetUserStats(c *gin.Context) {
	userIDStr := c.Param("id")
	if userIDStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "user id is required"})
		return
	}

	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"})
		return
	}

	// Vérifier que l'utilisateur peut accéder à ses propres stats
	var authenticatedUserID *uuid.UUID
	if uid, ok := c.Get("user_id"); ok {
		if uidUUID, ok := uid.(uuid.UUID); ok {
			authenticatedUserID = &uidUUID
		}
	}
	if authenticatedUserID != nil && *authenticatedUserID != userID {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot access other user's stats"})
		return
	}

	stats, err := h.analyticsService.GetUserStats(c.Request.Context(), userID)
	if err != nil {
		if err.Error() == "user not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"stats": stats})
}
