package services

import (
	"github.com/google/uuid"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap/zaptest"

	"veza-backend-api/internal/models"
)

func TestNewPlaybackExportService(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewPlaybackExportService(logger)

	assert.NotNil(t, service)
	assert.NotNil(t, service.logger)
}

func TestNewPlaybackExportService_NilLogger(t *testing.T) {
	service := NewPlaybackExportService(nil)

	assert.NotNil(t, service)
	assert.NotNil(t, service.logger)
}

func TestPlaybackExportService_ExportCSV_Success(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	// Créer un répertoire temporaire
	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.csv")

	// Créer des données de test
	now := time.Now()
	id1 := uuid.New()
	trackID := uuid.New()
	userID1 := uuid.New()
	id2 := uuid.New()
	userID2 := uuid.New()

	analytics := []models.PlaybackAnalytics{
		{
			ID:             id1,
			TrackID:        trackID,
			UserID:         userID1,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
		{
			ID:             id2,
			TrackID:        trackID,
			UserID:         userID2,
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 90.0,
			StartedAt:      now,
			EndedAt:        &now,
			CreatedAt:      now,
		},
	}

	err := service.ExportCSV(analytics, filename)
	require.NoError(t, err)

	// Vérifier que le fichier existe
	_, err = os.Stat(filename)
	assert.NoError(t, err)

	// Vérifier le contenu du fichier
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	assert.Contains(t, string(data), "ID")
	assert.Contains(t, string(data), "Track ID")
	assert.Contains(t, string(data), id1.String())
	assert.Contains(t, string(data), "120")
}

func TestPlaybackExportService_ExportCSV_EmptyData(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.csv")

	err := service.ExportCSV([]models.PlaybackAnalytics{}, filename)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "no analytics data")
}

func TestPlaybackExportService_ExportJSON_Success(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.json")

	now := time.Now()
	id := uuid.New()
	trackID := uuid.New()
	userID := uuid.New()

	analytics := []models.PlaybackAnalytics{
		{
			ID:             id,
			TrackID:        trackID,
			UserID:         userID,
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err := service.ExportJSON(analytics, filename)
	require.NoError(t, err)

	// Vérifier que le fichier existe
	_, err = os.Stat(filename)
	assert.NoError(t, err)

	// Vérifier que c'est du JSON valide
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	// Le JSON est indenté, donc les valeurs peuvent avoir des espaces
	assert.Contains(t, string(data), `"id": "`+id.String()+`"`)
	assert.Contains(t, string(data), `"track_id": "`+trackID.String()+`"`)
	assert.Contains(t, string(data), `"play_time": 120`)
}

func TestPlaybackExportService_ExportJSON_EmptyData(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.json")

	err := service.ExportJSON([]models.PlaybackAnalytics{}, filename)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "no analytics data")
}

func TestPlaybackExportService_ExportReport_CSV(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "report.csv")

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       171, // 95% de 180
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 95.0,
			StartedAt:      now,
			EndedAt:        &now,
			CreatedAt:      now,
		},
	}

	err := service.ExportReport(analytics, filename, FormatCSV)
	require.NoError(t, err)

	// Vérifier que le fichier existe
	_, err = os.Stat(filename)
	assert.NoError(t, err)

	// Vérifier le contenu
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	assert.Contains(t, string(data), "Total Sessions")
	assert.Contains(t, string(data), "Average Play Time")
	assert.Contains(t, string(data), "Completion Rate")
}

func TestPlaybackExportService_ExportReport_JSON(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "report.json")

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err := service.ExportReport(analytics, filename, FormatJSON)
	require.NoError(t, err)

	// Vérifier que le fichier existe
	_, err = os.Stat(filename)
	assert.NoError(t, err)

	// Vérifier que c'est du JSON valide avec statistiques
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	assert.Contains(t, string(data), `"statistics"`)
	assert.Contains(t, string(data), `"analytics"`)
	assert.Contains(t, string(data), `"total_sessions"`)
}

func TestPlaybackExportService_ExportReport_InvalidFormat(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "report.txt")

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err := service.ExportReport(analytics, filename, ExportFormat("invalid"))
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unsupported export format")
}

func TestPlaybackExportService_ExportReport_EmptyData(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "report.csv")

	err := service.ExportReport([]models.PlaybackAnalytics{}, filename, FormatCSV)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "no analytics data")
}

