package config

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

type Config struct {
	App           AppConfig
	HTTP          HTTPConfig
	Database      DatabaseConfig
	Auth          AuthConfig
	RateLimit     RateLimitConfig
	Security      SecurityConfig
	Cards         CardConfig
	KYC           KYCConfig
	Settlement    SettlementConfig
	Observability ObservabilityConfig
	Features      FeatureFlagsConfig
	Runtime       RuntimeConfig
	Providers     ProviderConfig
	Legal         LegalConfig
	GoLive        GoLiveConfig
}

type AppConfig struct {
	Name        string
	Environment string
}

type HTTPConfig struct {
	Addr            string
	MaxRequestBytes int64
}

type DatabaseConfig struct {
	URL             string
	MaxConns        int32
	MinConns        int32
	HealthCheckTime time.Duration
	RunMigrations   bool
	MigrationsDir   string
}

type AuthConfig struct {
	JWTSecret               string
	JWTIssuer               string
	AccessTokenTTL          time.Duration
	RefreshTokenTTL         time.Duration
	MFASecret               string
	StepUpTTL               time.Duration
	LoginIdentifierWindow   time.Duration
	LoginIdentifierFailures int
	LoginIPWindow           time.Duration
	LoginIPFailures         int
	LoginLockoutDuration    time.Duration
}

type RateLimitConfig struct {
	Enabled     bool
	Backend     string
	Window      time.Duration
	MaxRequests int
	FailOpen    bool
}

type SecurityConfig struct {
	SecretProvider       string
	KMSKeyReference      string
	WAFMode              string
	PublicBaseURL        string
	TLSRequired          bool
	HSTSEnabled          bool
	CSRFEnabled          bool
	AllowedHosts         []string
	TrustedProxyRequired bool
}

type CardConfig struct {
	Secret        string
	WebhookSecret string
}

type KYCConfig struct {
	WebhookSecret string
}

type SettlementConfig struct {
	SEPAEnabled bool
	Interval    time.Duration
	BatchSize   int
	MaxAttempts int
	RetryDelay  time.Duration
}

type ObservabilityConfig struct {
	MetricsEnabled            bool
	MetricsAllowedCIDRs       []string
	StructuredLogs            bool
	RequestCorrelationEnabled bool
	DashboardPackRef          string
	AlertRulesRef             string
	IncidentRunbookPackRef    string
	MajorIncidentContactsRef  string
	BCDRTestReference         string
	ChaosTestPlanRef          string
}

type FeatureFlagsConfig struct {
	Payments           bool
	Cards              bool
	FX                 bool
	Crypto             bool
	Savings            bool
	AdminMoneyMovement bool
}

type RuntimeConfig struct {
	HostingArchitecture  string
	NetworkSegmentation  string
	PrivateServiceAccess bool
	PostgresHAEnabled    bool
	PostgresPITREnabled  bool
	RestoreDrillRef      string
	ResourceProfile      string
	AutoscalingPolicy    string
	AsyncBackend         string
	DeploymentStrategy   string
	EmbeddedWorker       bool
}

type ProviderConfig struct {
	BankLedgerMode                 string
	CardIssuerMode                 string
	IdentityMode                   string
	SanctionsMode                  string
	CustodyMode                    string
	BlockchainAnalyticsMode        string
	MarketDataMode                 string
	PaymentRailMode                string
	BankLedgerName                 string
	CardIssuerName                 string
	IdentityName                   string
	SanctionsName                  string
	CustodyName                    string
	BlockchainAnalyticsName        string
	MarketDataName                 string
	PaymentRailName                string
	CryptoRealMovementEnabled      bool
	DefaultTimeout                 time.Duration
	MaxAttempts                    int
	RetryBaseDelay                 time.Duration
	CircuitBreakerFailureThreshold int
	CircuitBreakerCooldown         time.Duration
	DegradedMode                   string
	AuditEnabled                   bool
	WebhookTolerance               time.Duration
	ApprovalReference              string
}

type LegalConfig struct {
	ApprovalReference            string
	RegulatedActivitiesReference string
	OperatingModelReference      string
	LegalMemoPackReference       string
	RestrictedTermsReference     string
	ProviderContractsReference   string
	SafeguardingModelReference   string
	PolicyPackReference          string
	JurisdictionRulesReference   string
}

type GoLiveConfig struct {
	StagingSecretsReference         string
	ProviderWebhookE2EReference     string
	StagingObservabilityReference   string
	SeedUsersReference              string
	GateAReference                  string
	CICDExerciseReference           string
	SecurityScanningReference       string
	BetaLegalWordingReference       string
	SupportIncidentChannelsRef      string
	KnownRiskRegisterReference      string
	LegalLicensingApprovalReference string
	ProviderProductionReference     string
	ReconciliationLiveReference     string
	SecurityReviewReference         string
	KYCAMLOpsReference              string
	DORAApprovalReference           string
	PCIValidationReference          string
	BCDRProductionEvidenceReference string
	ExecutiveSignoffReference       string
}

