package config

import (
	"os"
	"path/filepath"
	"testing"
	"time"
)

// validConfig returns a minimal valid YAML configuration using the
// multi-provider format.
func validConfig() string {
	return `
address: ":9090"
shutdown_timeout: "60s"

default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`
}

func writeConfig(t *testing.T, content string) string {
	t.Helper()
	dir := t.TempDir()
	path := filepath.Join(dir, "config.yaml")
	if err := os.WriteFile(path, []byte(content), 0644); err != nil {
		t.Fatalf("write config file: %v", err)
	}
	return path
}

func TestLoad(t *testing.T) {
	tests := []struct {
		name      string
		config    string
		setup     func(t *testing.T)
		shouldErr bool
		check     func(t *testing.T, cfg *Config)
	}{
		{
			name:   "valid config",
			config: validConfig(),
			check: func(t *testing.T, cfg *Config) {
				if cfg.Address != ":9090" {
					t.Errorf(
						"Address = %q, want %q",
						cfg.Address, ":9090",
					)
				}
				if cfg.ShutdownTimeout != "60s" {
					t.Errorf(
						"ShutdownTimeout = %q, want %q",
						cfg.ShutdownTimeout, "60s",
					)
				}
				if cfg.GetShutdownTimeout() != 60*time.Second {
					t.Errorf(
						"GetShutdownTimeout() = %v, want 60s",
						cfg.GetShutdownTimeout(),
					)
				}
			},
		},
		{
			name: "defaults applied",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			check: func(t *testing.T, cfg *Config) {
				if cfg.Address != ":8080" {
					t.Errorf(
						"Address = %q, want %q",
						cfg.Address, ":8080",
					)
				}
				if cfg.ShutdownTimeout != "30s" {
					t.Errorf(
						"ShutdownTimeout = %q, want %q",
						cfg.ShutdownTimeout, "30s",
					)
				}
				if cfg.GetShutdownTimeout() != 30*time.Second {
					t.Errorf(
						"GetShutdownTimeout() = %v, want 30s",
						cfg.GetShutdownTimeout(),
					)
				}
			},
		},
		{
			name: "env expansion",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080
    key: ${TEST_LLM_KEY}

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			setup: func(t *testing.T) {
				t.Setenv("TEST_LLM_KEY", "secret-api-key")
			},
			check: func(t *testing.T, cfg *Config) {
				if len(cfg.Providers) != 1 {
					t.Fatalf("len(Providers) = %d, want 1", len(cfg.Providers))
				}
				if cfg.Providers[0].Key != "secret-api-key" {
					t.Errorf(
						"Providers[0].Key = %q, want %q",
						cfg.Providers[0].Key, "secret-api-key",
					)
				}
			},
		},
		{
			name: "env expansion unset var becomes empty",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080
    key: ${TEST_UNSET_VAR}

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			check: func(t *testing.T, cfg *Config) {
				if len(cfg.Providers) != 1 {
					t.Fatalf("len(Providers) = %d, want 1", len(cfg.Providers))
				}
				if cfg.Providers[0].Key != "" {
					t.Errorf(
						"Providers[0].Key = %q, want empty",
						cfg.Providers[0].Key,
					)
				}
			},
		},
		{
			name:      "invalid yaml",
			config:    `providers: [invalid yaml`,
			shouldErr: true,
		},
		{
			name: "invalid shutdown_timeout",
			config: `
shutdown_timeout: "not-a-duration"

default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing default_model.provider",
			config: `
default_model:
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing default_model.model",
			config: `
default_model:
  provider: testprovider

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing provider url",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing stt url",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: ""

tts:
  url: http://localhost:8880
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing tts url",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  voice: af_heart
`,
			shouldErr: true,
		},
		{
			name: "missing tts voice",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
`,
			shouldErr: true,
		},
		{
			name: "config with tools",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

tools:
  - name: echo
    description: echoes input
    command: echo
    arguments:
      - "{{.message}}"
`,
			check: func(t *testing.T, cfg *Config) {
				if len(cfg.Tools) != 1 {
					t.Errorf(
						"len(Tools) = %d, want 1",
						len(cfg.Tools),
					)
				}
				if cfg.Tools[0].Name != "echo" {
					t.Errorf(
						"Tools[0].Name = %q, want %q",
						cfg.Tools[0].Name, "echo",
					)
				}
			},
		},
		{
			name: "invalid tool",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

tools:
  - name: ""
    description: missing name
    command: echo
`,
			shouldErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.setup != nil {
				tt.setup(t)
			}

			path := writeConfig(t, tt.config)
			cfg, err := Load(path)

			if tt.shouldErr {
				if err == nil {
					t.Fatal("Load() expected error")
				}
				return
			}

			if err != nil {
				t.Fatalf("Load() error = %v", err)
			}

			if tt.check != nil {
				tt.check(t, cfg)
			}
		})
	}
}

func TestLoad_FileNotFound(t *testing.T) {
	if _, err := Load("/nonexistent/path/config.yaml"); err == nil {
		t.Fatal("Load() expected error for missing file")
	}
}

func TestApplyDefaults(t *testing.T) {
	cfg := &Config{}
	cfg.ApplyDefaults()

	if cfg.Address != ":8080" {
		t.Errorf("Address = %q, want %q", cfg.Address, ":8080")
	}
	if cfg.ShutdownTimeout != "30s" {
		t.Errorf(
			"ShutdownTimeout = %q, want %q",
			cfg.ShutdownTimeout, "30s",
		)
	}
}

func TestApplyDefaults_NoOverwrite(t *testing.T) {
	cfg := &Config{
		Address:         ":9999",
		ShutdownTimeout: "120s",
	}
	cfg.ApplyDefaults()

	if cfg.Address != ":9999" {
		t.Errorf("Address = %q, want %q", cfg.Address, ":9999")
	}
	if cfg.ShutdownTimeout != "120s" {
		t.Errorf(
			"ShutdownTimeout = %q, want %q",
			cfg.ShutdownTimeout, "120s",
		)
	}
}

func TestLoad_Jobs(t *testing.T) {
	tests := []struct {
		name      string
		config    string
		shouldErr bool
		check     func(t *testing.T, cfg *Config)
	}{
		{
			name: "valid job config",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

jobs:
  - name: daily_summary
    schedule: "0 9 * * *"
    system_message: "You are a research assistant."
    task: "Summarize today's news about AI."
    timeout: "10m"
    max_iterations: 20
`,
			check: func(t *testing.T, cfg *Config) {
				if len(cfg.Jobs) != 1 {
					t.Fatalf("len(Jobs) = %d, want 1", len(cfg.Jobs))
				}
				j := cfg.Jobs[0]
				if j.Name != "daily_summary" {
					t.Errorf("Name = %q, want %q", j.Name, "daily_summary")
				}
				if j.Task != "Summarize today's news about AI." {
					t.Errorf("Task = %q, want %q", j.Task, "Summarize today's news about AI.")
				}
				if j.Schedule != "0 9 * * *" {
					t.Errorf("Schedule = %q, want %q", j.Schedule, "0 9 * * *")
				}
				timeout, err := time.ParseDuration(j.Timeout)
				if err != nil {
					t.Fatalf("ParseDuration(%q) error = %v", j.Timeout, err)
				}
				if timeout.Minutes() != 10 {
					t.Errorf("Timeout = %v, want 10m", timeout)
				}
				if j.MaxIterations != 20 {
					t.Errorf("MaxIterations = %d, want 20", j.MaxIterations)
				}
			},
		},
		{
			name: "job with minimal config (defaults)",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

jobs:
  - name: log_job
    schedule: "*/5 * * * *"
    task: "Check system health."
`,
			check: func(t *testing.T, cfg *Config) {
				if len(cfg.Jobs) != 1 {
					t.Fatalf("len(Jobs) = %d, want 1", len(cfg.Jobs))
				}
				j := cfg.Jobs[0]
				if j.MaxIterations != 0 {
					t.Errorf("MaxIterations = %d, want 0 (unlimited)", j.MaxIterations)
				}
			},
		},
		{
			name: "job missing name",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

jobs:
  - schedule: "0 9 * * *"
    task: "do something"
`,
			shouldErr: true,
		},
		{
			name: "job missing task",
			config: `
default_model:
  provider: testprovider
  model: test-model

providers:
  - name: testprovider
    url: http://localhost:8080

stt:
  url: http://localhost:8178

tts:
  url: http://localhost:8880
  voice: af_heart

jobs:
  - name: no_task
    schedule: "0 9 * * *"
`,
			shouldErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			path := writeConfig(t, tt.config)
			cfg, err := Load(path)

			if tt.shouldErr {
				if err == nil {
					t.Fatal("Load() expected error")
				}
				return
			}

			if err != nil {
				t.Fatalf("Load() error = %v", err)
			}

			if tt.check != nil {
				tt.check(t, cfg)
			}
		})
	}
}