func TestPlaybackExportService_calculateReportStats(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			PauseCount:     2,
			SeekCount:      3,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       150,
			PauseCount:     1,
			SeekCount:      2,
			CompletionRate: 95.0, // ≥95%
			StartedAt:      now,
			EndedAt:        &now,
			CreatedAt:      now,
		},
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       100,
			PauseCount:     0,
			SeekCount:      1,
			CompletionRate: 92.0, // ≥90% mais <95%
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	stats := service.calculateReportStats(analytics)

	assert.Equal(t, 3, stats.TotalSessions)
	assert.Equal(t, int64(370), stats.TotalPlayTime)
	assert.InDelta(t, 123.33, stats.AveragePlayTime, 0.1)
	assert.Equal(t, int64(3), stats.TotalPauses)
	assert.InDelta(t, 1.0, stats.AveragePauses, 0.1)
	assert.Equal(t, int64(6), stats.TotalSeeks)
	assert.InDelta(t, 2.0, stats.AverageSeeks, 0.1)
	assert.InDelta(t, 87.33, stats.AverageCompletion, 0.1)

	// 2 sessions avec ≥90% completion (95% et 92%)
	assert.InDelta(t, 66.67, stats.CompletionRate, 0.1)

	// 1 session avec ≥95% completion
	assert.Equal(t, 1, stats.CompletedSessions)
}

func TestPlaybackExportService_calculateReportStats_Empty(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	stats := service.calculateReportStats([]models.PlaybackAnalytics{})

	assert.Equal(t, 0, stats.TotalSessions)
	assert.Equal(t, int64(0), stats.TotalPlayTime)
	assert.Equal(t, 0.0, stats.AveragePlayTime)
}

func TestPlaybackExportService_ExportCSV_WithEndedAt(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.csv")

	now := time.Now()
	endedAt := now.Add(5 * time.Minute)
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			EndedAt:        &endedAt,
			CreatedAt:      now,
		},
	}

	err := service.ExportCSV(analytics, filename)
	require.NoError(t, err)

	// Vérifier que EndedAt est dans le fichier
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	assert.Contains(t, string(data), endedAt.Format(time.RFC3339))
}

func TestPlaybackExportService_ExportCSV_WithoutEndedAt(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.csv")

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			EndedAt:        nil,
			CreatedAt:      now,
		},
	}

	err := service.ExportCSV(analytics, filename)
	require.NoError(t, err)

	// Vérifier que le fichier contient une ligne avec EndedAt vide
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	// La ligne devrait avoir une colonne vide pour EndedAt
	assert.Contains(t, string(data), ",120,0,0,75.00") // Part of the CSV line we can match safely
}

func TestPlaybackExportService_ExportToWriter_CSV(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.csv")

	file, err := os.Create(filename)
	require.NoError(t, err)
	defer file.Close()

	now := time.Now()
	id := uuid.New()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             id,
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err = service.ExportToWriter(analytics, FormatCSV, file)
	require.NoError(t, err)

	file.Close()

	// Vérifier le contenu
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	assert.Contains(t, string(data), "ID")
	assert.Contains(t, string(data), id.String())
}

func TestPlaybackExportService_ExportToWriter_JSON(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.json")

	file, err := os.Create(filename)
	require.NoError(t, err)
	defer file.Close()

	now := time.Now()
	id := uuid.New()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             id,
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err = service.ExportToWriter(analytics, FormatJSON, file)
	require.NoError(t, err)

	file.Close()

	// Vérifier le contenu
	data, err := os.ReadFile(filename)
	require.NoError(t, err)
	// Le JSON est indenté, donc les valeurs peuvent avoir des espaces
	assert.Contains(t, string(data), `"id": "`+id.String()+`"`)
}

func TestPlaybackExportService_ExportToWriter_InvalidFormat(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	tmpDir := t.TempDir()
	filename := filepath.Join(tmpDir, "test.txt")

	file, err := os.Create(filename)
	require.NoError(t, err)
	defer file.Close()

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	err = service.ExportToWriter(analytics, ExportFormat("invalid"), file)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unsupported export format")
}

func TestPlaybackExportService_ExportToWriter_InvalidWriter(t *testing.T) {
	service := NewPlaybackExportService(zaptest.NewLogger(t))

	now := time.Now()
	analytics := []models.PlaybackAnalytics{
		{
			ID:             uuid.New(),
			TrackID:        uuid.New(),
			UserID:         uuid.New(),
			PlayTime:       120,
			CompletionRate: 75.0,
			StartedAt:      now,
			CreatedAt:      now,
		},
	}

	// Passer un writer invalide
	err := service.ExportToWriter(analytics, FormatCSV, "invalid")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "writer must be")
}
