package client

import (
	"os"
	"testing"
	"time"

	"github.com/gesellix/bose-soundtouch/pkg/speaker"
)

// Integration tests for source selection functionality
// These tests require a real SoundTouch device for validation
// Set SOUNDTOUCH_TEST_HOST environment variable to run these tests

func TestClient_SelectSource_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration tests in short mode")
	}

	host := os.Getenv("SOUNDTOUCH_TEST_HOST")
	if host == "" {
		t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
	}

	// Parse host:port if provided
	finalHost, finalPort := parseHostPort(host)

	config := &Config{
		Host:      finalHost,
		Port:      finalPort,
		Timeout:   15 * time.Second,
		UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
	}

	client := NewClient(config)

	// First, get available sources to know what we can test
	sources, err := client.GetSources()
	if err != nil {
		t.Fatalf("Failed to get sources: %v", err)
	}

	t.Logf("Found %d total sources, %d ready", sources.GetSourceCount(), sources.GetReadySourceCount())

	// Test source selection based on what's available
	tests := []struct {
		name          string
		method        func() error
		checkSource   func() bool
		description   string
		skipIfMissing bool
	}{
		{
			name: "Select Spotify",
			method: func() error {
				spotifySources := sources.GetReadySpotifySources()
				if len(spotifySources) == 0 {
					return nil // Skip if no Spotify available
				}
				// Use first available Spotify account
				return client.SelectSpotify(spotifySources[0].SourceAccount)
			},
			checkSource: func() bool {
				return sources.HasSpotify()
			},
			description:   "Spotify source selection",
			skipIfMissing: true,
		},
		{
			name: "Select TuneIn",
			method: func() error {
				tuneInSources := sources.GetSourcesByType("TUNEIN")
				for _, src := range tuneInSources {
					if src.Status.IsReady() {
						return client.SelectTuneIn(src.SourceAccount)
					}
				}

				return nil // Skip if no TuneIn available
			},
			checkSource: func() bool {
				return sources.HasSource("TUNEIN")
			},
			description:   "TuneIn source selection",
			skipIfMissing: true,
		},
		{
			name: "Select via generic method",
			method: func() error {
				// Find any ready streaming source
				for _, source := range sources.GetAvailableSources() {
					if source.IsStreamingService() {
						return client.SelectSource(source.Source, source.SourceAccount)
					}
				}

				return nil // Skip if no streaming sources available
			},
			checkSource: func() bool {
				streaming := sources.GetStreamingSources()
				for _, src := range streaming {
					if src.Status.IsReady() {
						return true
					}
				}

				return false
			},
			description:   "Generic source selection",
			skipIfMissing: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.skipIfMissing && !tt.checkSource() {
				t.Skipf("Skipping %s - source not available on test device", tt.description)
			}

			t.Logf("Testing %s on %s:%d", tt.description, finalHost, finalPort)

			err := tt.method()
			if err != nil {
				t.Errorf("Failed to execute %s: %v", tt.description, err)
				return
			}

			t.Logf("✓ %s completed successfully", tt.description)

			// Give the device a moment to process the change
			time.Sleep(500 * time.Millisecond)
		})
	}
}

func TestClient_SelectSourceFromItem_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration tests in short mode")
	}

	host := os.Getenv("SOUNDTOUCH_TEST_HOST")
	if host == "" {
		t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
	}

	// Parse host:port if provided
	finalHost, finalPort := parseHostPort(host)

	config := &Config{
		Host:      finalHost,
		Port:      finalPort,
		Timeout:   15 * time.Second,
		UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
	}

	client := NewClient(config)

	// Get available sources
	sources, err := client.GetSources()
	if err != nil {
		t.Fatalf("Failed to get sources: %v", err)
	}

	// Test selecting from available source items
	availableSources := sources.GetAvailableSources()
	if len(availableSources) == 0 {
		t.Skip("No available sources to test with")
	}

	// Test with the first available source
	testSource := availableSources[0]
	t.Logf("Testing SelectSourceFromItem with source: %s (account: %s)",
		testSource.Source, testSource.SourceAccount)

	err = client.SelectSourceFromItem(&testSource)
	if err != nil {
		t.Errorf("Failed to select source from item: %v", err)
		return
	}

	t.Logf("✓ SelectSourceFromItem completed successfully")
}

func TestClient_SelectSource_ErrorHandling_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration tests in short mode")
	}

	host := os.Getenv("SOUNDTOUCH_TEST_HOST")
	if host == "" {
		t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
	}

	// Parse host:port if provided
	finalHost, finalPort := parseHostPort(host)

	config := &Config{
		Host:      finalHost,
		Port:      finalPort,
		Timeout:   15 * time.Second,
		UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
	}

	client := NewClient(config)

	// Test with invalid source
	t.Run("Invalid source", func(t *testing.T) {
		err := client.SelectSource("INVALID_SOURCE", "")
		if err == nil {
			t.Error("Expected error for invalid source, got nil")
		} else {
			t.Logf("✓ Got expected error for invalid source: %v", err)
		}
	})

	// Test with empty source (should fail validation)
	t.Run("Empty source", func(t *testing.T) {
		err := client.SelectSource("", "")
		if err == nil {
			t.Error("Expected error for empty source, got nil")
		} else if err.Error() != "source cannot be empty" {
			t.Errorf("Expected 'source cannot be empty' error, got: %v", err)
		} else {
			t.Logf("✓ Got expected validation error: %v", err)
		}
	})

	// Test with nil source item
	t.Run("Nil source item", func(t *testing.T) {
		err := client.SelectSourceFromItem(nil)
		if err == nil {
			t.Error("Expected error for nil source item, got nil")
		} else if err.Error() != "sourceItem cannot be nil" {
			t.Errorf("Expected 'sourceItem cannot be nil' error, got: %v", err)
		} else {
			t.Logf("✓ Got expected validation error: %v", err)
		}
	})
}

