package config

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

// validMatrix is a reusable matrix config block for tests.
const validMatrix = `
[matrix]
homeserver = "https://matrix.example.org"
user_id = "@pete:example.org"
password = "testpass"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!tech:example.org"
politics = "!politics:example.org"
`

func TestLoadValid(t *testing.T) {
	toml := validMatrix + `
[storage]
db_path = "/tmp/test.db"

[[sources]]
name = "Test Source"
feed_url = "https://example.com/rss"
tier = 1
poll_interval_minutes = 20
direct_route = "tech"
enabled = true
`
	cfg := loadFromString(t, toml)

	if cfg.Matrix.Homeserver != "https://matrix.example.org" {
		t.Errorf("homeserver = %q", cfg.Matrix.Homeserver)
	}
	if cfg.Matrix.UserID != "@pete:example.org" {
		t.Errorf("user_id = %q", cfg.Matrix.UserID)
	}
	if len(cfg.Sources) != 1 {
		t.Fatalf("sources = %d, want 1", len(cfg.Sources))
	}
	if cfg.Sources[0].Name != "Test Source" {
		t.Errorf("source name = %q", cfg.Sources[0].Name)
	}
	if cfg.Sources[0].DirectRoute != "tech" {
		t.Errorf("direct_route = %q, want tech", cfg.Sources[0].DirectRoute)
	}
}

func TestEnvVarExpansion(t *testing.T) {
	t.Setenv("TEST_PETE_PASS", "secret_pass_123")

	toml := `
[matrix]
homeserver = "https://matrix.example.org"
user_id = "@pete:example.org"
password = "${TEST_PETE_PASS}"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!tech:example.org"

[storage]
db_path = "/tmp/test.db"
`
	cfg := loadFromString(t, toml)

	if cfg.Matrix.Password != "secret_pass_123" {
		t.Errorf("password = %q, want secret_pass_123", cfg.Matrix.Password)
	}
}

func TestDefaults(t *testing.T) {
	toml := validMatrix + `
[storage]
db_path = "/tmp/test.db"

[[sources]]
name = "S"
feed_url = "https://example.com/rss"
tier = 1
poll_interval_minutes = 20
direct_route = "tech"
enabled = true
`
	cfg := loadFromString(t, toml)

	if cfg.Matrix.DataDir != "./data" {
		t.Errorf("data_dir default = %q, want ./data", cfg.Matrix.DataDir)
	}
	if cfg.Matrix.DisplayName != "Pete" {
		t.Errorf("display_name default = %q, want Pete", cfg.Matrix.DisplayName)
	}
	if cfg.Posting.MinIntervalSeconds != 300 {
		t.Errorf("min_interval default = %d, want 300", cfg.Posting.MinIntervalSeconds)
	}
	if cfg.Posting.BurstCapCount != 3 {
		t.Errorf("burst_cap default = %d, want 3", cfg.Posting.BurstCapCount)
	}
	if cfg.Posting.BurstCapWindowSeconds != 1800 {
		t.Errorf("burst_window default = %d, want 1800", cfg.Posting.BurstCapWindowSeconds)
	}
	if cfg.Storage.RecentWindowHours != 24 {
		t.Errorf("recent_window default = %d, want 24", cfg.Storage.RecentWindowHours)
	}
}

func TestValidationErrors(t *testing.T) {
	tests := []struct {
		name string
		toml string
	}{
		{"missing homeserver", `
[matrix]
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
`},
		{"missing user_id", `
[matrix]
homeserver = "https://h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
`},
		{"missing password", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
`},
		{"no channels", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[storage]
db_path = "/tmp/t.db"
`},
		{"missing db_path", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
`},
		{"invalid tier", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[[sources]]
name = "S"
feed_url = "https://e.com/rss"
tier = 5
poll_interval_minutes = 20
direct_route = "tech"
enabled = true
`},
		{"missing source name", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[[sources]]
feed_url = "https://e.com/rss"
tier = 1
poll_interval_minutes = 20
direct_route = "tech"
enabled = true
`},
		{"enabled source missing direct_route", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[[sources]]
name = "S"
feed_url = "https://e.com/rss"
tier = 1
poll_interval_minutes = 20
enabled = true
`},
		{"push enabled without auth", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[web.push]
enabled = true
vapid_public_key = "pub"
vapid_private_key = "priv"
subject = "mailto:a@b.c"
`},
		{"push enabled missing keys", `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[web.auth]
enabled = true
issuer = "https://i"
client_id = "id"
client_secret = "secret"
redirect_url = "https://r/cb"
session_secret = "session_secret_16chars_long"
[web.push]
enabled = true
subject = "mailto:a@b.c"
`},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dir := t.TempDir()
			path := filepath.Join(dir, "config.toml")
			os.WriteFile(path, []byte(tt.toml), 0o644)
			_, err := Load(path)
			if err == nil {
				t.Error("expected validation error, got nil")
			}
		})
	}
}

func TestDisabledSourceSkipsDirectRouteCheck(t *testing.T) {
	// A disabled source without direct_route must not fail validation.
	toml := `
[matrix]
homeserver = "https://h"
user_id = "@p:h"
password = "pw"
pickle_key = "test_pickle_key_1234"
[matrix.channels]
tech = "!t:e"
[storage]
db_path = "/tmp/t.db"
[[sources]]
name = "Off"
feed_url = "https://e.com/rss"
tier = 1
poll_interval_minutes = 20
enabled = false
`
	cfg := loadFromString(t, toml)
	if cfg.Sources[0].DirectRoute != "" {
		t.Errorf("expected empty direct_route, got %q", cfg.Sources[0].DirectRoute)
	}
}

func loadFromString(t *testing.T, tomlContent string) *Config {
	t.Helper()
	dir := t.TempDir()
	path := filepath.Join(dir, "config.toml")
	if err := os.WriteFile(path, []byte(tomlContent), 0o644); err != nil {
		t.Fatal(err)
	}
	cfg, err := Load(path)
	if err != nil {
		t.Fatal(err)
	}
	return cfg
}
