package config

import (
	"strings"
	"testing"
)

func TestLoadPreprodRejectsMissingDatabaseURL(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_DATABASE_URL", "")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod without explicit database url to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_DATABASE_URL") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRejectsPlaceholderSecret(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_JWT_SECRET", "dev-only-change-this-secret-before-running-in-production")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod placeholder secret to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_JWT_SECRET") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresPostgresRateLimit(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_RATE_LIMIT_BACKEND", "memory")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod memory rate limit backend to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_RATE_LIMIT_BACKEND=postgres") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresManagedSecretProvider(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_SECRET_PROVIDER", "env")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod env secret provider to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_SECRET_PROVIDER") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresHTTPSPublicURL(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_PUBLIC_BASE_URL", "http://preprod.example.com")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod http public url to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_PUBLIC_BASE_URL") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodAcceptsHardenedConfig(t *testing.T) {
	setValidPreprodEnv(t)

	cfg, err := Load()
	if err != nil {
		t.Fatalf("valid preprod config rejected: %v", err)
	}
	if cfg.App.Environment != "preprod" {
		t.Fatalf("unexpected environment: %s", cfg.App.Environment)
	}
	if cfg.RateLimit.Backend != "postgres" {
		t.Fatalf("unexpected rate limit backend: %s", cfg.RateLimit.Backend)
	}
	if cfg.Runtime.DeploymentStrategy != "canary" {
		t.Fatalf("unexpected deployment strategy: %s", cfg.Runtime.DeploymentStrategy)
	}
}

func TestLoadDevelopmentStillUsesLocalDefaults(t *testing.T) {
	t.Setenv("BANKING_APP_ENV", "development")
	t.Setenv("BANKING_JWT_SECRET", "")
	t.Setenv("BANKING_CARD_SECRET", "")
	t.Setenv("BANKING_CARD_WEBHOOK_SECRET", "")
	t.Setenv("BANKING_KYC_WEBHOOK_SECRET", "")
	t.Setenv("BANKING_MFA_SECRET", "")
	t.Setenv("BANKING_RATE_LIMIT_BACKEND", "memory")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("development defaults rejected: %v", err)
	}
	if cfg.Auth.JWTSecret == "" {
		t.Fatal("expected development JWT fallback")
	}
	if cfg.RateLimit.Backend != "memory" {
		t.Fatalf("unexpected development rate limit backend: %s", cfg.RateLimit.Backend)
	}
	if !cfg.Features.Payments || !cfg.Features.Cards || !cfg.Features.FX || !cfg.Features.Crypto || !cfg.Features.Savings || !cfg.Features.AdminMoneyMovement {
		t.Fatalf("expected high-risk product features to default enabled in development: %+v", cfg.Features)
	}
}

func TestLoadFeatureFlagsCanDisableHighRiskProducts(t *testing.T) {
	t.Setenv("BANKING_APP_ENV", "development")
	t.Setenv("BANKING_FEATURE_PAYMENTS_ENABLED", "false")
	t.Setenv("BANKING_FEATURE_CARDS_ENABLED", "false")
	t.Setenv("BANKING_FEATURE_FX_ENABLED", "false")
	t.Setenv("BANKING_FEATURE_CRYPTO_ENABLED", "false")
	t.Setenv("BANKING_FEATURE_SAVINGS_ENABLED", "false")
	t.Setenv("BANKING_FEATURE_ADMIN_MONEY_MOVEMENT_ENABLED", "false")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("feature flags rejected: %v", err)
	}
	if cfg.Features.Payments || cfg.Features.Cards || cfg.Features.FX || cfg.Features.Crypto || cfg.Features.Savings || cfg.Features.AdminMoneyMovement {
		t.Fatalf("expected all configured high-risk product flags disabled: %+v", cfg.Features)
	}
}