func Load() (Config, error) {
	cfg := Config{
		App: AppConfig{
			Name:        env("BANKING_APP_NAME", "banking-api"),
			Environment: normalizeEnvironment(env("BANKING_APP_ENV", "development")),
		},
		HTTP: HTTPConfig{
			Addr:            env("BANKING_HTTP_ADDR", ":8080"),
			MaxRequestBytes: envInt64("BANKING_MAX_REQUEST_BYTES", 1<<20),
		},
		Database: DatabaseConfig{
			URL:             env("BANKING_DATABASE_URL", "postgres://banking:banking@localhost:5432/banking?sslmode=disable"),
			MaxConns:        int32(envInt("BANKING_DB_MAX_CONNS", 20)),
			MinConns:        int32(envInt("BANKING_DB_MIN_CONNS", 2)),
			HealthCheckTime: envDuration("BANKING_DB_HEALTHCHECK_INTERVAL", 30*time.Second),
			RunMigrations:   envBool("BANKING_RUN_MIGRATIONS", true),
			MigrationsDir:   env("BANKING_MIGRATIONS_DIR", "migrations"),
		},
		Auth: AuthConfig{
			JWTSecret:               env("BANKING_JWT_SECRET", ""),
			JWTIssuer:               env("BANKING_JWT_ISSUER", "banking-api"),
			AccessTokenTTL:          envDuration("BANKING_ACCESS_TOKEN_TTL", 15*time.Minute),
			RefreshTokenTTL:         envDuration("BANKING_REFRESH_TOKEN_TTL", 30*24*time.Hour),
			MFASecret:               env("BANKING_MFA_SECRET", ""),
			StepUpTTL:               envDuration("BANKING_STEP_UP_TTL", 10*time.Minute),
			LoginIdentifierWindow:   envDuration("BANKING_LOGIN_IDENTIFIER_WINDOW", 15*time.Minute),
			LoginIdentifierFailures: envInt("BANKING_LOGIN_IDENTIFIER_MAX_FAILURES", 5),
			LoginIPWindow:           envDuration("BANKING_LOGIN_IP_WINDOW", 15*time.Minute),
			LoginIPFailures:         envInt("BANKING_LOGIN_IP_MAX_FAILURES", 30),
			LoginLockoutDuration:    envDuration("BANKING_LOGIN_LOCKOUT_DURATION", 15*time.Minute),
		},
		RateLimit: RateLimitConfig{
			Enabled:     envBool("BANKING_RATE_LIMIT_ENABLED", true),
			Backend:     strings.ToLower(env("BANKING_RATE_LIMIT_BACKEND", "postgres")),
			Window:      envDuration("BANKING_RATE_LIMIT_WINDOW", time.Minute),
			MaxRequests: envInt("BANKING_RATE_LIMIT_MAX_REQUESTS", 300),
			FailOpen:    envBool("BANKING_RATE_LIMIT_FAIL_OPEN", true),
		},
		Security: SecurityConfig{
			SecretProvider:       strings.ToLower(env("BANKING_SECRET_PROVIDER", "env")),
			KMSKeyReference:      env("BANKING_KMS_KEY_REFERENCE", ""),
			WAFMode:              strings.ToLower(env("BANKING_WAF_MODE", "disabled")),
			PublicBaseURL:        env("BANKING_PUBLIC_BASE_URL", "http://localhost:8080"),
			TLSRequired:          envBool("BANKING_TLS_REQUIRED", false),
			HSTSEnabled:          envBool("BANKING_HSTS_ENABLED", false),
			CSRFEnabled:          envBool("BANKING_CSRF_ENABLED", true),
			AllowedHosts:         envList("BANKING_ALLOWED_HOSTS"),
			TrustedProxyRequired: envBool("BANKING_TRUSTED_PROXY_REQUIRED", false),
		},
		Cards: CardConfig{
			Secret:        env("BANKING_CARD_SECRET", ""),
			WebhookSecret: env("BANKING_CARD_WEBHOOK_SECRET", ""),
		},
		KYC: KYCConfig{
			WebhookSecret: env("BANKING_KYC_WEBHOOK_SECRET", ""),
		},
		Settlement: SettlementConfig{
			SEPAEnabled: envBool("BANKING_SEPA_SETTLEMENT_ENABLED", true),
			Interval:    envDuration("BANKING_SEPA_SETTLEMENT_INTERVAL", 10*time.Second),
			BatchSize:   envInt("BANKING_SEPA_SETTLEMENT_BATCH_SIZE", 25),
			MaxAttempts: envInt("BANKING_SEPA_SETTLEMENT_MAX_ATTEMPTS", 3),
			RetryDelay:  envDuration("BANKING_SEPA_SETTLEMENT_RETRY_DELAY", 1*time.Minute),
		},
		Observability: ObservabilityConfig{
			MetricsEnabled:            envBool("BANKING_METRICS_ENABLED", true),
			MetricsAllowedCIDRs:       envListDefault("BANKING_METRICS_ALLOWED_CIDRS", defaultMetricsCIDRs()),
			StructuredLogs:            envBool("BANKING_STRUCTURED_LOGS_ENABLED", true),
			RequestCorrelationEnabled: envBool("BANKING_REQUEST_CORRELATION_ENABLED", true),
			DashboardPackRef:          env("BANKING_OBSERVABILITY_DASHBOARD_PACK", "deploy/observability/grafana-banking-overview.json"),
			AlertRulesRef:             env("BANKING_OBSERVABILITY_ALERT_RULES", "deploy/observability/prometheus-rules.yml"),
			IncidentRunbookPackRef:    env("BANKING_INCIDENT_RUNBOOK_PACK", "docs/runbooks"),
			MajorIncidentContactsRef:  env("BANKING_MAJOR_INCIDENT_CONTACTS", "docs/runbooks/major-incident-notification.md"),
			BCDRTestReference:         env("BANKING_BCDR_TEST_REFERENCE", ""),
			ChaosTestPlanRef:          env("BANKING_CHAOS_TEST_PLAN", "docs/resilience/chaos-failure-injection.md"),
		},
		Features: FeatureFlagsConfig{
			Payments:           envBool("BANKING_FEATURE_PAYMENTS_ENABLED", true),
			Cards:              envBool("BANKING_FEATURE_CARDS_ENABLED", true),
			FX:                 envBool("BANKING_FEATURE_FX_ENABLED", true),
			Crypto:             envBool("BANKING_FEATURE_CRYPTO_ENABLED", true),
			Savings:            envBool("BANKING_FEATURE_SAVINGS_ENABLED", true),
			AdminMoneyMovement: envBool("BANKING_FEATURE_ADMIN_MONEY_MOVEMENT_ENABLED", true),
		},
		Runtime: RuntimeConfig{
			HostingArchitecture:  strings.ToLower(env("BANKING_HOSTING_ARCHITECTURE", "local")),
			NetworkSegmentation:  strings.ToLower(env("BANKING_NETWORK_SEGMENTATION", "single_network")),
			PrivateServiceAccess: envBool("BANKING_PRIVATE_SERVICE_ACCESS", false),
			PostgresHAEnabled:    envBool("BANKING_POSTGRES_HA_ENABLED", false),
			PostgresPITREnabled:  envBool("BANKING_POSTGRES_PITR_ENABLED", false),
			RestoreDrillRef:      env("BANKING_RESTORE_DRILL_REFERENCE", ""),
			ResourceProfile:      strings.ToLower(env("BANKING_RESOURCE_PROFILE", "local")),
			AutoscalingPolicy:    strings.ToLower(env("BANKING_AUTOSCALING_POLICY", "none")),
			AsyncBackend:         strings.ToLower(env("BANKING_ASYNC_BACKEND", "postgres")),
			DeploymentStrategy:   strings.ToLower(env("BANKING_DEPLOYMENT_STRATEGY", "rolling")),
			EmbeddedWorker:       envBool("BANKING_EMBEDDED_WORKER_ENABLED", true),
		},
		Providers: ProviderConfig{
			BankLedgerMode:                 strings.ToLower(env("BANKING_BANK_LEDGER_PROVIDER_MODE", "local")),
			CardIssuerMode:                 strings.ToLower(env("BANKING_CARD_ISSUER_PROVIDER_MODE", "local")),
			IdentityMode:                   strings.ToLower(env("BANKING_IDENTITY_PROVIDER_MODE", "local")),
			SanctionsMode:                  strings.ToLower(env("BANKING_SANCTIONS_PROVIDER_MODE", "local")),
			CustodyMode:                    strings.ToLower(env("BANKING_CUSTODY_PROVIDER_MODE", "local")),
			BlockchainAnalyticsMode:        strings.ToLower(env("BANKING_BLOCKCHAIN_ANALYTICS_PROVIDER_MODE", "local")),
			MarketDataMode:                 strings.ToLower(env("BANKING_MARKET_DATA_PROVIDER_MODE", "local")),
			PaymentRailMode:                strings.ToLower(env("BANKING_PAYMENT_RAIL_PROVIDER_MODE", "local")),
			BankLedgerName:                 env("BANKING_BANK_LEDGER_PROVIDER_NAME", "local_bank_ledger"),
			CardIssuerName:                 env("BANKING_CARD_ISSUER_PROVIDER_NAME", "local_card_issuer"),
			IdentityName:                   env("BANKING_IDENTITY_PROVIDER_NAME", "local_identity"),
			SanctionsName:                  env("BANKING_SANCTIONS_PROVIDER_NAME", "local_sanctions"),
			CustodyName:                    env("BANKING_CUSTODY_PROVIDER_NAME", "local_custody"),
			BlockchainAnalyticsName:        env("BANKING_BLOCKCHAIN_ANALYTICS_PROVIDER_NAME", "local_blockchain_analytics"),
			MarketDataName:                 env("BANKING_MARKET_DATA_PROVIDER_NAME", "local_market_data"),
			PaymentRailName:                env("BANKING_PAYMENT_RAIL_PROVIDER_NAME", "local_sepa_simulator"),
			CryptoRealMovementEnabled:      envBool("BANKING_CRYPTO_REAL_MOVEMENT_ENABLED", false),
			DefaultTimeout:                 envDuration("BANKING_PROVIDER_TIMEOUT", 3*time.Second),
			MaxAttempts:                    envInt("BANKING_PROVIDER_MAX_ATTEMPTS", 3),
			RetryBaseDelay:                 envDuration("BANKING_PROVIDER_RETRY_BASE_DELAY", 100*time.Millisecond),
			CircuitBreakerFailureThreshold: envInt("BANKING_PROVIDER_CIRCUIT_FAILURE_THRESHOLD", 5),
			CircuitBreakerCooldown:         envDuration("BANKING_PROVIDER_CIRCUIT_COOLDOWN", time.Minute),
			DegradedMode:                   strings.ToLower(env("BANKING_PROVIDER_DEGRADED_MODE", "fail_closed")),
			AuditEnabled:                   envBool("BANKING_PROVIDER_AUDIT_ENABLED", true),
			WebhookTolerance:               envDuration("BANKING_PROVIDER_WEBHOOK_TOLERANCE", 5*time.Minute),
			ApprovalReference:              env("BANKING_PROVIDER_APPROVAL_REFERENCE", ""),
		},
		Legal: LegalConfig{
			ApprovalReference:            env("BANKING_LEGAL_APPROVAL_REFERENCE", ""),
			RegulatedActivitiesReference: env("BANKING_REGULATED_ACTIVITIES_REFERENCE", ""),
			OperatingModelReference:      env("BANKING_OPERATING_MODEL_REFERENCE", ""),
			LegalMemoPackReference:       env("BANKING_LEGAL_MEMO_PACK_REFERENCE", ""),
			RestrictedTermsReference:     env("BANKING_RESTRICTED_TERMS_REFERENCE", ""),
			ProviderContractsReference:   env("BANKING_PROVIDER_CONTRACTS_REFERENCE", ""),
			SafeguardingModelReference:   env("BANKING_SAFEGUARDING_MODEL_REFERENCE", ""),
			PolicyPackReference:          env("BANKING_LEGAL_POLICY_PACK_REFERENCE", ""),
			JurisdictionRulesReference:   env("BANKING_JURISDICTION_RULES_REFERENCE", ""),
		},
		GoLive: GoLiveConfig{
			StagingSecretsReference:         env("BANKING_GO_LIVE_STAGING_SECRETS_REFERENCE", ""),
			ProviderWebhookE2EReference:     env("BANKING_GO_LIVE_PROVIDER_WEBHOOK_E2E_REFERENCE", ""),
			StagingObservabilityReference:   env("BANKING_GO_LIVE_STAGING_OBSERVABILITY_REFERENCE", ""),
			SeedUsersReference:              env("BANKING_GO_LIVE_SEED_USERS_REFERENCE", ""),
			GateAReference:                  env("BANKING_GO_LIVE_GATE_A_REFERENCE", ""),
			CICDExerciseReference:           env("BANKING_GO_LIVE_CICD_EXERCISE_REFERENCE", ""),
			SecurityScanningReference:       env("BANKING_GO_LIVE_SECURITY_SCANNING_REFERENCE", ""),
			BetaLegalWordingReference:       env("BANKING_GO_LIVE_BETA_LEGAL_WORDING_REFERENCE", ""),
			SupportIncidentChannelsRef:      env("BANKING_GO_LIVE_SUPPORT_INCIDENT_CHANNELS_REFERENCE", ""),
			KnownRiskRegisterReference:      env("BANKING_GO_LIVE_KNOWN_RISK_REGISTER_REFERENCE", ""),
			LegalLicensingApprovalReference: env("BANKING_GO_LIVE_LEGAL_LICENSING_APPROVAL_REFERENCE", ""),
			ProviderProductionReference:     env("BANKING_GO_LIVE_PROVIDER_PRODUCTION_REFERENCE", ""),
			ReconciliationLiveReference:     env("BANKING_GO_LIVE_RECONCILIATION_LIVE_REFERENCE", ""),
			SecurityReviewReference:         env("BANKING_GO_LIVE_SECURITY_REVIEW_REFERENCE", ""),
			KYCAMLOpsReference:              env("BANKING_GO_LIVE_KYC_AML_OPS_REFERENCE", ""),
			DORAApprovalReference:           env("BANKING_GO_LIVE_DORA_APPROVAL_REFERENCE", ""),
			PCIValidationReference:          env("BANKING_GO_LIVE_PCI_VALIDATION_REFERENCE", ""),
			BCDRProductionEvidenceReference: env("BANKING_GO_LIVE_BCDR_PRODUCTION_EVIDENCE_REFERENCE", ""),
			ExecutiveSignoffReference:       env("BANKING_GO_LIVE_EXECUTIVE_SIGNOFF_REFERENCE", ""),
		},
	}

	if cfg.Auth.JWTSecret == "" && !isProductionLike(cfg.App.Environment) {
		cfg.Auth.JWTSecret = "dev-only-change-this-secret-before-running-in-production"
	}
	if cfg.Cards.Secret == "" && !isProductionLike(cfg.App.Environment) {
		cfg.Cards.Secret = cfg.Auth.JWTSecret
	}
	if cfg.Cards.WebhookSecret == "" && !isProductionLike(cfg.App.Environment) {
		cfg.Cards.WebhookSecret = cfg.Cards.Secret
	}
	if cfg.KYC.WebhookSecret == "" && !isProductionLike(cfg.App.Environment) {
		cfg.KYC.WebhookSecret = cfg.Auth.JWTSecret
	}
	if cfg.Auth.MFASecret == "" && !isProductionLike(cfg.App.Environment) {
		cfg.Auth.MFASecret = cfg.Auth.JWTSecret
	}

	switch cfg.App.Environment {
	case "development", "test", "ci", "staging", "preprod", "production":
	default:
		return Config{}, fmt.Errorf("BANKING_APP_ENV must be development, test, ci, staging, preprod or production")
	}

	productionLike := isProductionLike(cfg.App.Environment)
	if productionLike && !hasNonEmptyEnv("BANKING_DATABASE_URL") {
		return Config{}, fmt.Errorf("%s must set BANKING_DATABASE_URL explicitly", cfg.App.Environment)
	}
	if err := validateSecret("BANKING_JWT_SECRET", cfg.Auth.JWTSecret, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateSecret("BANKING_CARD_SECRET", cfg.Cards.Secret, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateSecret("BANKING_CARD_WEBHOOK_SECRET", cfg.Cards.WebhookSecret, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateSecret("BANKING_KYC_WEBHOOK_SECRET", cfg.KYC.WebhookSecret, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateSecret("BANKING_MFA_SECRET", cfg.Auth.MFASecret, cfg.App.Environment); err != nil {
		return Config{}, err
	}

	if cfg.HTTP.MaxRequestBytes < 1024 {
		return Config{}, fmt.Errorf("BANKING_MAX_REQUEST_BYTES must be at least 1024")
	}
	if cfg.Auth.StepUpTTL < time.Minute || cfg.Auth.StepUpTTL > time.Hour {
		return Config{}, fmt.Errorf("BANKING_STEP_UP_TTL must be between 1m and 1h")
	}
	if cfg.Auth.RefreshTokenTTL < time.Hour || cfg.Auth.RefreshTokenTTL > 90*24*time.Hour {
		return Config{}, fmt.Errorf("BANKING_REFRESH_TOKEN_TTL must be between 1h and 90d")
	}
	if cfg.Auth.LoginIdentifierWindow < time.Minute || cfg.Auth.LoginIdentifierWindow > time.Hour {
		return Config{}, fmt.Errorf("BANKING_LOGIN_IDENTIFIER_WINDOW must be between 1m and 1h")
	}
	if cfg.Auth.LoginIdentifierFailures < 2 || cfg.Auth.LoginIdentifierFailures > 50 {
		return Config{}, fmt.Errorf("BANKING_LOGIN_IDENTIFIER_MAX_FAILURES must be between 2 and 50")
	}
	if cfg.Auth.LoginIPWindow < time.Minute || cfg.Auth.LoginIPWindow > time.Hour {
		return Config{}, fmt.Errorf("BANKING_LOGIN_IP_WINDOW must be between 1m and 1h")
	}
	if cfg.Auth.LoginIPFailures < 5 || cfg.Auth.LoginIPFailures > 500 {
		return Config{}, fmt.Errorf("BANKING_LOGIN_IP_MAX_FAILURES must be between 5 and 500")
	}
	if cfg.Auth.LoginLockoutDuration < time.Minute || cfg.Auth.LoginLockoutDuration > 24*time.Hour {
		return Config{}, fmt.Errorf("BANKING_LOGIN_LOCKOUT_DURATION must be between 1m and 24h")
	}
	if cfg.RateLimit.Window < time.Second || cfg.RateLimit.Window > time.Hour {
		return Config{}, fmt.Errorf("BANKING_RATE_LIMIT_WINDOW must be between 1s and 1h")
	}
	if cfg.RateLimit.MaxRequests < 10 || cfg.RateLimit.MaxRequests > 100000 {
		return Config{}, fmt.Errorf("BANKING_RATE_LIMIT_MAX_REQUESTS must be between 10 and 100000")
	}
	if cfg.RateLimit.Backend != "memory" && cfg.RateLimit.Backend != "postgres" {
		return Config{}, fmt.Errorf("BANKING_RATE_LIMIT_BACKEND must be memory or postgres")
	}
	if productionLike && cfg.RateLimit.Backend != "postgres" {
		return Config{}, fmt.Errorf("%s must use BANKING_RATE_LIMIT_BACKEND=postgres", cfg.App.Environment)
	}
	if err := validateApplicationSecurity(cfg.Security, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateCIDRs("BANKING_METRICS_ALLOWED_CIDRS", cfg.Observability.MetricsAllowedCIDRs); err != nil {
		return Config{}, err
	}
	if err := validateObservability(cfg.Observability, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateRuntime(cfg.Runtime, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateProviders(cfg.Providers, cfg.Features, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateLegal(cfg.Legal, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if err := validateGoLive(cfg.GoLive, cfg.App.Environment); err != nil {
		return Config{}, err
	}
	if len(cfg.Security.AllowedHosts) == 0 {
		publicURL, _ := url.Parse(cfg.Security.PublicBaseURL)
		if publicURL != nil && publicURL.Host != "" {
			cfg.Security.AllowedHosts = []string{strings.ToLower(publicURL.Host)}
		}
	}
	if cfg.Database.MigrationsDir == "" {
		return Config{}, fmt.Errorf("BANKING_MIGRATIONS_DIR is required when migrations are enabled")
	}
	if cfg.Settlement.Interval < time.Second {
		return Config{}, fmt.Errorf("BANKING_SEPA_SETTLEMENT_INTERVAL must be at least 1s")
	}
	if cfg.Settlement.BatchSize < 1 || cfg.Settlement.BatchSize > 100 {
		return Config{}, fmt.Errorf("BANKING_SEPA_SETTLEMENT_BATCH_SIZE must be between 1 and 100")
	}
	if cfg.Settlement.MaxAttempts < 1 || cfg.Settlement.MaxAttempts > 10 {
		return Config{}, fmt.Errorf("BANKING_SEPA_SETTLEMENT_MAX_ATTEMPTS must be between 1 and 10")
	}
	if cfg.Settlement.RetryDelay < time.Second {
		return Config{}, fmt.Errorf("BANKING_SEPA_SETTLEMENT_RETRY_DELAY must be at least 1s")
	}

	return cfg, nil
}

func validateLegal(cfg LegalConfig, environment string) error {
	if !isProductionLike(environment) {
		return nil
	}
	requiredRefs := map[string]string{
		"BANKING_LEGAL_APPROVAL_REFERENCE":       cfg.ApprovalReference,
		"BANKING_REGULATED_ACTIVITIES_REFERENCE": cfg.RegulatedActivitiesReference,
		"BANKING_OPERATING_MODEL_REFERENCE":      cfg.OperatingModelReference,
		"BANKING_LEGAL_MEMO_PACK_REFERENCE":      cfg.LegalMemoPackReference,
		"BANKING_RESTRICTED_TERMS_REFERENCE":     cfg.RestrictedTermsReference,
		"BANKING_PROVIDER_CONTRACTS_REFERENCE":   cfg.ProviderContractsReference,
		"BANKING_SAFEGUARDING_MODEL_REFERENCE":   cfg.SafeguardingModelReference,
		"BANKING_LEGAL_POLICY_PACK_REFERENCE":    cfg.PolicyPackReference,
		"BANKING_JURISDICTION_RULES_REFERENCE":   cfg.JurisdictionRulesReference,
	}
	for name, value := range requiredRefs {
		if strings.TrimSpace(value) == "" {
			return fmt.Errorf("%s must set %s", environment, name)
		}
	}
	return nil
}

func validateGoLive(cfg GoLiveConfig, environment string) error {
	if !isProductionLike(environment) {
		return nil
	}
	requiredRefs := map[string]string{
		"BANKING_GO_LIVE_STAGING_SECRETS_REFERENCE":           cfg.StagingSecretsReference,
		"BANKING_GO_LIVE_PROVIDER_WEBHOOK_E2E_REFERENCE":      cfg.ProviderWebhookE2EReference,
		"BANKING_GO_LIVE_STAGING_OBSERVABILITY_REFERENCE":     cfg.StagingObservabilityReference,
		"BANKING_GO_LIVE_SEED_USERS_REFERENCE":                cfg.SeedUsersReference,
		"BANKING_GO_LIVE_GATE_A_REFERENCE":                    cfg.GateAReference,
		"BANKING_GO_LIVE_CICD_EXERCISE_REFERENCE":             cfg.CICDExerciseReference,
		"BANKING_GO_LIVE_SECURITY_SCANNING_REFERENCE":         cfg.SecurityScanningReference,
		"BANKING_GO_LIVE_BETA_LEGAL_WORDING_REFERENCE":        cfg.BetaLegalWordingReference,
		"BANKING_GO_LIVE_SUPPORT_INCIDENT_CHANNELS_REFERENCE": cfg.SupportIncidentChannelsRef,
		"BANKING_GO_LIVE_KNOWN_RISK_REGISTER_REFERENCE":       cfg.KnownRiskRegisterReference,
		"BANKING_GO_LIVE_LEGAL_LICENSING_APPROVAL_REFERENCE":  cfg.LegalLicensingApprovalReference,
		"BANKING_GO_LIVE_PROVIDER_PRODUCTION_REFERENCE":       cfg.ProviderProductionReference,
		"BANKING_GO_LIVE_RECONCILIATION_LIVE_REFERENCE":       cfg.ReconciliationLiveReference,
		"BANKING_GO_LIVE_SECURITY_REVIEW_REFERENCE":           cfg.SecurityReviewReference,
		"BANKING_GO_LIVE_KYC_AML_OPS_REFERENCE":               cfg.KYCAMLOpsReference,
		"BANKING_GO_LIVE_DORA_APPROVAL_REFERENCE":             cfg.DORAApprovalReference,
		"BANKING_GO_LIVE_PCI_VALIDATION_REFERENCE":            cfg.PCIValidationReference,
		"BANKING_GO_LIVE_BCDR_PRODUCTION_EVIDENCE_REFERENCE":  cfg.BCDRProductionEvidenceReference,
		"BANKING_GO_LIVE_EXECUTIVE_SIGNOFF_REFERENCE":         cfg.ExecutiveSignoffReference,
	}
	for name, value := range requiredRefs {
		if strings.TrimSpace(value) == "" {
			return fmt.Errorf("%s must set %s", environment, name)
		}
	}
	return nil
}

func env(key, fallback string) string {
	if value, ok := os.LookupEnv(key); ok && strings.TrimSpace(value) != "" {
		return strings.TrimSpace(value)
	}
	return fallback
}

func envInt(key string, fallback int) int {
	value := env(key, "")
	if value == "" {
		return fallback
	}
	parsed, err := strconv.Atoi(value)
	if err != nil {
		return fallback
	}
	return parsed
}

func envInt64(key string, fallback int64) int64 {
	value := env(key, "")
	if value == "" {
		return fallback
	}
	parsed, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fallback
	}
	return parsed
}

func envBool(key string, fallback bool) bool {
	value := strings.ToLower(env(key, ""))
	if value == "" {
		return fallback
	}
	switch value {
	case "1", "true", "yes", "y", "on":
		return true
	case "0", "false", "no", "n", "off":
		return false
	default:
		return fallback
	}
}

func envList(key string) []string {
	raw := env(key, "")
	if raw == "" {
		return nil
	}
	parts := strings.Split(raw, ",")
	values := make([]string, 0, len(parts))
	for _, part := range parts {
		value := strings.ToLower(strings.TrimSpace(part))
		if value != "" {
			values = append(values, value)
		}
	}
	return values
}

func envListDefault(key string, fallback []string) []string {
	values := envList(key)
	if len(values) == 0 {
		return fallback
	}
	return values
}

func envDuration(key string, fallback time.Duration) time.Duration {
	value := env(key, "")
	if value == "" {
		return fallback
	}
	parsed, err := time.ParseDuration(value)
	if err != nil {
		return fallback
	}
	return parsed
}

func normalizeEnvironment(value string) string {
	normalized := strings.ToLower(strings.TrimSpace(value))
	switch normalized {
	case "dev":
		return "development"
	case "pre-prod", "pre_prod", "preproduction", "pre-production":
		return "preprod"
	default:
		return normalized
	}
}

func isProductionLike(environment string) bool {
	return environment == "preprod" || environment == "production"
}

func hasNonEmptyEnv(key string) bool {
	value, ok := os.LookupEnv(key)
	return ok && strings.TrimSpace(value) != ""
}

func validateSecret(name, value, environment string) error {
	if len(value) < 32 {
		return fmt.Errorf("%s must be at least 32 characters", name)
	}
	if isProductionLike(environment) && looksLikePlaceholderSecret(value) {
		return fmt.Errorf("%s must use a real %s", environment, name)
	}
	return nil
}

func looksLikePlaceholderSecret(value string) bool {
	normalized := strings.ToLower(strings.TrimSpace(value))
	placeholders := []string{
		"dev-only",
		"change-me",
		"changeme",
		"replace-me",
		"placeholder",
		"default-secret",
	}
	for _, placeholder := range placeholders {
		if strings.Contains(normalized, placeholder) {
			return true
		}
	}
	return false
}

func validateApplicationSecurity(cfg SecurityConfig, environment string) error {
	publicURL, err := url.Parse(strings.TrimSpace(cfg.PublicBaseURL))
	if err != nil || publicURL.Host == "" {
		return fmt.Errorf("BANKING_PUBLIC_BASE_URL must be an absolute URL")
	}
	if cfg.WAFMode != "disabled" && cfg.WAFMode != "monitor" && cfg.WAFMode != "enforce" {
		return fmt.Errorf("BANKING_WAF_MODE must be disabled, monitor or enforce")
	}
	if cfg.SecretProvider == "" {
		return fmt.Errorf("BANKING_SECRET_PROVIDER is required")
	}
	if isProductionLike(environment) {
		if cfg.SecretProvider == "env" || cfg.SecretProvider == "local" || cfg.SecretProvider == "development" {
			return fmt.Errorf("%s must use managed BANKING_SECRET_PROVIDER", environment)
		}
		if strings.TrimSpace(cfg.KMSKeyReference) == "" {
			return fmt.Errorf("%s must set BANKING_KMS_KEY_REFERENCE", environment)
		}
		if publicURL.Scheme != "https" {
			return fmt.Errorf("%s must use https BANKING_PUBLIC_BASE_URL", environment)
		}
		if !cfg.TLSRequired {
			return fmt.Errorf("%s must set BANKING_TLS_REQUIRED=true", environment)
		}
		if !cfg.HSTSEnabled {
			return fmt.Errorf("%s must set BANKING_HSTS_ENABLED=true", environment)
		}
		if cfg.WAFMode != "enforce" {
			return fmt.Errorf("%s must set BANKING_WAF_MODE=enforce", environment)
		}
		if !cfg.CSRFEnabled {
			return fmt.Errorf("%s must set BANKING_CSRF_ENABLED=true", environment)
		}
	}
	return nil
}

func validateCIDRs(name string, values []string) error {
	if len(values) == 0 {
		return fmt.Errorf("%s must contain at least one CIDR", name)
	}
	for _, value := range values {
		if _, _, err := net.ParseCIDR(value); err != nil {
			return fmt.Errorf("%s contains invalid CIDR %q", name, value)
		}
	}
	return nil
}

func validateObservability(cfg ObservabilityConfig, environment string) error {
	if !isProductionLike(environment) {
		return nil
	}
	if !cfg.MetricsEnabled {
		return fmt.Errorf("%s must set BANKING_METRICS_ENABLED=true", environment)
	}
	if !cfg.StructuredLogs {
		return fmt.Errorf("%s must set BANKING_STRUCTURED_LOGS_ENABLED=true", environment)
	}
	if !cfg.RequestCorrelationEnabled {
		return fmt.Errorf("%s must set BANKING_REQUEST_CORRELATION_ENABLED=true", environment)
	}
	requiredRefs := map[string]string{
		"BANKING_OBSERVABILITY_DASHBOARD_PACK": cfg.DashboardPackRef,
		"BANKING_OBSERVABILITY_ALERT_RULES":    cfg.AlertRulesRef,
		"BANKING_INCIDENT_RUNBOOK_PACK":        cfg.IncidentRunbookPackRef,
		"BANKING_MAJOR_INCIDENT_CONTACTS":      cfg.MajorIncidentContactsRef,
		"BANKING_BCDR_TEST_REFERENCE":          cfg.BCDRTestReference,
		"BANKING_CHAOS_TEST_PLAN":              cfg.ChaosTestPlanRef,
	}
	for name, value := range requiredRefs {
		if strings.TrimSpace(value) == "" {
			return fmt.Errorf("%s must set %s", environment, name)
		}
	}
	return nil
}

func validateRuntime(cfg RuntimeConfig, environment string) error {
	switch cfg.AsyncBackend {
	case "postgres", "managed_queue":
	default:
		return fmt.Errorf("BANKING_ASYNC_BACKEND must be postgres or managed_queue")
	}
	switch cfg.DeploymentStrategy {
	case "rolling", "blue_green", "canary":
	default:
		return fmt.Errorf("BANKING_DEPLOYMENT_STRATEGY must be rolling, blue_green or canary")
	}
	if !isProductionLike(environment) {
		return nil
	}
	if cfg.HostingArchitecture == "" || cfg.HostingArchitecture == "local" {
		return fmt.Errorf("%s must set BANKING_HOSTING_ARCHITECTURE to a production hosting model", environment)
	}
	if cfg.NetworkSegmentation == "" || cfg.NetworkSegmentation == "single_network" || cfg.NetworkSegmentation == "flat" {
		return fmt.Errorf("%s must set BANKING_NETWORK_SEGMENTATION to a segmented private network model", environment)
	}
	if !cfg.PrivateServiceAccess {
		return fmt.Errorf("%s must set BANKING_PRIVATE_SERVICE_ACCESS=true", environment)
	}
	if !cfg.PostgresHAEnabled {
		return fmt.Errorf("%s must set BANKING_POSTGRES_HA_ENABLED=true", environment)
	}
	if !cfg.PostgresPITREnabled {
		return fmt.Errorf("%s must set BANKING_POSTGRES_PITR_ENABLED=true", environment)
	}
	if strings.TrimSpace(cfg.RestoreDrillRef) == "" {
		return fmt.Errorf("%s must set BANKING_RESTORE_DRILL_REFERENCE", environment)
	}
	if cfg.ResourceProfile == "" || cfg.ResourceProfile == "local" {
		return fmt.Errorf("%s must set BANKING_RESOURCE_PROFILE", environment)
	}
	if cfg.AutoscalingPolicy == "" || cfg.AutoscalingPolicy == "none" {
		return fmt.Errorf("%s must set BANKING_AUTOSCALING_POLICY", environment)
	}
	return nil
}

func validateProviders(cfg ProviderConfig, features FeatureFlagsConfig, environment string) error {
	modes := map[string]string{
		"BANKING_BANK_LEDGER_PROVIDER_MODE":          cfg.BankLedgerMode,
		"BANKING_CARD_ISSUER_PROVIDER_MODE":          cfg.CardIssuerMode,
		"BANKING_IDENTITY_PROVIDER_MODE":             cfg.IdentityMode,
		"BANKING_SANCTIONS_PROVIDER_MODE":            cfg.SanctionsMode,
		"BANKING_CUSTODY_PROVIDER_MODE":              cfg.CustodyMode,
		"BANKING_BLOCKCHAIN_ANALYTICS_PROVIDER_MODE": cfg.BlockchainAnalyticsMode,
		"BANKING_MARKET_DATA_PROVIDER_MODE":          cfg.MarketDataMode,
		"BANKING_PAYMENT_RAIL_PROVIDER_MODE":         cfg.PaymentRailMode,
	}
	for name, mode := range modes {
		if err := validateProviderMode(name, mode); err != nil {
			return err
		}
	}
	if cfg.DefaultTimeout < 100*time.Millisecond || cfg.DefaultTimeout > 30*time.Second {
		return fmt.Errorf("BANKING_PROVIDER_TIMEOUT must be between 100ms and 30s")
	}
	if cfg.MaxAttempts < 1 || cfg.MaxAttempts > 5 {
		return fmt.Errorf("BANKING_PROVIDER_MAX_ATTEMPTS must be between 1 and 5")
	}
	if cfg.RetryBaseDelay < 10*time.Millisecond || cfg.RetryBaseDelay > 5*time.Second {
		return fmt.Errorf("BANKING_PROVIDER_RETRY_BASE_DELAY must be between 10ms and 5s")
	}
	if cfg.CircuitBreakerFailureThreshold < 1 || cfg.CircuitBreakerFailureThreshold > 100 {
		return fmt.Errorf("BANKING_PROVIDER_CIRCUIT_FAILURE_THRESHOLD must be between 1 and 100")
	}
	if cfg.CircuitBreakerCooldown < time.Second || cfg.CircuitBreakerCooldown > time.Hour {
		return fmt.Errorf("BANKING_PROVIDER_CIRCUIT_COOLDOWN must be between 1s and 1h")
	}
	switch cfg.DegradedMode {
	case "fail_closed", "read_only", "manual_review":
	default:
		return fmt.Errorf("BANKING_PROVIDER_DEGRADED_MODE must be fail_closed, read_only or manual_review")
	}
	if cfg.WebhookTolerance < time.Minute || cfg.WebhookTolerance > 24*time.Hour {
		return fmt.Errorf("BANKING_PROVIDER_WEBHOOK_TOLERANCE must be between 1m and 24h")
	}

	if !isProductionLike(environment) {
		return nil
	}

	requiredContracted := map[string]string{
		"BANKING_BANK_LEDGER_PROVIDER_MODE":  cfg.BankLedgerMode,
		"BANKING_CARD_ISSUER_PROVIDER_MODE":  cfg.CardIssuerMode,
		"BANKING_IDENTITY_PROVIDER_MODE":     cfg.IdentityMode,
		"BANKING_SANCTIONS_PROVIDER_MODE":    cfg.SanctionsMode,
		"BANKING_PAYMENT_RAIL_PROVIDER_MODE": cfg.PaymentRailMode,
	}
	for name, mode := range requiredContracted {
		if mode != "contracted" {
			return fmt.Errorf("%s must set %s=contracted", environment, name)
		}
	}
	if features.FX && cfg.MarketDataMode != "contracted" {
		return fmt.Errorf("%s must set BANKING_MARKET_DATA_PROVIDER_MODE=contracted when FX is enabled", environment)
	}
	if features.Crypto {
		if cfg.CryptoRealMovementEnabled {
			if cfg.CustodyMode != "contracted" {
				return fmt.Errorf("%s must set BANKING_CUSTODY_PROVIDER_MODE=contracted when real crypto movement is enabled", environment)
			}
			if cfg.BlockchainAnalyticsMode != "contracted" {
				return fmt.Errorf("%s must set BANKING_BLOCKCHAIN_ANALYTICS_PROVIDER_MODE=contracted when real crypto movement is enabled", environment)
			}
		} else if cfg.CustodyMode != "disabled" && cfg.CustodyMode != "contracted" {
			return fmt.Errorf("%s must disable real crypto custody or use a contracted custody provider", environment)
		}
	}
	if !cfg.AuditEnabled {
		return fmt.Errorf("%s must set BANKING_PROVIDER_AUDIT_ENABLED=true", environment)
	}
	if strings.TrimSpace(cfg.ApprovalReference) == "" {
		return fmt.Errorf("%s must set BANKING_PROVIDER_APPROVAL_REFERENCE", environment)
	}
	return nil
}

func validateProviderMode(name, mode string) error {
	switch strings.ToLower(strings.TrimSpace(mode)) {
	case "local", "sandbox", "contracted", "disabled":
		return nil
	default:
		return fmt.Errorf("%s must be local, sandbox, contracted or disabled", name)
	}
}

func defaultMetricsCIDRs() []string {
	return []string{
		"127.0.0.1/32",
		"::1/128",
		"10.0.0.0/8",
		"172.16.0.0/12",
		"192.168.0.0/16",
		"100.64.0.0/10",
	}
}
