package services

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

	"veza-backend-api/internal/models"

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

// UserSegment représente un segment d'utilisateur
// T0378: Create Playback Analytics User Segmentation
type UserSegment string

const (
	// Segments par engagement
	SegmentHighEngagement   UserSegment = "high_engagement"
	SegmentMediumEngagement UserSegment = "medium_engagement"
	SegmentLowEngagement    UserSegment = "low_engagement"

	// Segments par completion rate
	SegmentHighCompletion   UserSegment = "high_completion"
	SegmentMediumCompletion UserSegment = "medium_completion"
	SegmentLowCompletion    UserSegment = "low_completion"

	// Segments par comportement
	SegmentActiveListener  UserSegment = "active_listener"  // Beaucoup de sessions
	SegmentCasualListener  UserSegment = "casual_listener"  // Peu de sessions
	SegmentFrequentSkipper UserSegment = "frequent_skipper" // Beaucoup de skips
	SegmentFocusedListener UserSegment = "focused_listener" // Peu de skips, beaucoup d'écoute
)

// PlaybackSegmentationService gère la segmentation des utilisateurs pour les analytics de lecture
// T0378: Create Playback Analytics User Segmentation
type PlaybackSegmentationService struct {
	db     *gorm.DB
	logger *zap.Logger
}

// NewPlaybackSegmentationService crée un nouveau service de segmentation d'utilisateurs
func NewPlaybackSegmentationService(db *gorm.DB, logger *zap.Logger) *PlaybackSegmentationService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &PlaybackSegmentationService{
		db:     db,
		logger: logger,
	}
}

// UserMetrics représente les métriques agrégées pour un utilisateur
// MIGRATION UUID: UserID migré vers uuid.UUID
type UserMetrics struct {
	UserID            uuid.UUID `json:"user_id"`
	SessionCount      int64     `json:"session_count"`
	AverageCompletion float64   `json:"average_completion"` // Taux de complétion moyen (%)
	AveragePlayTime   float64   `json:"average_play_time"`  // Temps de lecture moyen (secondes)
	TotalPlayTime     int64     `json:"total_play_time"`    // Temps de lecture total (secondes)
	AveragePauses     float64   `json:"average_pauses"`     // Nombre moyen de pauses
	AverageSeeks      float64   `json:"average_seeks"`      // Nombre moyen de seeks
	EngagementScore   float64   `json:"engagement_score"`   // Score d'engagement (0-100)
	CompletionRate    float64   `json:"completion_rate"`    // Pourcentage de sessions complétées (>90%)
	SkipRate          float64   `json:"skip_rate"`          // Taux de skips (seeks par session)
}

// SegmentationResult représente le résultat de la segmentation
type SegmentationResult struct {
	TrackID       uuid.UUID                   `json:"track_id"`
	TotalUsers    int64                       `json:"total_users"`
	Segments      map[UserSegment][]uuid.UUID `json:"segments"`               // Map de segment -> liste d'user UUIDs
	UserMetrics   map[uuid.UUID]*UserMetrics  `json:"user_metrics,omitempty"` // Métriques par utilisateur
	SegmentCounts map[UserSegment]int64       `json:"segment_counts"`         // Nombre d'utilisateurs par segment
	AnalyzedAt    time.Time                   `json:"analyzed_at"`
}

// SegmentUsers segmente les utilisateurs pour un track donné
// T0378: Create Playback Analytics User Segmentation
func (s *PlaybackSegmentationService) SegmentUsers(ctx context.Context, trackID uuid.UUID) (*SegmentationResult, 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, "id = ?", 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)
	}

	// Récupérer toutes les analytics pour ce track
	var analytics []models.PlaybackAnalytics
	if err := s.db.WithContext(ctx).Model(&models.PlaybackAnalytics{}).
		Where("track_id = ?", trackID).
		Find(&analytics).Error; err != nil {
		return nil, fmt.Errorf("failed to get analytics: %w", err)
	}

	if len(analytics) == 0 {
		// Retourner un résultat vide
		return &SegmentationResult{
			TrackID:       trackID,
			TotalUsers:    0,
			Segments:      make(map[UserSegment][]uuid.UUID),
			UserMetrics:   make(map[uuid.UUID]*UserMetrics),
			SegmentCounts: make(map[UserSegment]int64),
			AnalyzedAt:    time.Now(),
		}, nil
	}

	// Calculer les métriques par utilisateur
	userMetrics := s.calculateUserMetrics(analytics)

	// Segmenter par engagement
	engagementSegments := s.segmentByEngagement(userMetrics)

	// Segmenter par completion rate
	completionSegments := s.segmentByCompletionRate(userMetrics)

	// Segmenter par comportement
	behaviorSegments := s.segmentByBehavior(userMetrics)

	// Combiner tous les segments
	allSegments := make(map[UserSegment][]uuid.UUID)
	for segment, userIDs := range engagementSegments {
		allSegments[segment] = userIDs
	}
	for segment, userIDs := range completionSegments {
		allSegments[segment] = userIDs
	}
	for segment, userIDs := range behaviorSegments {
		allSegments[segment] = userIDs
	}

	// Calculer les compteurs par segment
	segmentCounts := make(map[UserSegment]int64)
	for segment, userIDs := range allSegments {
		segmentCounts[segment] = int64(len(userIDs))
	}

	result := &SegmentationResult{
		TrackID:       trackID,
		TotalUsers:    int64(len(userMetrics)),
		Segments:      allSegments,
		UserMetrics:   userMetrics,
		SegmentCounts: segmentCounts,
		AnalyzedAt:    time.Now(),
	}

	s.logger.Info("Segmented users for track",
		zap.String("track_id", trackID.String()),
		zap.Int64("total_users", result.TotalUsers),
		zap.Int("total_segments", len(allSegments)))

	return result, nil
}

