package config_test

import (
	"context"
	"os"
	"runtime"
	"strings"
	"testing"

	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/config"
)

func TestLoadDefaults(t *testing.T) {
	t.Setenv("PORT", "")
	t.Setenv("ENVIRONMENT", "local")
	t.Setenv("REDIS_URL", "")
	t.Setenv("REDIS_STREAM_PREFIX", "")
	t.Setenv("ARTIFACT_STORE_PATH", "")
	t.Setenv("GIT_WORKSPACE_PATH", "")
	t.Setenv("PARSER_WORKERS", "")
	t.Setenv("PARSER_VERSION", "")

	cfg, err := config.Load("code-parser-service", 6002)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Port != 6002 {
		t.Fatalf("expected port 6002, got %d", cfg.Port)
	}
	if cfg.Address() != "0.0.0.0:6002" {
		t.Fatalf("unexpected address: %s", cfg.Address())
	}
	if cfg.Redis.URL != "redis://localhost:6379" {
		t.Fatalf("unexpected redis url: %s", cfg.Redis.URL)
	}
	if cfg.Redis.StreamPrefix != "" {
		t.Fatalf("expected empty stream prefix, got %q", cfg.Redis.StreamPrefix)
	}
	if cfg.Artifacts.StorePath != "./data/artifacts" {
		t.Fatalf("unexpected artifact store path: %s", cfg.Artifacts.StorePath)
	}
	if cfg.Git.WorkspacePath != "./data/repos" {
		t.Fatalf("unexpected workspace path: %s", cfg.Git.WorkspacePath)
	}
	if cfg.Parser.Workers != runtime.NumCPU() {
		t.Fatalf("expected %d workers, got %d", runtime.NumCPU(), cfg.Parser.Workers)
	}
	if cfg.Parser.Version != "v0.1.0" {
		t.Fatalf("unexpected parser version: %s", cfg.Parser.Version)
	}
}

func TestLoadEnvOverrides(t *testing.T) {
	t.Setenv("ENVIRONMENT", "local")
	t.Setenv("REDIS_URL", "redis://redis.example:6380")
	t.Setenv("REDIS_STREAM_PREFIX", "staging.")
	t.Setenv("ARTIFACT_STORE_PATH", "/var/artifacts")
	t.Setenv("GIT_WORKSPACE_PATH", "/var/repos")
	t.Setenv("PARSER_WORKERS", "8")
	t.Setenv("PARSER_VERSION", "v1.2.3")

	cfg, err := config.Load("code-parser-service", 6002)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Redis.URL != "redis://redis.example:6380" {
		t.Fatalf("unexpected redis url: %s", cfg.Redis.URL)
	}
	if cfg.Redis.StreamPrefix != "staging." {
		t.Fatalf("unexpected stream prefix: %s", cfg.Redis.StreamPrefix)
	}
	if cfg.Artifacts.StorePath != "/var/artifacts" {
		t.Fatalf("unexpected artifact store path: %s", cfg.Artifacts.StorePath)
	}
	if cfg.Git.WorkspacePath != "/var/repos" {
		t.Fatalf("unexpected workspace path: %s", cfg.Git.WorkspacePath)
	}
	if cfg.Parser.Workers != 8 {
		t.Fatalf("unexpected workers: %d", cfg.Parser.Workers)
	}
	if cfg.Parser.Version != "v1.2.3" {
		t.Fatalf("unexpected parser version: %s", cfg.Parser.Version)
	}
}

func TestValidateNonLocalMissingFields(t *testing.T) {
	t.Setenv("ENVIRONMENT", "staging")
	t.Setenv("REDIS_URL", "")
	t.Setenv("MONGO_URI", "")
	t.Setenv("MONGODB_DATABASE", "")
	t.Setenv("GIT_WORKSPACE_PATH", "")

	_, err := config.Load("code-parser-service", 6002)
	if err == nil {
		t.Fatal("expected validation error")
	}
	msg := err.Error()
	for _, field := range []string{"REDIS_URL", "MONGO_URI", "MONGODB_DATABASE", "GIT_WORKSPACE_PATH"} {
		if !strings.Contains(msg, field) {
			t.Fatalf("expected error to mention %s, got: %s", field, msg)
		}
	}
}

