package config

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

const (
	defaultRedisURL              = "redis://localhost:6379"
	defaultGitWorkspacePath      = "./data/repos"
	defaultBitbucketBaseURL      = "https://bit.admedia.com"
	defaultMongoURI              = "mongodb://localhost:27018/adpilot_repo_sync"
	defaultMongoDatabase         = "adpilot_repo_sync"
	defaultCodeParserDatabase    = "adpilot_code_parser"
	defaultCommitIntelDatabase   = "adpilot_commit_intel"
	defaultEmbeddingDatabase     = "adpilot_indexing"
	defaultDocsDatabase          = "repo_docs"
	defaultEmbeddingEngineURL    = "http://localhost:6004"
	localEnvironment             = "local"
)

// Config holds runtime configuration for the service.
type Config struct {
	ServiceName string
	Host        string
	Port        int
	LogLevel    string
	LogFile     string
	Environment string
	Redis       RedisConfig
	Git         GitConfig
	Bitbucket   BitbucketConfig
	Mongo       MongoConfig
	Pipeline    PipelineConfig
	ForceSync   ForceSyncConfig
	Gateway     GatewayConfig
}

// PipelineConfig holds cross-database Mongo purge settings for force sync.
type PipelineConfig struct {
	MongoURI            string
	RepoSyncDatabase    string
	CodeParserDatabase  string
	CommitIntelDatabase string
	EmbeddingDatabase   string
	DocsDatabase        string
}

// ForceSyncConfig holds settings for purge side effects during force sync.
type ForceSyncConfig struct {
	EmbeddingEngineURL    string
	GatewayInternalSecret string
}

// GatewayConfig holds trusted gateway integration settings.
type GatewayConfig struct {
	InternalServiceSecret string
}

// MongoConfig holds MongoDB metadata store settings.
type MongoConfig struct {
	URI      string
	Database string
}

// RedisConfig holds Redis Streams connection settings.
type RedisConfig struct {
	URL          string
	StreamPrefix string
}

// GitConfig holds local Git workspace settings.
type GitConfig struct {
	WorkspacePath string
}

// BitbucketConfig holds Bitbucket Server integration settings.
type BitbucketConfig struct {
	BaseURL       string
	Username      string
	Token         string
	WebhookSecret string
}

// StreamName returns the Redis stream name with an optional environment prefix.
func (r RedisConfig) StreamName(base string) string {
	if r.StreamPrefix == "" {
		return base
	}
	prefix := r.StreamPrefix
	if !strings.HasSuffix(prefix, ".") {
		prefix += "."
	}
	return prefix + base
}

// Load reads configuration from environment variables with defaults.
func Load(serviceName string, defaultPort int) (Config, error) {
	port := defaultPort
	if raw := os.Getenv("PORT"); raw != "" {
		value, err := strconv.Atoi(raw)
		if err != nil {
			return Config{}, fmt.Errorf("invalid PORT: %w", err)
		}
		port = value
	}

	environment := getenv("ENVIRONMENT", localEnvironment)

	cfg := Config{
		ServiceName: serviceName,
		Host:        getenv("HOST", "0.0.0.0"),
		Port:        port,
		LogLevel:    getenv("LOG_LEVEL", "info"),
		LogFile:     strings.TrimSpace(os.Getenv("LOG_FILE")),
		Environment: environment,
		Redis: RedisConfig{
			URL:          loadWithLocalDefault("REDIS_URL", defaultRedisURL, environment),
			StreamPrefix: os.Getenv("REDIS_STREAM_PREFIX"),
		},
		Git: GitConfig{
			WorkspacePath: loadWithLocalDefault("GIT_WORKSPACE_PATH", defaultGitWorkspacePath, environment),
		},
		Bitbucket: BitbucketConfig{
			BaseURL:       loadWithLocalDefault("BITBUCKET_BASE_URL", defaultBitbucketBaseURL, environment),
			Username:      os.Getenv("BITBUCKET_USERNAME"),
			Token:         os.Getenv("BITBUCKET_TOKEN"),
			WebhookSecret: os.Getenv("BITBUCKET_WEBHOOK_SECRET"),
		},
		Mongo: MongoConfig{
			URI:      loadWithLocalDefault("MONGO_URI", defaultMongoURI, environment),
			Database: loadWithLocalDefault("MONGODB_DATABASE", defaultMongoDatabase, environment),
		},
		Gateway: GatewayConfig{
			InternalServiceSecret: strings.TrimSpace(os.Getenv("GATEWAY_INTERNAL_SERVICE_SECRET")),
		},
	}

	mongoURI := cfg.Mongo.URI
	cfg.Pipeline = PipelineConfig{
		MongoURI:            loadWithLocalDefault("PIPELINE_MONGO_URI", mongoURI, environment),
		RepoSyncDatabase:    cfg.Mongo.Database,
		CodeParserDatabase:  loadWithLocalDefault("CODE_PARSER_MONGO_DATABASE", defaultCodeParserDatabase, environment),
		CommitIntelDatabase: loadWithLocalDefault("COMMIT_INTEL_MONGO_DATABASE", defaultCommitIntelDatabase, environment),
		EmbeddingDatabase:   loadWithLocalDefault("EMBEDDING_MONGO_DATABASE", defaultEmbeddingDatabase, environment),
		DocsDatabase:        loadWithLocalDefault("DOCS_MONGO_DATABASE", defaultDocsDatabase, environment),
	}
	cfg.ForceSync = ForceSyncConfig{
		EmbeddingEngineURL:    loadWithLocalDefault("EMBEDDING_ENGINE_URL", defaultEmbeddingEngineURL, environment),
		GatewayInternalSecret: os.Getenv("GATEWAY_INTERNAL_SERVICE_SECRET"),
	}

	if cfg.Environment != localEnvironment {
		if err := cfg.Validate(); err != nil {
			return Config{}, err
		}
	}

	return cfg, nil
}

// Validate checks required configuration for non-local environments.
func (c Config) Validate() error {
	checks := []struct {
		env   string
		value string
	}{
		{"REDIS_URL", c.Redis.URL},
		{"GIT_WORKSPACE_PATH", c.Git.WorkspacePath},
		{"BITBUCKET_BASE_URL", c.Bitbucket.BaseURL},
		{"BITBUCKET_USERNAME", c.Bitbucket.Username},
		{"BITBUCKET_TOKEN", c.Bitbucket.Token},
		{"BITBUCKET_WEBHOOK_SECRET", c.Bitbucket.WebhookSecret},
		{"MONGO_URI", c.Mongo.URI},
		{"MONGODB_DATABASE", c.Mongo.Database},
	}

	var missing []string
	for _, check := range checks {
		if strings.TrimSpace(check.value) == "" {
			missing = append(missing, check.env)
		}
	}

	if len(missing) == 0 {
		return nil
	}
	return fmt.Errorf("missing required configuration: %s", strings.Join(missing, ", "))
}

func (c Config) Address() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

func getenv(key, fallback string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return fallback
}

func loadWithLocalDefault(key, fallback, environment string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	if environment == localEnvironment {
		return fallback
	}
	return ""
}
