package services

import (
	"context"
	"fmt"
	"testing"

	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
)

func TestNewHLSStreamingService(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	logger := zap.NewNop()
	service := NewHLSStreamingService(db, "/tmp/hls", logger)

	if service == nil {
		t.Error("NewHLSStreamingService() returned nil")
	}
	if service.db == nil {
		t.Error("NewHLSStreamingService() returned service with nil db")
	}
	if service.logger == nil {
		t.Error("NewHLSStreamingService() returned service with nil logger")
	}
}

func TestHLSStreamingService_parsePlaylistSegments(t *testing.T) {
	service := NewHLSStreamingService(nil, "", zap.NewNop())

	tests := []struct {
		name     string
		playlist string
		expected []string
	}{
		{
			name: "valid playlist with segments",
			playlist: `#EXTM3U
#EXT-X-VERSION:3
#EXTINF:10.0,
segment_000.ts
#EXTINF:10.0,
segment_001.ts
#EXTINF:10.0,
segment_002.ts
#EXT-X-ENDLIST`,
			expected: []string{"segment_000.ts", "segment_001.ts", "segment_002.ts"},
		},
		{
			name: "playlist with comments only",
			playlist: `#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ENDLIST`,
			expected: []string{},
		},
		{
			name:     "empty playlist",
			playlist: "",
			expected: []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			segments := service.parsePlaylistSegments(tt.playlist)
			if len(segments) != len(tt.expected) {
				t.Errorf("parsePlaylistSegments() returned %d segments, want %d", len(segments), len(tt.expected))
			}
			for i, seg := range segments {
				if seg != tt.expected[i] {
					t.Errorf("parsePlaylistSegments() segment[%d] = %s, want %s", i, seg, tt.expected[i])
				}
			}
		})
	}
}

func TestHLSStreamingService_GetStreamURLs(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
		t.Fatalf("Failed to migrate: %v", err)
	}

	service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
	ctx := context.Background()
	trackID := uuid.New()

	// Create test stream
	stream := &models.HLSStream{
		TrackID:       trackID,
		Status:        models.HLSStatusReady,
		PlaylistURL:   "/tmp/hls/track_123/master.m3u8",
		Bitrates:      models.BitrateList{128, 192, 320},
		SegmentsCount: 10,
	}
	if err := db.Create(stream).Error; err != nil {
		t.Fatalf("Failed to create test stream: %v", err)
	}

	urls, err := service.GetStreamURLs(ctx, trackID, "https://api.example.com")
	if err != nil {
		t.Fatalf("GetStreamURLs() error = %v", err)
	}

	if urls == nil {
		t.Error("GetStreamURLs() returned nil urls")
	}

	// Check master playlist URL
	if urls["master_playlist"] == "" {
		t.Error("GetStreamURLs() missing master_playlist URL")
	}

	// Check quality URLs
	expectedQualities := []int{128, 192, 320}
	for _, bitrate := range expectedQualities {
		key := fmt.Sprintf("quality_%d", bitrate)
		if urls[key] == "" {
			t.Errorf("GetStreamURLs() missing quality URL for %dk", bitrate)
		}
	}
}

func TestHLSStreamingService_CheckStreamExists(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
		t.Fatalf("Failed to migrate: %v", err)
	}

	service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
	ctx := context.Background()
	trackID := uuid.New()

	// Check non-existent stream
	exists, err := service.CheckStreamExists(ctx, trackID)
	if err != nil {
		t.Fatalf("CheckStreamExists() error = %v", err)
	}
	if exists {
		t.Error("CheckStreamExists() returned true for non-existent stream")
	}

	// Create stream
	stream := &models.HLSStream{
		TrackID: trackID,
		Status:  models.HLSStatusReady,
	}
	if err := db.Create(stream).Error; err != nil {
		t.Fatalf("Failed to create test stream: %v", err)
	}

	// Check existing stream
	exists, err = service.CheckStreamExists(ctx, trackID)
	if err != nil {
		t.Fatalf("CheckStreamExists() error = %v", err)
	}
	if !exists {
		t.Error("CheckStreamExists() returned false for existing stream")
	}
}

