package config

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

// validConfig returns a minimal valid YAML configuration.
func validConfig() string {
	return `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: secret
  mailbox: INBOX

intake:
  mode: poll
  poll_interval: 30s

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587
`
}

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 with defaults",
			config: validConfig(),
			check: func(t *testing.T, cfg *Config) {
				if cfg.Concurrency != 1 {
					t.Errorf(
						"Concurrency = %d, want 1",
						cfg.Concurrency,
					)
				}
				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(),
					)
				}
				if cfg.SMTP.User != cfg.IMAP.User {
					t.Errorf(
						"SMTP.User = %q, want %q",
						cfg.SMTP.User, cfg.IMAP.User,
					)
				}
				if cfg.SMTP.Password != cfg.IMAP.Password {
					t.Errorf(
						"SMTP.Password not inherited from IMAP",
					)
				}
				if cfg.SMTP.From != cfg.IMAP.User {
					t.Errorf(
						"SMTP.From = %q, want %q",
						cfg.SMTP.From, cfg.IMAP.User,
					)
				}
				if cfg.Intake.Mode != "poll" {
					t.Errorf(
						"Intake.Mode = %q, want %q",
						cfg.Intake.Mode, "poll",
					)
				}
				if cfg.Intake.PollInterval != "30s" {
					t.Errorf(
						"Intake.PollInterval = %q, want %q",
						cfg.Intake.PollInterval, "30s",
					)
				}
			},
		},
		{
			name: "explicit values not overwritten",
			config: `
concurrency: 5
shutdown_timeout: "60s"

imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: secret
  mailbox: INBOX

intake:
  mode: idle

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587
  user: smtp-user
  password: smtp-password
  from: sender@example.com
`,
			check: func(t *testing.T, cfg *Config) {
				if cfg.Concurrency != 5 {
					t.Errorf(
						"Concurrency = %d, want 5",
						cfg.Concurrency,
					)
				}
				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(),
					)
				}
				if cfg.SMTP.User != "smtp-user" {
					t.Errorf(
						"SMTP.User = %q, want %q",
						cfg.SMTP.User, "smtp-user",
					)
				}
				if cfg.SMTP.Password != "smtp-password" {
					t.Errorf(
						"SMTP.Password = %q, want %q",
						cfg.SMTP.Password,
						"smtp-password",
					)
				}
				if cfg.SMTP.From != "sender@example.com" {
					t.Errorf(
						"SMTP.From = %q, want %q",
						cfg.SMTP.From,
						"sender@example.com",
					)
				}
			},
		},
		{
			name: "env expansion",
			config: `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: ${TEST_IMAP_PASSWORD}
  mailbox: INBOX

intake:
  mode: poll
  poll_interval: 30s

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587
`,
			setup: func(t *testing.T) {
				t.Setenv("TEST_IMAP_PASSWORD", "env-secret")
			},
			check: func(t *testing.T, cfg *Config) {
				if cfg.IMAP.Password != "env-secret" {
					t.Errorf(
						"IMAP.Password = %q, want %q",
						cfg.IMAP.Password,
						"env-secret",
					)
				}
			},
		},
		{
			name: "env expansion unset var becomes empty",
			config: `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: ${TEST_UNSET_VAR}
  mailbox: INBOX

intake:
  mode: poll
  poll_interval: 30s

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587
`,
			setup: func(t *testing.T) {
				t.Setenv("TEST_UNSET_VAR", "")
			},
			shouldErr: true,
		},
		{
			name:      "invalid yaml",
			config:    `imap: [invalid yaml`,
			shouldErr: true,
		},
		{
			name: "invalid shutdown_timeout",
			config: `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: secret
  mailbox: INBOX

intake:
  mode: poll
  poll_interval: 30s

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587

shutdown_timeout: "not-a-duration"
`,
			shouldErr: true,
		},
		{
			name: "config with tools",
			config: `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: secret
  mailbox: INBOX

intake:
  mode: poll
  poll_interval: 30s

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587

tools:
  - name: echo
    description: echoes input
    command: echo
`,
			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: "intake mode defaults when empty",
			config: `
imap:
  host: imap.example.com
  port: 993
  user: test@example.com
  password: secret
  mailbox: INBOX

llm:
  provider: llama.cpp
  model: qwen3-4b
  url: http://localhost:8080

smtp:
  host: smtp.example.com
  port: 587
`,
			check: func(t *testing.T, cfg *Config) {
				if cfg.Intake.Mode != "poll" {
					t.Errorf(
						"Intake.Mode = %q, want %q",
						cfg.Intake.Mode, "poll",
					)
				}
			},
		},
	}

	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")
	}
}