func TestValidateNonLocalOK(t *testing.T) {
	t.Setenv("ENVIRONMENT", "staging")
	t.Setenv("REDIS_URL", "redis://redis.example:6379")
	t.Setenv("MONGO_URI", "mongodb://mongo.example:27018/adpilot_code_parser")
	t.Setenv("MONGODB_DATABASE", "adpilot_code_parser")
	t.Setenv("GIT_WORKSPACE_PATH", "/var/repos")

	cfg, err := config.Load("code-parser-service", 6002)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Environment != "staging" {
		t.Fatalf("expected staging environment, got %s", cfg.Environment)
	}
}

func TestRedisStreamName(t *testing.T) {
	tests := []struct {
		name   string
		prefix string
		base   string
		want   string
	}{
		{name: "no prefix", prefix: "", base: "repo.snapshot.ready", want: "repo.snapshot.ready"},
		{name: "prefix without dot", prefix: "staging", base: "files.changed", want: "staging.files.changed"},
		{name: "prefix with dot", prefix: "staging.", base: "commits.changed", want: "staging.commits.changed"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := config.RedisConfig{StreamPrefix: tt.prefix}
			if got := r.StreamName(tt.base); got != tt.want {
				t.Fatalf("StreamName() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestParserWorkersDefault(t *testing.T) {
	t.Setenv("ENVIRONMENT", "local")
	t.Setenv("PARSER_WORKERS", "")

	cfg, err := config.Load("code-parser-service", 6002)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Parser.Workers != runtime.NumCPU() {
		t.Fatalf("expected %d workers, got %d", runtime.NumCPU(), cfg.Parser.Workers)
	}
}

func TestParserWorkersInvalid(t *testing.T) {
	t.Setenv("ENVIRONMENT", "local")

	t.Setenv("PARSER_WORKERS", "0")
	if _, err := config.Load("code-parser-service", 6002); err == nil {
		t.Fatal("expected error for PARSER_WORKERS=0")
	}

	t.Setenv("PARSER_WORKERS", "abc")
	if _, err := config.Load("code-parser-service", 6002); err == nil {
		t.Fatal("expected error for invalid PARSER_WORKERS")
	}
}

func TestGitWorkspaceEnsureAndReady(t *testing.T) {
	dir := t.TempDir()
	gitCfg := config.GitConfig{WorkspacePath: dir}

	if err := gitCfg.EnsureWorkspace(); err != nil {
		t.Fatalf("EnsureWorkspace: %v", err)
	}
	if !gitCfg.Ready(context.Background()) {
		t.Fatal("expected ready workspace")
	}

	missing := config.GitConfig{WorkspacePath: dir + "/missing/nested/path"}
	if err := missing.EnsureWorkspace(); err != nil {
		t.Fatalf("EnsureWorkspace nested: %v", err)
	}
	if !missing.Ready(context.Background()) {
		t.Fatal("expected ready after nested create")
	}
}

func TestGitWorkspaceReadyMissingPath(t *testing.T) {
	empty := config.GitConfig{WorkspacePath: ""}
	if empty.Ready(context.Background()) {
		t.Fatal("expected not ready for empty path")
	}

	dir := t.TempDir()
	missingPath := dir + "/does-not-exist"
	gitCfg := config.GitConfig{WorkspacePath: missingPath}
	if gitCfg.Ready(context.Background()) {
		t.Fatal("expected not ready before EnsureWorkspace")
	}
}

func TestGitWorkspaceReadyNotDirectory(t *testing.T) {
	file := t.TempDir() + "/not-a-dir"
	if err := os.WriteFile(file, []byte("x"), 0o644); err != nil {
		t.Fatalf("write file: %v", err)
	}

	gitCfg := config.GitConfig{WorkspacePath: file}
	if gitCfg.Ready(context.Background()) {
		t.Fatal("expected not ready for file path")
	}
}