func TestClient_ConvenienceSourceMethods_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration tests in short mode")
	}

	host := os.Getenv("SOUNDTOUCH_TEST_HOST")
	if host == "" {
		t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
	}

	// Parse host:port if provided
	finalHost, finalPort := parseHostPort(host)

	config := &Config{
		Host:      finalHost,
		Port:      finalPort,
		Timeout:   15 * time.Second,
		UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
	}

	client := NewClient(config)

	// Get available sources to determine what we can test
	sources, err := client.GetSources()
	if err != nil {
		t.Fatalf("Failed to get sources: %v", err)
	}

	// Test convenience methods based on availability
	if sources.HasSpotify() {
		t.Run("SelectSpotify", func(t *testing.T) {
			spotifySources := sources.GetReadySpotifySources()
			if len(spotifySources) > 0 {
				err := client.SelectSpotify(spotifySources[0].SourceAccount)
				if err != nil {
					t.Errorf("SelectSpotify failed: %v", err)
				} else {
					t.Log("✓ SelectSpotify succeeded")
				}
			}
		})
	} else {
		t.Log("Spotify not available - skipping SelectSpotify test")
	}

	if sources.HasBluetooth() {
		t.Run("SelectBluetooth", func(t *testing.T) {
			err := client.SelectBluetooth()
			if err != nil {
				t.Errorf("SelectBluetooth failed: %v", err)
			} else {
				t.Log("✓ SelectBluetooth succeeded")
			}
		})
	} else {
		t.Log("Bluetooth not available - skipping SelectBluetooth test")
	}

	if sources.HasSource("TUNEIN") {
		t.Run("SelectTuneIn", func(t *testing.T) {
			tuneInSources := sources.GetSourcesByType("TUNEIN")
			for _, src := range tuneInSources {
				if src.Status.IsReady() {
					err := client.SelectTuneIn(src.SourceAccount)
					if err != nil {
						t.Errorf("SelectTuneIn failed: %v", err)
					} else {
						t.Log("✓ SelectTuneIn succeeded")
					}

					break
				}
			}
		})
	} else {
		t.Log("TuneIn not available - skipping SelectTuneIn test")
	}

	if sources.HasSource("PANDORA") {
		t.Run("SelectPandora", func(t *testing.T) {
			pandoraSources := sources.GetSourcesByType("PANDORA")
			for _, src := range pandoraSources {
				if src.Status.IsReady() {
					err := client.SelectPandora(src.SourceAccount)
					if err != nil {
						t.Errorf("SelectPandora failed: %v", err)
					} else {
						t.Log("✓ SelectPandora succeeded")
					}

					break
				}
			}
		})
	} else {
		t.Log("Pandora not available - skipping SelectPandora test")
	}
}

// Benchmark source selection performance
func BenchmarkClient_SelectSource_Integration(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping integration benchmarks in short mode")
	}

	host := os.Getenv("SOUNDTOUCH_TEST_HOST")
	if host == "" {
		b.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration benchmarks")
	}

	// Parse host:port if provided
	finalHost, finalPort := parseHostPort(host)

	config := &Config{
		Host:      finalHost,
		Port:      finalPort,
		Timeout:   15 * time.Second,
		UserAgent: "Bose-SoundTouch-Go-Benchmark-Test/1.0",
	}

	client := NewClient(config)

	// Get available sources
	sources, err := client.GetSources()
	if err != nil {
		b.Fatalf("Failed to get sources: %v", err)
	}

	availableSources := sources.GetAvailableSources()
	if len(availableSources) == 0 {
		b.Skip("No available sources to benchmark with")
	}

	// Use first available source for benchmarking
	testSource := availableSources[0]

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		err := client.SelectSource(testSource.Source, testSource.SourceAccount)
		if err != nil {
			b.Fatalf("SelectSource failed: %v", err)
		}
	}
}

// parseHostPort is a helper function for integration tests
// This is a simple version for test use
func parseHostPort(hostPort string) (string, int) {
	if !containsSubstring(hostPort, ":") {
		return hostPort, speaker.HTTPPort
	}

	// Simple parsing - in real use, we'd use net.SplitHostPort
	parts := make([]string, 0, 2)
	current := ""

	for _, char := range hostPort {
		if char == ':' {
			parts = append(parts, current)
			current = ""
		} else {
			current += string(char)
		}
	}

	if current != "" {
		parts = append(parts, current)
	}

	if len(parts) == 2 {
		// Try to parse port
		port := speaker.HTTPPort
		portStr := parts[1]
		portInt := 0

		for _, char := range portStr {
			if char >= '0' && char <= '9' {
				portInt = portInt*10 + int(char-'0')
			} else {
				portInt = -1
				break
			}
		}

		if portInt > 0 && portInt <= 65535 {
			port = portInt
		}

		return parts[0], port
	}

	return hostPort, speaker.HTTPPort
}
