package constants

import (
	"os"
	"strings"
	"testing"
	"time"
)

func TestGetConfig(t *testing.T) {
	// Test successful config loading
	cfg, err := GetConfig()
	if err != nil {
		t.Fatalf("GetConfig() failed: %v", err)
	}
	if cfg == nil {
		t.Fatal("GetConfig() returned nil config")
	}

	// Test that subsequent calls return the same instance (singleton)
	cfg2, err := GetConfig()
	if err != nil {
		t.Fatalf("Second GetConfig() call failed: %v", err)
	}

	// Both should be the same instance due to sync.Once
	if cfg != cfg2 {
		t.Error("GetConfig() should return the same instance (singleton)")
	}
}

func TestMustGetConfig(t *testing.T) {
	// This should not panic with valid environment
	cfg := MustGetConfig()
	if cfg == nil {
		t.Fatal("MustGetConfig() returned nil")
	}

	// Verify it returns a properly initialized config
	if cfg.Core.WorkerCount <= 0 {
		t.Errorf("Expected positive WorkerCount, got %d", cfg.Core.WorkerCount)
	}
	if cfg.Core.RefreshDelay <= 0 {
		t.Errorf("Expected positive RefreshDelay, got %v", cfg.Core.RefreshDelay)
	}
}

func TestMustGetConfig_Panic(t *testing.T) {
	// We can't easily test the panic scenario without breaking the singleton,
	// but we can test that MustGetConfig works normally
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("MustGetConfig() panicked unexpectedly: %v", r)
		}
	}()

	cfg := MustGetConfig()
	if cfg == nil {
		t.Fatal("MustGetConfig() returned nil without panicking")
	}
}

func TestConfigSingleton(t *testing.T) {
	// Reset the singleton for this test (this is a bit hacky but necessary for testing)
	// We'll create multiple goroutines to test concurrent access

	configs := make(chan interface{}, 10)

	// Launch multiple goroutines to call GetConfig concurrently
	for i := 0; i < 10; i++ {
		go func() {
			cfg, _ := GetConfig()
			configs <- cfg
		}()
	}

	// Collect all configs
	var allConfigs []interface{}
	for i := 0; i < 10; i++ {
		allConfigs = append(allConfigs, <-configs)
	}

	// All should be the same instance
	firstConfig := allConfigs[0]
	for i, cfg := range allConfigs {
		if cfg != firstConfig {
			t.Errorf("Config %d is different from first config", i)
		}
	}
}

func TestConstants_Values(t *testing.T) {
	// Test that constants have expected values
	if WorkerCount != 4 {
		t.Errorf("Expected WorkerCount=4, got %d", WorkerCount)
	}
	if RefreshDelay != 3 {
		t.Errorf("Expected RefreshDelay=3, got %d", RefreshDelay)
	}

	// Test HTTP constants
	if HTTPUserAgent == "" {
		t.Error("HTTPUserAgent should not be empty")
	}
	if !strings.Contains(HTTPUserAgent, "Mozilla") {
		t.Error("HTTPUserAgent should contain 'Mozilla'")
	}
	if REFERRER != "https://www.flomarching.com" {
		t.Errorf("Expected REFERRER='https://www.flomarching.com', got '%s'", REFERRER)
	}

	// Test default NAS constants
	if DefaultNASOutputPath != "\\\\HomeLabNAS\\dci\\streams" {
		t.Errorf("Expected DefaultNASOutputPath='\\\\HomeLabNAS\\dci\\streams', got '%s'", DefaultNASOutputPath)
	}
	if DefaultNASUsername != "NASAdmin" {
		t.Errorf("Expected DefaultNASUsername='NASAdmin', got '%s'", DefaultNASUsername)
	}

	// Test transfer constants
	if DefaultTransferWorkerCount != 2 {
		t.Errorf("Expected DefaultTransferWorkerCount=2, got %d", DefaultTransferWorkerCount)
	}
	if DefaultTransferRetryLimit != 3 {
		t.Errorf("Expected DefaultTransferRetryLimit=3, got %d", DefaultTransferRetryLimit)
	}
	if DefaultTransferTimeout != 30 {
		t.Errorf("Expected DefaultTransferTimeout=30, got %d", DefaultTransferTimeout)
	}
	if DefaultFileSettlingDelay != 5 {
		t.Errorf("Expected DefaultFileSettlingDelay=5, got %d", DefaultFileSettlingDelay)
	}
	if DefaultTransferQueueSize != 100000 {
		t.Errorf("Expected DefaultTransferQueueSize=100000, got %d", DefaultTransferQueueSize)
	}
	if DefaultBatchSize != 1000 {
		t.Errorf("Expected DefaultBatchSize=1000, got %d", DefaultBatchSize)
	}

	// Test cleanup constants
	if DefaultCleanupBatchSize != 1000 {
		t.Errorf("Expected DefaultCleanupBatchSize=1000, got %d", DefaultCleanupBatchSize)
	}
	if DefaultRetainLocalHours != 0 {
		t.Errorf("Expected DefaultRetainLocalHours=0, got %d", DefaultRetainLocalHours)
	}

	// Test processing constants
	if DefaultProcessWorkerCount != 2 {
		t.Errorf("Expected DefaultProcessWorkerCount=2, got %d", DefaultProcessWorkerCount)
	}
	if DefaultFFmpegPath != "ffmpeg" {
		t.Errorf("Expected DefaultFFmpegPath='ffmpeg', got '%s'", DefaultFFmpegPath)
	}
}

