package services

import (
	"context"
	"fmt"
	"github.com/google/uuid"
	"time"

	"veza-backend-api/internal/models"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// PlaybackFilter représente les critères de filtrage pour les analytics de lecture
// T0372: Create Playback Analytics Filtering Service
type PlaybackFilter struct {
	// Filtres par date
	StartDate *time.Time `json:"start_date,omitempty"` // Date de début (inclusive)
	EndDate   *time.Time `json:"end_date,omitempty"`   // Date de fin (inclusive)

	// Filtre par utilisateur
	UserID *uuid.UUID `json:"user_id,omitempty"` // ID de l'utilisateur

	// Filtres par completion rate
	MinCompletionRate *float64 `json:"min_completion_rate,omitempty"` // Taux de complétion minimum (0-100)
	MaxCompletionRate *float64 `json:"max_completion_rate,omitempty"` // Taux de complétion maximum (0-100)

	// Filtres par temps de lecture
	MinPlayTime *int `json:"min_play_time,omitempty"` // Temps de lecture minimum (secondes)
	MaxPlayTime *int `json:"max_play_time,omitempty"` // Temps de lecture maximum (secondes)

	// Filtres par période (prédéfinies)
	Period *string `json:"period,omitempty"` // "today", "week", "month", "year"

	// Pagination
	Page  int `json:"page,omitempty"`  // Numéro de page (commence à 1)
	Limit int `json:"limit,omitempty"` // Nombre d'éléments par page

	// Tri
	SortBy    string `json:"sort_by,omitempty"`    // Champ de tri: "created_at", "play_time", "completion_rate"
	SortOrder string `json:"sort_order,omitempty"` // Ordre: "asc" ou "desc"
}

// PlaybackFilterService gère le filtrage des analytics de lecture
// T0372: Create Playback Analytics Filtering Service
type PlaybackFilterService struct {
	db     *gorm.DB
	logger *zap.Logger
}

// NewPlaybackFilterService crée un nouveau service de filtrage d'analytics
func NewPlaybackFilterService(db *gorm.DB, logger *zap.Logger) *PlaybackFilterService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &PlaybackFilterService{
		db:     db,
		logger: logger,
	}
}

// Filter applique les filtres et retourne les analytics correspondantes
// T0372: Create Playback Analytics Filtering Service
func (s *PlaybackFilterService) Filter(ctx context.Context, trackID uuid.UUID, filter PlaybackFilter) ([]models.PlaybackAnalytics, int64, error) {
	if trackID == uuid.Nil {
		return nil, 0, fmt.Errorf("invalid track ID: %s", trackID)
	}

	// Vérifier que le track existe
	var track models.Track
	if err := s.db.WithContext(ctx).First(&track, trackID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, 0, fmt.Errorf("track not found: %s", trackID)
		}
		return nil, 0, fmt.Errorf("failed to get track: %w", err)
	}

	// Construire la requête de base
	query := s.db.WithContext(ctx).Model(&models.PlaybackAnalytics{}).Where("track_id = ?", trackID)

	// Appliquer les filtres
	query = s.applyFilters(query, filter)

	// Compter le total avant pagination
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to count filtered analytics: %w", err)
	}

	// Appliquer le tri
	query = s.applySorting(query, filter)

	// Appliquer la pagination
	query = s.applyPagination(query, filter)

	// Exécuter la requête
	var results []models.PlaybackAnalytics
	if err := query.Find(&results).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to filter analytics: %w", err)
	}

	s.logger.Info("Filtered playback analytics",
		zap.String("track_id", trackID.String()),
		zap.Int64("total", total),
		zap.Int("results_count", len(results)))

	return results, total, nil
}

// applyFilters applique tous les filtres à la requête
func (s *PlaybackFilterService) applyFilters(query *gorm.DB, filter PlaybackFilter) *gorm.DB {
	// Filtre par période prédéfinie (prioritaire sur StartDate/EndDate)
	if filter.Period != nil {
		now := time.Now()
		var startDate, endDate time.Time

		switch *filter.Period {
		case "today":
			startDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
			endDate = now
		case "week":
			startDate = now.AddDate(0, 0, -7)
			endDate = now
		case "month":
			startDate = now.AddDate(0, 0, -30)
			endDate = now
		case "year":
			startDate = now.AddDate(-1, 0, 0)
			endDate = now
		default:
			// Période invalide, ignorer
			s.logger.Warn("Invalid period filter", zap.String("period", *filter.Period))
		}

		if !startDate.IsZero() && !endDate.IsZero() {
			query = query.Where("created_at >= ? AND created_at <= ?", startDate, endDate)
		}
	} else {
		// Filtres par date personnalisés
		if filter.StartDate != nil {
			query = query.Where("created_at >= ?", *filter.StartDate)
		}
		if filter.EndDate != nil {
			query = query.Where("created_at <= ?", *filter.EndDate)
		}
	}

	// Filtre par utilisateur
	if filter.UserID != nil && *filter.UserID != uuid.Nil {
		query = query.Where("user_id = ?", *filter.UserID)
	}

	// Filtres par completion rate
	if filter.MinCompletionRate != nil {
		if *filter.MinCompletionRate < 0 {
			*filter.MinCompletionRate = 0
		}
		if *filter.MinCompletionRate > 100 {
			*filter.MinCompletionRate = 100
		}
		query = query.Where("completion_rate >= ?", *filter.MinCompletionRate)
	}
	if filter.MaxCompletionRate != nil {
		if *filter.MaxCompletionRate < 0 {
			*filter.MaxCompletionRate = 0
		}
		if *filter.MaxCompletionRate > 100 {
			*filter.MaxCompletionRate = 100
		}
		query = query.Where("completion_rate <= ?", *filter.MaxCompletionRate)
	}

	// Filtres par temps de lecture
	if filter.MinPlayTime != nil && *filter.MinPlayTime >= 0 {
		query = query.Where("play_time >= ?", *filter.MinPlayTime)
	}
	if filter.MaxPlayTime != nil && *filter.MaxPlayTime >= 0 {
		query = query.Where("play_time <= ?", *filter.MaxPlayTime)
	}

	return query
}

