package config

import (
	"context"
	"fmt"
	"os"
	"strconv"
	"strings"

	awsconfig "github.com/aws/aws-sdk-go-v2/config"
	"github.com/warmbly/warmbly/internal/infrastructure/secrets"
	"github.com/warmbly/warmbly/internal/infrastructure/ssm"
)

type Config struct {
	Env              string // "dev" or "prod"
	AWSConfigEnabled bool
	params           *ssm.SSMParameterStore
	secrets          *secrets.SecretsManagerClient
}

// NewConfig creates a new config instance with env-first loading and optional AWS fallback.
// AWS clients are only initialized if AWS_CONFIG_ENABLED=true.
func NewConfig(ctx context.Context) (*Config, error) {
	env := getEnvOrDefault("APP_ENV", "dev")
	awsEnabled := getEnvOrDefault("AWS_CONFIG_ENABLED", "false") == "true"

	cfg := &Config{
		Env:              env,
		AWSConfigEnabled: awsEnabled,
	}

	if awsEnabled {
		awsCfg, err := awsconfig.LoadDefaultConfig(ctx)
		if err != nil {
			return nil, fmt.Errorf("failed to load AWS config: %w", err)
		}

		params, err := ssm.NewSSMParameterStore(ctx, awsCfg)
		if err != nil {
			return nil, fmt.Errorf("failed to create SSM client: %w", err)
		}
		cfg.params = params

		secretsClient, err := secrets.NewSecretsManagerClient(ctx, awsCfg)
		if err != nil {
			return nil, fmt.Errorf("failed to create Secrets Manager client: %w", err)
		}
		cfg.secrets = secretsClient
	}

	return cfg, nil
}

// Load creates a Config with pre-initialized AWS clients (legacy compatibility).
// Deprecated: Use NewConfig for new code.
func Load(params *ssm.SSMParameterStore, secrets *secrets.SecretsManagerClient) *Config {
	env := getEnvOrDefault("APP_ENV", "dev")

	return &Config{
		Env:              env,
		AWSConfigEnabled: true, // Legacy mode always has AWS enabled
		params:           params,
		secrets:          secrets,
	}
}

// GetKeyID returns the full AWS parameter/secret path with environment prefix.
func (c *Config) GetKeyID(s string) string {
	return "/warmbly/" + c.Env + "/" + s
}

// getEnvOrDefault returns the environment variable value or a default if not set.
func getEnvOrDefault(key, defaultVal string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return defaultVal
}

// GetString retrieves a configuration value, checking env var first, then AWS SSM if enabled.
func (c *Config) GetString(ctx context.Context, envKey, awsKey string) (string, error) {
	if val := os.Getenv(envKey); val != "" {
		return val, nil
	}
	if c.AWSConfigEnabled && c.params != nil {
		return c.params.Get(ctx, c.GetKeyID(awsKey))
	}
	return "", fmt.Errorf("config %s not found (env: %s, aws: %s)", envKey, envKey, awsKey)
}

// GetStringRaw retrieves a configuration value from AWS SSM using the raw key (no environment prefix).
func (c *Config) GetStringRaw(ctx context.Context, envKey, awsKey string) (string, error) {
	if val := os.Getenv(envKey); val != "" {
		return val, nil
	}
	if c.AWSConfigEnabled && c.params != nil {
		return c.params.Get(ctx, awsKey)
	}
	return "", fmt.Errorf("config %s not found (env: %s, aws: %s)", envKey, envKey, awsKey)
}

// GetSecret retrieves a secret value, checking env var first, then AWS Secrets Manager if enabled.
func (c *Config) GetSecret(ctx context.Context, envKey, awsKey string) (string, error) {
	if val := os.Getenv(envKey); val != "" {
		return val, nil
	}
	if c.AWSConfigEnabled && c.secrets != nil {
		return c.secrets.Get(ctx, c.GetKeyID(awsKey))
	}
	return "", fmt.Errorf("secret %s not found (env: %s, aws: %s)", envKey, envKey, awsKey)
}

// GetSecretRaw retrieves a secret value from AWS Secrets Manager using the raw key (no environment prefix).
func (c *Config) GetSecretRaw(ctx context.Context, envKey, awsKey string) (string, error) {
	if val := os.Getenv(envKey); val != "" {
		return val, nil
	}
	if c.AWSConfigEnabled && c.secrets != nil {
		return c.secrets.Get(ctx, awsKey)
	}
	return "", fmt.Errorf("secret %s not found (env: %s, aws: %s)", envKey, envKey, awsKey)
}

// GetStringOptional retrieves an optional configuration value with a default fallback.
func (c *Config) GetStringOptional(ctx context.Context, envKey, awsKey, defaultVal string) string {
	if val := os.Getenv(envKey); val != "" {
		return val
	}
	if c.AWSConfigEnabled && c.params != nil {
		if val, err := c.params.Get(ctx, c.GetKeyID(awsKey)); err == nil && val != "" {
			return val
		}
	}
	return defaultVal
}

// GetBoolOptional retrieves an optional boolean configuration value, parsed
// with strconv.ParseBool ("1", "t", "true", "0", "false", ...). An unset or
// unparseable value returns the default.
func (c *Config) GetBoolOptional(ctx context.Context, envKey, awsKey string, defaultVal bool) bool {
	raw := c.GetStringOptional(ctx, envKey, awsKey, "")
	if raw == "" {
		return defaultVal
	}
	if b, err := strconv.ParseBool(strings.TrimSpace(raw)); err == nil {
		return b
	}
	return defaultVal
}

// GetBoolPtr retrieves an optional boolean, returning nil when the value is
// unset (rather than a default), so a caller can distinguish "not configured"
// from an explicit false and fall back to its own default.
func (c *Config) GetBoolPtr(ctx context.Context, envKey, awsKey string) *bool {
	raw := c.GetStringOptional(ctx, envKey, awsKey, "")
	if strings.TrimSpace(raw) == "" {
		return nil
	}
	b, err := strconv.ParseBool(strings.TrimSpace(raw))
	if err != nil {
		return nil
	}
	return &b
}

// GetStringOptionalRaw retrieves an optional configuration value using raw AWS key.
func (c *Config) GetStringOptionalRaw(ctx context.Context, envKey, awsKey, defaultVal string) string {
	if val := os.Getenv(envKey); val != "" {
		return val
	}
	if c.AWSConfigEnabled && c.params != nil {
		if val, err := c.params.Get(ctx, awsKey); err == nil && val != "" {
			return val
		}
	}
	return defaultVal
}

// GetSecretOptional retrieves an optional secret value with a default fallback.
func (c *Config) GetSecretOptional(ctx context.Context, envKey, awsKey, defaultVal string) string {
	if val := os.Getenv(envKey); val != "" {
		return val
	}
	if c.AWSConfigEnabled && c.secrets != nil {
		if val, err := c.secrets.Get(ctx, c.GetKeyID(awsKey)); err == nil && val != "" {
			return val
		}
	}
	return defaultVal
}

// GetSecretOptionalRaw retrieves an optional secret value using raw AWS key.
func (c *Config) GetSecretOptionalRaw(ctx context.Context, envKey, awsKey, defaultVal string) string {
	if val := os.Getenv(envKey); val != "" {
		return val
	}
	if c.AWSConfigEnabled && c.secrets != nil {
		if val, err := c.secrets.Get(ctx, awsKey); err == nil && val != "" {
			return val
		}
	}
	return defaultVal
}