// calculateUserMetrics calcule les métriques agrégées pour chaque utilisateur
// MIGRATION UUID: retourne map[uuid.UUID]*UserMetrics
func (s *PlaybackSegmentationService) calculateUserMetrics(analytics []models.PlaybackAnalytics) map[uuid.UUID]*UserMetrics {
	userMetricsMap := make(map[uuid.UUID]*UserMetrics)

	// Grouper les analytics par utilisateur
	userAnalytics := make(map[uuid.UUID][]models.PlaybackAnalytics)
	for _, a := range analytics {
		userAnalytics[a.UserID] = append(userAnalytics[a.UserID], a)
	}

	// Calculer les métriques pour chaque utilisateur
	for userID, userSessions := range userAnalytics {
		if len(userSessions) == 0 {
			continue
		}

		var totalCompletion float64
		var totalPlayTime int64
		var totalPauses int64
		var totalSeeks int64
		var completedSessions int64

		for _, session := range userSessions {
			totalCompletion += session.CompletionRate
			totalPlayTime += int64(session.PlayTime)
			totalPauses += int64(session.PauseCount)
			totalSeeks += int64(session.SeekCount)
			if session.CompletionRate >= 90.0 {
				completedSessions++
			}
		}

		sessionCount := int64(len(userSessions))
		averageCompletion := totalCompletion / float64(sessionCount)
		averagePlayTime := float64(totalPlayTime) / float64(sessionCount)
		averagePauses := float64(totalPauses) / float64(sessionCount)
		averageSeeks := float64(totalSeeks) / float64(sessionCount)
		completionRate := float64(completedSessions) / float64(sessionCount) * 100.0
		skipRate := averageSeeks // Taux de skips = nombre moyen de seeks

		// Calculer le score d'engagement (0-100)
		// Basé sur: completion rate (50%), pauses (25%), seeks (25%)
		engagementScore := averageCompletion * 0.5

		// Normaliser les pauses (0-10 pauses = 0-25 points)
		pauseScore := 25.0
		if averagePauses > 0 {
			pauseScore = 25.0 - (averagePauses / 10.0 * 25.0)
			if pauseScore < 0 {
				pauseScore = 0
			}
		}
		engagementScore += pauseScore

		// Normaliser les seeks (0-5 seeks = 0-25 points)
		seekScore := 25.0
		if averageSeeks > 0 {
			seekScore = 25.0 - (averageSeeks / 5.0 * 25.0)
			if seekScore < 0 {
				seekScore = 0
			}
		}
		engagementScore += seekScore

		// S'assurer que le score est entre 0 et 100
		if engagementScore > 100.0 {
			engagementScore = 100.0
		}
		if engagementScore < 0.0 {
			engagementScore = 0.0
		}

		userMetricsMap[userID] = &UserMetrics{
			UserID:            userID, // UUID
			SessionCount:      sessionCount,
			AverageCompletion: averageCompletion,
			AveragePlayTime:   averagePlayTime,
			TotalPlayTime:     totalPlayTime,
			AveragePauses:     averagePauses,
			AverageSeeks:      averageSeeks,
			EngagementScore:   engagementScore,
			CompletionRate:    completionRate,
			SkipRate:          skipRate,
		}
	}

	return userMetricsMap
}

// segmentByEngagement segmente les utilisateurs par niveau d'engagement
// MIGRATION UUID: paramètre et retour utilisent uuid.UUID
func (s *PlaybackSegmentationService) segmentByEngagement(userMetrics map[uuid.UUID]*UserMetrics) map[UserSegment][]uuid.UUID {
	segments := make(map[UserSegment][]uuid.UUID)
	segments[SegmentHighEngagement] = []uuid.UUID{}
	segments[SegmentMediumEngagement] = []uuid.UUID{}
	segments[SegmentLowEngagement] = []uuid.UUID{}

	for userID, metrics := range userMetrics {
		if metrics.EngagementScore >= 75.0 {
			segments[SegmentHighEngagement] = append(segments[SegmentHighEngagement], userID)
		} else if metrics.EngagementScore >= 50.0 {
			segments[SegmentMediumEngagement] = append(segments[SegmentMediumEngagement], userID)
		} else {
			segments[SegmentLowEngagement] = append(segments[SegmentLowEngagement], userID)
		}
	}

	return segments
}

