package services

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"veza-backend-api/internal/models"

	"go.uber.org/zap"
)

// PlaybackExportService gère l'export des analytics de lecture
// T0367: Create Playback Analytics Export Service
type PlaybackExportService struct {
	logger *zap.Logger
}

// NewPlaybackExportService crée un nouveau service d'export d'analytics
func NewPlaybackExportService(logger *zap.Logger) *PlaybackExportService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &PlaybackExportService{
		logger: logger,
	}
}

// ExportFormat représente le format d'export
type ExportFormat string

const (
	FormatCSV  ExportFormat = "csv"
	FormatJSON ExportFormat = "json"
)

// ExportCSV exporte les analytics en format CSV
// T0367: Create Playback Analytics Export Service
func (s *PlaybackExportService) ExportCSV(analytics []models.PlaybackAnalytics, filename string) error {
	if len(analytics) == 0 {
		return fmt.Errorf("no analytics data to export")
	}

	// Créer le répertoire parent si nécessaire
	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// Écrire l'en-tête CSV
	header := []string{
		"ID",
		"Track ID",
		"User ID",
		"Play Time (seconds)",
		"Pause Count",
		"Seek Count",
		"Completion Rate (%)",
		"Started At",
		"Ended At",
		"Created At",
	}
	if err := writer.Write(header); err != nil {
		return fmt.Errorf("failed to write CSV header: %w", err)
	}

	// Écrire les données
	for _, a := range analytics {
		endedAt := ""
		if a.EndedAt != nil {
			endedAt = a.EndedAt.Format(time.RFC3339)
		}

		row := []string{
			a.ID.String(),      // UUID as string
			a.TrackID.String(), // UUID as string
			a.UserID.String(),  // UUID as string
			fmt.Sprintf("%d", a.PlayTime),
			fmt.Sprintf("%d", a.PauseCount),
			fmt.Sprintf("%d", a.SeekCount),
			fmt.Sprintf("%.2f", a.CompletionRate),
			a.StartedAt.Format(time.RFC3339),
			endedAt,
			a.CreatedAt.Format(time.RFC3339),
		}
		if err := writer.Write(row); err != nil {
			return fmt.Errorf("failed to write CSV row: %w", err)
		}
	}

	s.logger.Info("Analytics exported to CSV",
		zap.String("filename", filename),
		zap.Int("count", len(analytics)))

	return nil
}

// ExportJSON exporte les analytics en format JSON
// T0367: Create Playback Analytics Export Service
func (s *PlaybackExportService) ExportJSON(analytics []models.PlaybackAnalytics, filename string) error {
	if len(analytics) == 0 {
		return fmt.Errorf("no analytics data to export")
	}

	// Créer le répertoire parent si nécessaire
	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	data, err := json.MarshalIndent(analytics, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}

	if err := os.WriteFile(filename, data, 0644); err != nil {
		return fmt.Errorf("failed to write JSON file: %w", err)
	}

	s.logger.Info("Analytics exported to JSON",
		zap.String("filename", filename),
		zap.Int("count", len(analytics)))

	return nil
}

// ExportReport génère un rapport d'analytics avec statistiques agrégées
// T0367: Create Playback Analytics Export Service
func (s *PlaybackExportService) ExportReport(analytics []models.PlaybackAnalytics, filename string, format ExportFormat) error {
	if len(analytics) == 0 {
		return fmt.Errorf("no analytics data to export")
	}

	// Calculer les statistiques
	stats := s.calculateReportStats(analytics)

	// Générer le rapport selon le format
	switch format {
	case FormatCSV:
		return s.exportReportCSV(analytics, stats, filename)
	case FormatJSON:
		return s.exportReportJSON(analytics, stats, filename)
	default:
		return fmt.Errorf("unsupported export format: %s", format)
	}
}

// ReportStats représente les statistiques d'un rapport
type ReportStats struct {
	TotalSessions     int     `json:"total_sessions"`
	TotalPlayTime     int64   `json:"total_play_time"`   // seconds
	AveragePlayTime   float64 `json:"average_play_time"` // seconds
	TotalPauses       int64   `json:"total_pauses"`
	AveragePauses     float64 `json:"average_pauses"`
	TotalSeeks        int64   `json:"total_seeks"`
	AverageSeeks      float64 `json:"average_seeks"`
	AverageCompletion float64 `json:"average_completion"` // percentage
	CompletionRate    float64 `json:"completion_rate"`    // percentage of sessions with >90% completion
	CompletedSessions int     `json:"completed_sessions"` // sessions with ≥95% completion
}