// applySorting applique le tri à la requête
func (s *PlaybackFilterService) applySorting(query *gorm.DB, filter PlaybackFilter) *gorm.DB {
	// Valider le champ de tri
	validSortFields := map[string]bool{
		"created_at":      true,
		"play_time":       true,
		"completion_rate": true,
		"pause_count":     true,
		"seek_count":      true,
	}

	sortBy := filter.SortBy
	if sortBy == "" {
		sortBy = "created_at" // Par défaut
	}

	if !validSortFields[sortBy] {
		sortBy = "created_at" // Fallback si invalide
	}

	// Valider l'ordre de tri
	sortOrder := filter.SortOrder
	if sortOrder != "asc" && sortOrder != "desc" {
		sortOrder = "desc" // Par défaut
	}

	return query.Order(fmt.Sprintf("%s %s", sortBy, sortOrder))
}

// applyPagination applique la pagination à la requête
func (s *PlaybackFilterService) applyPagination(query *gorm.DB, filter PlaybackFilter) *gorm.DB {
	// Valeurs par défaut
	page := filter.Page
	if page <= 0 {
		page = 1
	}

	limit := filter.Limit
	if limit <= 0 {
		limit = 20 // Par défaut
	}
	if limit > 100 {
		limit = 100 // Maximum
	}

	offset := (page - 1) * limit
	return query.Offset(offset).Limit(limit)
}

// GetFilteredStats retourne les statistiques agrégées pour les analytics filtrées
// T0372: Create Playback Analytics Filtering Service
func (s *PlaybackFilterService) GetFilteredStats(ctx context.Context, trackID uuid.UUID, filter PlaybackFilter) (*PlaybackStats, error) {
	if trackID == uuid.Nil {
		return nil, fmt.Errorf("invalid track ID: %s", trackID)
	}

	// Vérifier que le track existe
	var track models.Track
	if err := s.db.WithContext(ctx).First(&track, trackID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("track not found: %s", trackID)
		}
		return nil, fmt.Errorf("failed to get track: %w", err)
	}

	// Construire la requête avec les filtres
	query := s.db.WithContext(ctx).Model(&models.PlaybackAnalytics{}).Where("track_id = ?", trackID)
	query = s.applyFilters(query, filter)

	var stats PlaybackStats

	// Total sessions
	if err := query.Count(&stats.TotalSessions).Error; err != nil {
		return nil, fmt.Errorf("failed to count sessions: %w", err)
	}

	if stats.TotalSessions == 0 {
		return &stats, nil
	}

	// Total play time
	var totalPlayTime int64
	if err := query.Select("COALESCE(SUM(play_time), 0)").Scan(&totalPlayTime).Error; err != nil {
		return nil, fmt.Errorf("failed to calculate total play time: %w", err)
	}
	stats.TotalPlayTime = totalPlayTime
	stats.AveragePlayTime = float64(totalPlayTime) / float64(stats.TotalSessions)

	// Total pauses
	var totalPauses int64
	if err := query.Select("COALESCE(SUM(pause_count), 0)").Scan(&totalPauses).Error; err != nil {
		return nil, fmt.Errorf("failed to calculate total pauses: %w", err)
	}
	stats.TotalPauses = totalPauses
	stats.AveragePauses = float64(totalPauses) / float64(stats.TotalSessions)

	// Total seeks
	var totalSeeks int64
	if err := query.Select("COALESCE(SUM(seek_count), 0)").Scan(&totalSeeks).Error; err != nil {
		return nil, fmt.Errorf("failed to calculate total seeks: %w", err)
	}
	stats.TotalSeeks = totalSeeks
	stats.AverageSeeks = float64(totalSeeks) / float64(stats.TotalSessions)

	// Average completion rate
	var avgCompletion float64
	if err := query.Select("COALESCE(AVG(completion_rate), 0)").Scan(&avgCompletion).Error; err != nil {
		return nil, fmt.Errorf("failed to calculate average completion: %w", err)
	}
	stats.AverageCompletion = avgCompletion

	// Completion rate (sessions with >90% completion)
	var completedSessions int64
	if err := query.Where("completion_rate >= ?", 90.0).Count(&completedSessions).Error; err != nil {
		return nil, fmt.Errorf("failed to count completed sessions: %w", err)
	}
	if stats.TotalSessions > 0 {
		stats.CompletionRate = float64(completedSessions) / float64(stats.TotalSessions) * 100.0
	}

	return &stats, nil
}