// segmentByCompletionRate segmente les utilisateurs par taux de complétion
// MIGRATION UUID: paramètre et retour utilisent uuid.UUID
func (s *PlaybackSegmentationService) segmentByCompletionRate(userMetrics map[uuid.UUID]*UserMetrics) map[UserSegment][]uuid.UUID {
	segments := make(map[UserSegment][]uuid.UUID)
	segments[SegmentHighCompletion] = []uuid.UUID{}
	segments[SegmentMediumCompletion] = []uuid.UUID{}
	segments[SegmentLowCompletion] = []uuid.UUID{}

	for userID, metrics := range userMetrics {
		if metrics.AverageCompletion >= 75.0 {
			segments[SegmentHighCompletion] = append(segments[SegmentHighCompletion], userID)
		} else if metrics.AverageCompletion >= 50.0 {
			segments[SegmentMediumCompletion] = append(segments[SegmentMediumCompletion], userID)
		} else {
			segments[SegmentLowCompletion] = append(segments[SegmentLowCompletion], userID)
		}
	}

	return segments
}

// segmentByBehavior segmente les utilisateurs par comportement d'écoute
// MIGRATION UUID: paramètre et retour utilisent uuid.UUID
func (s *PlaybackSegmentationService) segmentByBehavior(userMetrics map[uuid.UUID]*UserMetrics) map[UserSegment][]uuid.UUID {
	segments := make(map[UserSegment][]uuid.UUID)
	segments[SegmentActiveListener] = []uuid.UUID{}
	segments[SegmentCasualListener] = []uuid.UUID{}
	segments[SegmentFrequentSkipper] = []uuid.UUID{}
	segments[SegmentFocusedListener] = []uuid.UUID{}

	// Calculer les seuils basés sur les données
	var totalSessions int64
	var totalSeeks float64
	var maxSessions int64

	for _, metrics := range userMetrics {
		totalSessions += metrics.SessionCount
		totalSeeks += metrics.AverageSeeks
		if metrics.SessionCount > maxSessions {
			maxSessions = metrics.SessionCount
		}
	}

	avgSessions := float64(totalSessions) / float64(len(userMetrics))
	avgSeeks := totalSeeks / float64(len(userMetrics))

	// Seuils pour la segmentation
	activeThreshold := avgSessions * 1.5 // 50% au-dessus de la moyenne
	casualThreshold := avgSessions * 0.5 // 50% en dessous de la moyenne
	skipThreshold := avgSeeks * 1.5      // 50% au-dessus de la moyenne des seeks
	focusedThreshold := avgSeeks * 0.5   // 50% en dessous de la moyenne des seeks

	for userID, metrics := range userMetrics {
		// Segmentation par nombre de sessions
		if float64(metrics.SessionCount) >= activeThreshold {
			segments[SegmentActiveListener] = append(segments[SegmentActiveListener], userID)
		} else if float64(metrics.SessionCount) <= casualThreshold {
			segments[SegmentCasualListener] = append(segments[SegmentCasualListener], userID)
		}

		// Segmentation par comportement de skip
		if metrics.AverageSeeks >= skipThreshold {
			segments[SegmentFrequentSkipper] = append(segments[SegmentFrequentSkipper], userID)
		} else if metrics.AverageSeeks <= focusedThreshold && metrics.AverageCompletion >= 70.0 {
			// Focused listener: peu de skips ET bonne complétion
			segments[SegmentFocusedListener] = append(segments[SegmentFocusedListener], userID)
		}
	}

	return segments
}

// GetUserSegment retourne le segment principal d'un utilisateur pour un track
// MIGRATION UUID: userID migré vers uuid.UUID, trackID reste int64
func (s *PlaybackSegmentationService) GetUserSegment(ctx context.Context, trackID uuid.UUID, userID uuid.UUID) (UserSegment, error) {
	if trackID == uuid.Nil || userID == uuid.Nil {
		return "", fmt.Errorf("invalid track ID or user ID: trackID=%s, userID=%s", trackID, userID)
	}

	result, err := s.SegmentUsers(ctx, trackID)
	if err != nil {
		return "", err
	}

	// Trouver le segment principal de l'utilisateur (priorité: engagement > completion > behavior)
	userMetrics, exists := result.UserMetrics[userID]
	if !exists {
		return "", fmt.Errorf("user %s not found in analytics for track %s", userID, trackID)
	}

	// Déterminer le segment principal basé sur l'engagement
	if userMetrics.EngagementScore >= 75.0 {
		return SegmentHighEngagement, nil
	} else if userMetrics.EngagementScore >= 50.0 {
		return SegmentMediumEngagement, nil
	} else {
		return SegmentLowEngagement, nil
	}
}