// calculateReportStats calcule les statistiques agrégées
func (s *PlaybackExportService) calculateReportStats(analytics []models.PlaybackAnalytics) ReportStats {
	stats := ReportStats{
		TotalSessions: len(analytics),
	}

	if len(analytics) == 0 {
		return stats
	}

	var totalPlayTime int64
	var totalPauses int64
	var totalSeeks int64
	var totalCompletion float64
	completedSessions := 0

	for _, a := range analytics {
		totalPlayTime += int64(a.PlayTime)
		totalPauses += int64(a.PauseCount)
		totalSeeks += int64(a.SeekCount)
		totalCompletion += a.CompletionRate

		if a.CompletionRate >= 95.0 {
			completedSessions++
		}
	}

	stats.TotalPlayTime = totalPlayTime
	stats.AveragePlayTime = float64(totalPlayTime) / float64(len(analytics))
	stats.TotalPauses = totalPauses
	stats.AveragePauses = float64(totalPauses) / float64(len(analytics))
	stats.TotalSeeks = totalSeeks
	stats.AverageSeeks = float64(totalSeeks) / float64(len(analytics))
	stats.AverageCompletion = totalCompletion / float64(len(analytics))
	stats.CompletedSessions = completedSessions

	// Completion rate (sessions with >90% completion)
	sessionsOver90 := 0
	for _, a := range analytics {
		if a.CompletionRate >= 90.0 {
			sessionsOver90++
		}
	}
	if len(analytics) > 0 {
		stats.CompletionRate = float64(sessionsOver90) / float64(len(analytics)) * 100.0
	}

	return stats
}

// exportReportCSV exporte un rapport en CSV avec statistiques
func (s *PlaybackExportService) exportReportCSV(analytics []models.PlaybackAnalytics, stats ReportStats, filename string) error {
	// Créer le répertoire parent si nécessaire
	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// Écrire les statistiques
	statsHeader := []string{"Statistic", "Value"}
	if err := writer.Write(statsHeader); err != nil {
		return fmt.Errorf("failed to write stats header: %w", err)
	}

	statsRows := [][]string{
		{"Total Sessions", fmt.Sprintf("%d", stats.TotalSessions)},
		{"Total Play Time (seconds)", fmt.Sprintf("%d", stats.TotalPlayTime)},
		{"Average Play Time (seconds)", fmt.Sprintf("%.2f", stats.AveragePlayTime)},
		{"Total Pauses", fmt.Sprintf("%d", stats.TotalPauses)},
		{"Average Pauses", fmt.Sprintf("%.2f", stats.AveragePauses)},
		{"Total Seeks", fmt.Sprintf("%d", stats.TotalSeeks)},
		{"Average Seeks", fmt.Sprintf("%.2f", stats.AverageSeeks)},
		{"Average Completion (%)", fmt.Sprintf("%.2f", stats.AverageCompletion)},
		{"Completion Rate (%)", fmt.Sprintf("%.2f", stats.CompletionRate)},
		{"Completed Sessions (≥95%)", fmt.Sprintf("%d", stats.CompletedSessions)},
	}

	for _, row := range statsRows {
		if err := writer.Write(row); err != nil {
			return fmt.Errorf("failed to write stats row: %w", err)
		}
	}

	// Ligne vide
	if err := writer.Write([]string{}); err != nil {
		return fmt.Errorf("failed to write empty row: %w", err)
	}

	// Écrire l'en-tête des données
	header := []string{
		"ID",
		"Track ID",
		"User ID",
		"Play Time (seconds)",
		"Pause Count",
		"Seek Count",
		"Completion Rate (%)",
		"Started At",
		"Ended At",
		"Created At",
	}
	if err := writer.Write(header); err != nil {
		return fmt.Errorf("failed to write CSV header: %w", err)
	}

	// Écrire les données
	for _, a := range analytics {
		endedAt := ""
		if a.EndedAt != nil {
			endedAt = a.EndedAt.Format(time.RFC3339)
		}

		row := []string{
			a.ID.String(),      // UUID as string
			a.TrackID.String(), // UUID as string
			a.UserID.String(),  // UUID as string
			fmt.Sprintf("%d", a.PlayTime),
			fmt.Sprintf("%d", a.PauseCount),
			fmt.Sprintf("%d", a.SeekCount),
			fmt.Sprintf("%.2f", a.CompletionRate),
			a.StartedAt.Format(time.RFC3339),
			endedAt,
			a.CreatedAt.Format(time.RFC3339),
		}
		if err := writer.Write(row); err != nil {
			return fmt.Errorf("failed to write CSV row: %w", err)
		}
	}

	s.logger.Info("Analytics report exported to CSV",
		zap.String("filename", filename),
		zap.Int("count", len(analytics)))

	return nil
}