func TestLoadPreprodRequiresProductionRuntimeControls(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_APP_ENV", "pre-prod")
	t.Setenv("BANKING_POSTGRES_HA_ENABLED", "false")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod without PostgreSQL HA to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_POSTGRES_HA_ENABLED") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadRejectsInvalidMetricsCIDR(t *testing.T) {
	t.Setenv("BANKING_APP_ENV", "development")
	t.Setenv("BANKING_METRICS_ALLOWED_CIDRS", "not-a-cidr")

	_, err := Load()
	if err == nil {
		t.Fatal("expected invalid metrics CIDR to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_METRICS_ALLOWED_CIDRS") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresObservabilityControls(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_REQUEST_CORRELATION_ENABLED", "false")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod without request correlation to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_REQUEST_CORRELATION_ENABLED") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRejectsLocalProviderModes(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_CARD_ISSUER_PROVIDER_MODE", "local")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod local card issuer provider to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_CARD_ISSUER_PROVIDER_MODE=contracted") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresLegalControls(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_LEGAL_APPROVAL_REFERENCE", "")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod without legal approval reference to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_LEGAL_APPROVAL_REFERENCE") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodRequiresGoLiveControls(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_GO_LIVE_EXECUTIVE_SIGNOFF_REFERENCE", "")

	_, err := Load()
	if err == nil {
		t.Fatal("expected preprod without go-live executive signoff reference to fail")
	}
	if !strings.Contains(err.Error(), "BANKING_GO_LIVE_EXECUTIVE_SIGNOFF_REFERENCE") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestLoadPreprodAllowsCryptoWithRealMovementDisabled(t *testing.T) {
	setValidPreprodEnv(t)
	t.Setenv("BANKING_CRYPTO_REAL_MOVEMENT_ENABLED", "false")
	t.Setenv("BANKING_CUSTODY_PROVIDER_MODE", "disabled")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("expected disabled real crypto movement to pass: %v", err)
	}
	if cfg.Providers.CustodyMode != "disabled" || cfg.Providers.CryptoRealMovementEnabled {
		t.Fatalf("unexpected crypto provider config: %+v", cfg.Providers)
	}
}

func setValidPreprodEnv(t *testing.T) {
	t.Helper()
	secret := "preprod-secret-value-with-enough-entropy-64-characters-001"
	t.Setenv("BANKING_APP_ENV", "preprod")
	t.Setenv("BANKING_DATABASE_URL", "postgres://banking:banking@localhost:5432/banking_preprod?sslmode=disable")
	t.Setenv("BANKING_JWT_SECRET", secret+"-jwt")
	t.Setenv("BANKING_CARD_SECRET", secret+"-card")
	t.Setenv("BANKING_CARD_WEBHOOK_SECRET", secret+"-card-webhook")
	t.Setenv("BANKING_KYC_WEBHOOK_SECRET", secret+"-kyc-webhook")
	t.Setenv("BANKING_MFA_SECRET", secret+"-mfa")
	t.Setenv("BANKING_RATE_LIMIT_BACKEND", "postgres")
	t.Setenv("BANKING_SECRET_PROVIDER", "aws_kms")
	t.Setenv("BANKING_KMS_KEY_REFERENCE", "arn:aws:kms:eu-west-1:123456789012:key/example")
	t.Setenv("BANKING_PUBLIC_BASE_URL", "https://preprod.example.com")
	t.Setenv("BANKING_TLS_REQUIRED", "true")
	t.Setenv("BANKING_HSTS_ENABLED", "true")
	t.Setenv("BANKING_WAF_MODE", "enforce")
	t.Setenv("BANKING_CSRF_ENABLED", "true")
	t.Setenv("BANKING_HOSTING_ARCHITECTURE", "managed_kubernetes_multi_az")
	t.Setenv("BANKING_NETWORK_SEGMENTATION", "public_edge_private_app_private_data")
	t.Setenv("BANKING_PRIVATE_SERVICE_ACCESS", "true")
	t.Setenv("BANKING_POSTGRES_HA_ENABLED", "true")
	t.Setenv("BANKING_POSTGRES_PITR_ENABLED", "true")
	t.Setenv("BANKING_RESTORE_DRILL_REFERENCE", "restore-drill-2026-06-22")
	t.Setenv("BANKING_RESOURCE_PROFILE", "api-standard")
	t.Setenv("BANKING_AUTOSCALING_POLICY", "cpu60_min2_max6")
	t.Setenv("BANKING_ASYNC_BACKEND", "postgres")
	t.Setenv("BANKING_DEPLOYMENT_STRATEGY", "canary")
	t.Setenv("BANKING_METRICS_ALLOWED_CIDRS", "10.0.0.0/8,127.0.0.1/32")
	t.Setenv("BANKING_STRUCTURED_LOGS_ENABLED", "true")
	t.Setenv("BANKING_REQUEST_CORRELATION_ENABLED", "true")
	t.Setenv("BANKING_OBSERVABILITY_DASHBOARD_PACK", "deploy/observability/grafana-banking-overview.json")
	t.Setenv("BANKING_OBSERVABILITY_ALERT_RULES", "deploy/observability/prometheus-rules.yml")
	t.Setenv("BANKING_INCIDENT_RUNBOOK_PACK", "docs/runbooks")
	t.Setenv("BANKING_MAJOR_INCIDENT_CONTACTS", "docs/runbooks/major-incident-notification.md")
	t.Setenv("BANKING_BCDR_TEST_REFERENCE", "bcdr-drill-2026-06-23")
	t.Setenv("BANKING_CHAOS_TEST_PLAN", "docs/resilience/chaos-failure-injection.md")
	t.Setenv("BANKING_BANK_LEDGER_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_CARD_ISSUER_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_IDENTITY_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_SANCTIONS_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_PAYMENT_RAIL_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_MARKET_DATA_PROVIDER_MODE", "contracted")
	t.Setenv("BANKING_CUSTODY_PROVIDER_MODE", "disabled")
	t.Setenv("BANKING_BLOCKCHAIN_ANALYTICS_PROVIDER_MODE", "sandbox")
	t.Setenv("BANKING_CRYPTO_REAL_MOVEMENT_ENABLED", "false")
	t.Setenv("BANKING_PROVIDER_APPROVAL_REFERENCE", "provider-approval-preprod-2026-07-10")
	t.Setenv("BANKING_LEGAL_APPROVAL_REFERENCE", "legal-approval-preprod-2026-07-26")
	t.Setenv("BANKING_REGULATED_ACTIVITIES_REFERENCE", "regulated-activities-matrix-2026-07-26")
	t.Setenv("BANKING_OPERATING_MODEL_REFERENCE", "operating-model-decision-2026-07-26")
	t.Setenv("BANKING_LEGAL_MEMO_PACK_REFERENCE", "legal-memo-pack-2026-07-26")
	t.Setenv("BANKING_RESTRICTED_TERMS_REFERENCE", "restricted-terms-review-2026-07-26")
	t.Setenv("BANKING_PROVIDER_CONTRACTS_REFERENCE", "provider-contract-pack-2026-07-26")
	t.Setenv("BANKING_SAFEGUARDING_MODEL_REFERENCE", "safeguarding-model-2026-07-26")
	t.Setenv("BANKING_LEGAL_POLICY_PACK_REFERENCE", "customer-policy-pack-2026-07-26")
	t.Setenv("BANKING_JURISDICTION_RULES_REFERENCE", "jurisdiction-rules-2026-07-26")
	t.Setenv("BANKING_GO_LIVE_STAGING_SECRETS_REFERENCE", "go-live-133-staging-secrets-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_PROVIDER_WEBHOOK_E2E_REFERENCE", "go-live-134-provider-webhook-e2e-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_STAGING_OBSERVABILITY_REFERENCE", "go-live-135-staging-observability-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_SEED_USERS_REFERENCE", "go-live-136-seed-users-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_GATE_A_REFERENCE", "go-live-137-gate-a-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_CICD_EXERCISE_REFERENCE", "go-live-138-cicd-exercise-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_SECURITY_SCANNING_REFERENCE", "go-live-139-security-scanning-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_BETA_LEGAL_WORDING_REFERENCE", "go-live-140-beta-legal-wording-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_SUPPORT_INCIDENT_CHANNELS_REFERENCE", "go-live-141-support-incident-channels-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_KNOWN_RISK_REGISTER_REFERENCE", "go-live-142-known-risk-register-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_LEGAL_LICENSING_APPROVAL_REFERENCE", "go-live-143-legal-licensing-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_PROVIDER_PRODUCTION_REFERENCE", "go-live-144-provider-production-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_RECONCILIATION_LIVE_REFERENCE", "go-live-145-reconciliation-live-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_SECURITY_REVIEW_REFERENCE", "go-live-146-security-review-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_KYC_AML_OPS_REFERENCE", "go-live-147-kyc-aml-ops-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_DORA_APPROVAL_REFERENCE", "go-live-148-dora-approval-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_PCI_VALIDATION_REFERENCE", "go-live-149-pci-validation-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_BCDR_PRODUCTION_EVIDENCE_REFERENCE", "go-live-150-bcdr-evidence-2026-08-14")
	t.Setenv("BANKING_GO_LIVE_EXECUTIVE_SIGNOFF_REFERENCE", "go-live-151-executive-signoff-2026-08-14")
}