func TestHLSStreamingService_UpdateStreamStatus(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
		t.Fatalf("Failed to migrate: %v", err)
	}

	service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
	ctx := context.Background()
	trackID := uuid.New()

	// Create stream
	stream := &models.HLSStream{
		TrackID: trackID,
		Status:  models.HLSStatusProcessing,
	}
	if err := db.Create(stream).Error; err != nil {
		t.Fatalf("Failed to create test stream: %v", err)
	}

	// Update status
	err = service.UpdateStreamStatus(ctx, trackID, models.HLSStatusReady)
	if err != nil {
		t.Fatalf("UpdateStreamStatus() error = %v", err)
	}

	// Verify status was updated
	var updatedStream models.HLSStream
	if err := db.Where("track_id = ?", trackID).First(&updatedStream).Error; err != nil {
		t.Fatalf("Failed to query updated stream: %v", err)
	}

	if updatedStream.Status != models.HLSStatusReady {
		t.Errorf("UpdateStreamStatus() status = %v, want %v", updatedStream.Status, models.HLSStatusReady)
	}
}

func TestHLSStreamingService_GetStreamStatistics(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}

	if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
		t.Fatalf("Failed to migrate: %v", err)
	}

	service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
	ctx := context.Background()

	// Create test streams with different statuses
	streams := []*models.HLSStream{
		{TrackID: uuid.New(), Status: models.HLSStatusReady},
		{TrackID: uuid.New(), Status: models.HLSStatusReady},
		{TrackID: uuid.New(), Status: models.HLSStatusProcessing},
		{TrackID: uuid.New(), Status: models.HLSStatusFailed},
	}

	for _, stream := range streams {
		if err := db.Create(stream).Error; err != nil {
			t.Fatalf("Failed to create test stream: %v", err)
		}
	}

	stats, err := service.GetStreamStatistics(ctx)
	if err != nil {
		t.Fatalf("GetStreamStatistics() error = %v", err)
	}

	if stats == nil {
		t.Error("GetStreamStatistics() returned nil stats")
	}

	// Verify counts
	if stats["total_streams"] != int64(4) {
		t.Errorf("GetStreamStatistics() total_streams = %v, want 4", stats["total_streams"])
	}
	if stats["ready_streams"] != int64(2) {
		t.Errorf("GetStreamStatistics() ready_streams = %v, want 2", stats["ready_streams"])
	}
	if stats["processing_streams"] != int64(1) {
		t.Errorf("GetStreamStatistics() processing_streams = %v, want 1", stats["processing_streams"])
	}
	if stats["failed_streams"] != int64(1) {
		t.Errorf("GetStreamStatistics() failed_streams = %v, want 1", stats["failed_streams"])
	}
}

// Note: Full integration tests would require:
// 1. Real HLS stream files (master.m3u8, playlists, segments)
// 2. File system access for validation
// 3. Verification of stream health checks
//
// Example integration test structure:
// func TestHLSStreamingService_ValidateStream_Integration(t *testing.T) {
//     // Setup test database and file system
//     db := setupTestDB(t)
//     outputDir := setupTestHLSFiles(t)
//     defer cleanupTestFiles(t, outputDir)
//
//     service := NewHLSStreamingService(db, outputDir, zap.NewNop())
//
//     // Create test stream
//     trackID := uuid.New()
//     stream := createTestHLSStream(t, db, trackID, outputDir)
//
//     ctx := context.Background()
//     health, err := service.ValidateStream(ctx, trackID)
//     if err != nil {
//         t.Fatalf("ValidateStream() error = %v", err)
//     }
//
//     if !health.IsHealthy {
//         t.Error("ValidateStream() stream should be healthy")
//     }
//     if !health.PlaylistExists {
//         t.Error("ValidateStream() playlist should exist")
//     }
//     if !health.SegmentsValid {
//         t.Error("ValidateStream() segments should be valid")
//     }
// }