// exportReportJSON exporte un rapport en JSON avec statistiques
func (s *PlaybackExportService) exportReportJSON(analytics []models.PlaybackAnalytics, stats ReportStats, filename string) error {
	// Créer le répertoire parent si nécessaire
	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	// Structure du rapport
	report := map[string]interface{}{
		"generated_at": time.Now().Format(time.RFC3339),
		"statistics":   stats,
		"analytics":    analytics,
	}

	data, err := json.MarshalIndent(report, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}

	if err := os.WriteFile(filename, data, 0644); err != nil {
		return fmt.Errorf("failed to write JSON file: %w", err)
	}

	s.logger.Info("Analytics report exported to JSON",
		zap.String("filename", filename),
		zap.Int("count", len(analytics)))

	return nil
}

// ExportToWriter exporte les analytics vers un writer (pour streaming HTTP)
func (s *PlaybackExportService) ExportToWriter(analytics []models.PlaybackAnalytics, format ExportFormat, writer interface{}) error {
	switch format {
	case FormatCSV:
		return s.exportCSVToWriter(analytics, writer)
	case FormatJSON:
		return s.exportJSONToWriter(analytics, writer)
	default:
		return fmt.Errorf("unsupported export format: %s", format)
	}
}

// exportCSVToWriter exporte en CSV vers un writer
func (s *PlaybackExportService) exportCSVToWriter(analytics []models.PlaybackAnalytics, writer interface{}) error {
	// Cette méthode peut être étendue pour supporter différents types de writers
	// Pour l'instant, on retourne une erreur si le writer n'est pas un *os.File
	file, ok := writer.(*os.File)
	if !ok {
		return fmt.Errorf("writer must be *os.File for CSV export")
	}

	csvWriter := csv.NewWriter(file)
	defer csvWriter.Flush()

	// Écrire l'en-tête
	header := []string{
		"ID",
		"Track ID",
		"User ID",
		"Play Time (seconds)",
		"Pause Count",
		"Seek Count",
		"Completion Rate (%)",
		"Started At",
		"Ended At",
		"Created At",
	}
	if err := csvWriter.Write(header); err != nil {
		return fmt.Errorf("failed to write CSV header: %w", err)
	}

	// Écrire les données
	for _, a := range analytics {
		endedAt := ""
		if a.EndedAt != nil {
			endedAt = a.EndedAt.Format(time.RFC3339)
		}

		row := []string{
			a.ID.String(),      // UUID as string
			a.TrackID.String(), // UUID as string
			a.UserID.String(),  // UUID as string
			fmt.Sprintf("%d", a.PlayTime),
			fmt.Sprintf("%d", a.PauseCount),
			fmt.Sprintf("%d", a.SeekCount),
			fmt.Sprintf("%.2f", a.CompletionRate),
			a.StartedAt.Format(time.RFC3339),
			endedAt,
			a.CreatedAt.Format(time.RFC3339),
		}
		if err := csvWriter.Write(row); err != nil {
			return fmt.Errorf("failed to write CSV row: %w", err)
		}
	}

	return nil
}

// exportJSONToWriter exporte en JSON vers un writer
func (s *PlaybackExportService) exportJSONToWriter(analytics []models.PlaybackAnalytics, writer interface{}) error {
	// Cette méthode peut être étendue pour supporter différents types de writers
	// Pour l'instant, on retourne une erreur si le writer n'est pas un *os.File
	file, ok := writer.(*os.File)
	if !ok {
		return fmt.Errorf("writer must be *os.File for JSON export")
	}

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	return encoder.Encode(analytics)
}