func TestConfig_Integration(t *testing.T) {
	cfg := MustGetConfig()

	// Test that config values match or override constants appropriately
	if cfg.Core.WorkerCount != WorkerCount && os.Getenv("WORKER_COUNT") == "" {
		t.Errorf("Config WorkerCount (%d) should match constant (%d) when no env override", cfg.Core.WorkerCount, WorkerCount)
	}

	if cfg.Core.RefreshDelay != time.Duration(RefreshDelay)*time.Second && os.Getenv("REFRESH_DELAY_SECONDS") == "" {
		t.Errorf("Config RefreshDelay (%v) should match constant (%v) when no env override", cfg.Core.RefreshDelay, time.Duration(RefreshDelay)*time.Second)
	}

	// Test HTTP settings
	if cfg.HTTP.UserAgent != HTTPUserAgent {
		t.Errorf("Config UserAgent (%s) should match constant (%s)", cfg.HTTP.UserAgent, HTTPUserAgent)
	}
	if cfg.HTTP.Referer != REFERRER {
		t.Errorf("Config Referer (%s) should match constant (%s)", cfg.HTTP.Referer, REFERRER)
	}
}

func TestConfig_PathMethods(t *testing.T) {
	cfg := MustGetConfig()

	testEvent := "test-event-123"
	testQuality := "1080p"

	// Test GetEventPath
	eventPath := cfg.GetEventPath(testEvent)
	if !strings.Contains(eventPath, testEvent) {
		t.Errorf("GetEventPath should contain event name '%s', got: %s", testEvent, eventPath)
	}

	// Test GetManifestPath
	manifestPath := cfg.GetManifestPath(testEvent)
	if !strings.Contains(manifestPath, testEvent) {
		t.Errorf("GetManifestPath should contain event name '%s', got: %s", testEvent, manifestPath)
	}
	if !strings.HasSuffix(manifestPath, ".json") {
		t.Errorf("GetManifestPath should end with '.json', got: %s", manifestPath)
	}

	// Test GetNASEventPath
	nasPath := cfg.GetNASEventPath(testEvent)
	if !strings.Contains(nasPath, testEvent) {
		t.Errorf("GetNASEventPath should contain event name '%s', got: %s", testEvent, nasPath)
	}

	// Test GetProcessOutputPath
	processPath := cfg.GetProcessOutputPath(testEvent)
	if !strings.Contains(processPath, testEvent) {
		t.Errorf("GetProcessOutputPath should contain event name '%s', got: %s", testEvent, processPath)
	}

	// Test GetQualityPath
	qualityPath := cfg.GetQualityPath(testEvent, testQuality)
	if !strings.Contains(qualityPath, testEvent) {
		t.Errorf("GetQualityPath should contain event name '%s', got: %s", testEvent, qualityPath)
	}
	if !strings.Contains(qualityPath, testQuality) {
		t.Errorf("GetQualityPath should contain quality '%s', got: %s", testQuality, qualityPath)
	}
}

func TestConfig_DefaultValues(t *testing.T) {
	cfg := MustGetConfig()

	// Test that default values are reasonable
	if cfg.Transfer.QueueSize != DefaultTransferQueueSize {
		t.Errorf("Expected transfer queue size %d, got %d", DefaultTransferQueueSize, cfg.Transfer.QueueSize)
	}

	if cfg.Transfer.BatchSize != DefaultBatchSize {
		t.Errorf("Expected transfer batch size %d, got %d", DefaultBatchSize, cfg.Transfer.BatchSize)
	}

	if cfg.Processing.WorkerCount != DefaultProcessWorkerCount {
		t.Errorf("Expected processing worker count %d, got %d", DefaultProcessWorkerCount, cfg.Processing.WorkerCount)
	}

	if cfg.Cleanup.BatchSize != DefaultCleanupBatchSize {
		t.Errorf("Expected cleanup batch size %d, got %d", DefaultCleanupBatchSize, cfg.Cleanup.BatchSize)
	}
}
