package config_test

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

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.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("CONSUMER_GROUP", "")
	t.Setenv("MONGO_URI", "")
	t.Setenv("MONGODB_DATABASE", "")
	t.Setenv("DELTAS_MONGODB_DATABASE", "")
	t.Setenv("INDEXING_RUNS_DATABASE", "")
	t.Setenv("ARTIFACT_STORE_PATH", "")
	t.Setenv("ANALYSIS_WORKERS", "")
	t.Setenv("SERVICE_VERSION", "")

	cfg, err := config.Load("commit-intelligence-service", 6003)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Port != 6003 {
		t.Fatalf("expected port 6003, got %d", cfg.Port)
	}
	if cfg.Address() != "0.0.0.0:6003" {
		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.Redis.ConsumerGroup != "commit-intelligence-service" {
		t.Fatalf("unexpected consumer group: %s", cfg.Redis.ConsumerGroup)
	}
	if cfg.Mongo.URI != "mongodb://localhost:27018/adpilot_commit_intel" {
		t.Fatalf("unexpected mongo uri: %s", cfg.Mongo.URI)
	}
	if cfg.Mongo.Database != "adpilot_commit_intel" {
		t.Fatalf("unexpected mongo database: %s", cfg.Mongo.Database)
	}
	if cfg.Mongo.DeltasDatabase != "adpilot_code_parser" {
		t.Fatalf("unexpected deltas database: %s", cfg.Mongo.DeltasDatabase)
	}
	if cfg.Mongo.IndexingRunsEnabled != true {
		t.Fatal("expected indexing runs enabled by default")
	}
	if cfg.Stream.MaxRetries != 5 {
		t.Fatalf("unexpected stream max retries: %d", cfg.Stream.MaxRetries)
	}
	if cfg.Artifacts.StorePath != "./data/artifacts" {
		t.Fatalf("unexpected artifact store path: %s", cfg.Artifacts.StorePath)
	}
	if cfg.Analysis.Workers != runtime.NumCPU() {
		t.Fatalf("expected %d workers, got %d", runtime.NumCPU(), cfg.Analysis.Workers)
	}
	if cfg.Analysis.ServiceVersion != "v0.1.0" {
		t.Fatalf("unexpected service version: %s", cfg.Analysis.ServiceVersion)
	}
}

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("CONSUMER_GROUP", "custom-group")
	t.Setenv("ARTIFACT_STORE_PATH", "/var/artifacts")
	t.Setenv("ANALYSIS_WORKERS", "4")
	t.Setenv("SERVICE_VERSION", "v1.2.3")

	cfg, err := config.Load("commit-intelligence-service", 6003)
	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.Redis.ConsumerGroup != "custom-group" {
		t.Fatalf("unexpected consumer group: %s", cfg.Redis.ConsumerGroup)
	}
	if cfg.Artifacts.StorePath != "/var/artifacts" {
		t.Fatalf("unexpected artifact store path: %s", cfg.Artifacts.StorePath)
	}
	if cfg.Analysis.Workers != 4 {
		t.Fatalf("unexpected workers: %d", cfg.Analysis.Workers)
	}
	if cfg.Analysis.ServiceVersion != "v1.2.3" {
		t.Fatalf("unexpected service version: %s", cfg.Analysis.ServiceVersion)
	}
}

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

	_, err := config.Load("commit-intelligence-service", 6003)
	if err == nil {
		t.Fatal("expected validation error")
	}
	msg := err.Error()
	for _, field := range []string{"REDIS_URL", "MONGO_URI", "MONGODB_DATABASE"} {
		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:27017/adpilot_commit_intel")
	t.Setenv("MONGODB_DATABASE", "adpilot_commit_intel")

	cfg, err := config.Load("commit-intelligence-service", 6003)
	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: "graph.delta.ready", want: "graph.delta.ready"},
		{name: "prefix without dot", prefix: "staging", base: "commit.analysis.ready", want: "staging.commit.analysis.ready"},
		{name: "prefix with dot", prefix: "staging.", base: "graph.artifact.ready", want: "staging.graph.artifact.ready"},
	}

	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 TestAnalysisWorkersDefault(t *testing.T) {
	t.Setenv("ENVIRONMENT", "local")
	t.Setenv("ANALYSIS_WORKERS", "")

	cfg, err := config.Load("commit-intelligence-service", 6003)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if cfg.Analysis.Workers != runtime.NumCPU() {
		t.Fatalf("expected %d workers, got %d", runtime.NumCPU(), cfg.Analysis.Workers)
	}
}

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

	t.Setenv("ANALYSIS_WORKERS", "0")
	if _, err := config.Load("commit-intelligence-service", 6003); err == nil {
		t.Fatal("expected error for ANALYSIS_WORKERS=0")
	}

	t.Setenv("ANALYSIS_WORKERS", "abc")
	if _, err := config.Load("commit-intelligence-service", 6003); err == nil {
		t.Fatal("expected error for invalid ANALYSIS_WORKERS")
	}
}

func TestArtifactsEnsureStorePathAndReady(t *testing.T) {
	dir := t.TempDir()
	artifacts := config.ArtifactsConfig{StorePath: dir}

	if err := artifacts.EnsureStorePath(); err != nil {
		t.Fatalf("EnsureStorePath: %v", err)
	}
	if !artifacts.Ready(context.Background()) {
		t.Fatal("expected ready artifact store")
	}

	nested := config.ArtifactsConfig{StorePath: dir + "/nested/store"}
	if err := nested.EnsureStorePath(); err != nil {
		t.Fatalf("EnsureStorePath nested: %v", err)
	}
	if !nested.Ready(context.Background()) {
		t.Fatal("expected ready after nested create")
	}
}

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

	dir := t.TempDir()
	missing := config.ArtifactsConfig{StorePath: dir + "/does-not-exist"}
	if missing.Ready(context.Background()) {
		t.Fatal("expected not ready before EnsureStorePath")
	}
}

func TestArtifactsReadyNotDirectory(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)
	}

	artifacts := config.ArtifactsConfig{StorePath: file}
	if artifacts.Ready(context.Background()) {
		t.Fatal("expected not ready for file path")
	}
}
