package httpapi

import (
	"context"
	"log/slog"
	"net/http"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/niels/banking-app/backend/internal/accounts"
	"github.com/niels/banking-app/backend/internal/admin"
	"github.com/niels/banking-app/backend/internal/aml"
	"github.com/niels/banking-app/backend/internal/appsec"
	"github.com/niels/banking-app/backend/internal/audit"
	"github.com/niels/banking-app/backend/internal/authguard"
	"github.com/niels/banking-app/backend/internal/backoffice"
	"github.com/niels/banking-app/backend/internal/beneficiaries"
	"github.com/niels/banking-app/backend/internal/cards"
	"github.com/niels/banking-app/backend/internal/compliance"
	"github.com/niels/banking-app/backend/internal/config"
	"github.com/niels/banking-app/backend/internal/crypto"
	"github.com/niels/banking-app/backend/internal/fx"
	"github.com/niels/banking-app/backend/internal/golive"
	"github.com/niels/banking-app/backend/internal/httpapi/middleware"
	"github.com/niels/banking-app/backend/internal/kyc"
	"github.com/niels/banking-app/backend/internal/ledger"
	"github.com/niels/banking-app/backend/internal/legal"
	"github.com/niels/banking-app/backend/internal/observability"
	"github.com/niels/banking-app/backend/internal/privacy"
	"github.com/niels/banking-app/backend/internal/providers"
	"github.com/niels/banking-app/backend/internal/reconciliation"
	"github.com/niels/banking-app/backend/internal/respond"
	"github.com/niels/banking-app/backend/internal/risk"
	"github.com/niels/banking-app/backend/internal/savings"
	"github.com/niels/banking-app/backend/internal/security"
	"github.com/niels/banking-app/backend/internal/settlement"
	"github.com/niels/banking-app/backend/internal/transfers"
	"github.com/niels/banking-app/backend/internal/users"
	"github.com/niels/banking-app/backend/internal/wallets"
)

type Dependencies struct {
	Config         config.Config
	Logger         *slog.Logger
	DB             *pgxpool.Pool
	Metrics        *observability.Registry
	Tokens         *security.TokenManager
	Users          *users.Handler
	Accounts       *accounts.Handler
	Transfers      *transfers.Handler
	Beneficiaries  *beneficiaries.Handler
	Cards          *cards.Handler
	Wallets        *wallets.Handler
	KYC            *kyc.Handler
	AML            *aml.Handler
	Compliance     *compliance.Handler
	Admin          *admin.Handler
	AppSec         *appsec.Handler
	Backoffice     *backoffice.Handler
	Crypto         *crypto.Handler
	Savings        *savings.Handler
	FX             *fx.Handler
	Reconciliation *reconciliation.Handler
	Risk           *risk.Handler
	Settlement     *settlement.Handler
	AuthGuard      *authguard.Handler
	Ledger         *ledger.Handler
	Audit          *audit.Handler
	Privacy        *privacy.Handler
	Providers      *providers.Handler
	Legal          *legal.Handler
	GoLive         *golive.Handler
}

func NewRouter(deps Dependencies) http.Handler {
	mux := http.NewServeMux()
	rateLimiter := middleware.NewRateLimiter(middleware.RateLimitOptions{
		Enabled:     deps.Config.RateLimit.Enabled,
		Backend:     deps.Config.RateLimit.Backend,
		Window:      deps.Config.RateLimit.Window,
		MaxRequests: deps.Config.RateLimit.MaxRequests,
		FailOpen:    deps.Config.RateLimit.FailOpen,
	})
	if deps.Config.RateLimit.Backend == "postgres" {
		rateLimiter = middleware.NewPostgresRateLimiter(deps.DB, middleware.RateLimitOptions{
			Enabled:     deps.Config.RateLimit.Enabled,
			Backend:     deps.Config.RateLimit.Backend,
			Window:      deps.Config.RateLimit.Window,
			MaxRequests: deps.Config.RateLimit.MaxRequests,
			FailOpen:    deps.Config.RateLimit.FailOpen,
		})
	}

	mux.HandleFunc("GET /healthz", healthz(deps.Config))
	mux.HandleFunc("GET /readyz", readyz(deps.DB))
	if deps.Config.Observability.MetricsEnabled && deps.Metrics != nil {
		mux.Handle("GET /metrics", middleware.InternalCIDRAllowlist(deps.Config.Observability.MetricsAllowedCIDRs)(deps.Metrics.Handler()))
	}
	requestCorrelation := middleware.Middleware(func(next http.Handler) http.Handler {
		return next
	})
	if deps.Config.Observability.RequestCorrelationEnabled {
		requestCorrelation = middleware.RequestID
	}

	featureOnly := func(feature string, enabled bool, h http.HandlerFunc) http.Handler {
		return middleware.Chain(http.HandlerFunc(h), middleware.RequireFeature(feature, enabled))
	}

	mux.HandleFunc("POST /v1/auth/register", deps.Users.Register)
	mux.HandleFunc("POST /v1/auth/login", deps.Users.Login)
	mux.HandleFunc("POST /v1/auth/refresh", deps.Users.Refresh)
	mux.HandleFunc("POST /v1/auth/logout", deps.Users.Logout)
	mux.Handle("POST /v1/card-issuer/webhooks", featureOnly("cards", deps.Config.Features.Cards, deps.Cards.Webhook))
	mux.HandleFunc("POST /v1/kyc/provider/webhooks", deps.KYC.Webhook)
	mux.HandleFunc("GET /v1/currencies", deps.Wallets.ListCurrencies)
	mux.Handle("GET /v1/crypto/assets", featureOnly("crypto", deps.Config.Features.Crypto, deps.Crypto.ListAssets))
	mux.HandleFunc("GET /v1/privacy/notices", deps.Privacy.PublicNotices)

	authenticated := middleware.Authenticate(deps.Tokens)
	adminOnly := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireScope(security.ScopeAdminRead))
	}
	adminWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireScope(security.ScopeAdminWrite), middleware.RequireStepUp)
	}
	featureRead := func(feature string, enabled bool, h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature(feature, enabled))
	}
	featureAdminRead := func(feature string, enabled bool, h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature(feature, enabled), middleware.RequireScope(security.ScopeAdminRead))
	}
	featureAdminWrite := func(feature string, enabled bool, h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature(feature, enabled), middleware.RequireScope(security.ScopeAdminWrite), middleware.RequireStepUp)
	}
	paymentsRead := func(h http.Handler) http.Handler {
		return featureRead("payments", deps.Config.Features.Payments, h)
	}
	paymentsWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature("payments", deps.Config.Features.Payments), middleware.RequireScope(security.ScopeMoneyWrite), middleware.RequireStepUp)
	}
	fxRead := func(h http.Handler) http.Handler {
		return featureRead("fx", deps.Config.Features.FX, h)
	}
	fxWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature("fx", deps.Config.Features.FX), middleware.RequireScope(security.ScopeMoneyWrite), middleware.RequireStepUp)
	}
	cardsRead := func(h http.Handler) http.Handler {
		return featureRead("cards", deps.Config.Features.Cards, h)
	}
	cardsWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature("cards", deps.Config.Features.Cards), middleware.RequireScope(security.ScopeCardsWrite), middleware.RequireStepUp)
	}
	cryptoRead := func(h http.Handler) http.Handler {
		return featureRead("crypto", deps.Config.Features.Crypto, h)
	}
	cryptoWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature("crypto", deps.Config.Features.Crypto), middleware.RequireScope(security.ScopeCryptoWrite), middleware.RequireStepUp)
	}
	savingsRead := func(h http.Handler) http.Handler {
		return featureRead("savings", deps.Config.Features.Savings, h)
	}
	savingsWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireFeature("savings", deps.Config.Features.Savings), middleware.RequireScope(security.ScopeMoneyWrite), middleware.RequireStepUp)
	}
	adminMoneyRead := func(h http.Handler) http.Handler {
		return featureAdminRead("admin_money_movement", deps.Config.Features.AdminMoneyMovement, h)
	}
	adminMoneyWrite := func(h http.Handler) http.Handler {
		return featureAdminWrite("admin_money_movement", deps.Config.Features.AdminMoneyMovement, h)
	}
	complianceWrite := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireScope(security.ScopeComplianceWrite))
	}
	selfStepUp := func(h http.Handler) http.Handler {
		return middleware.Chain(h, authenticated, middleware.RequireStepUp)
	}

	mux.Handle("GET /v1/me", authenticated(http.HandlerFunc(deps.Users.Me)))
	mux.Handle("GET /v1/auth/sessions", authenticated(http.HandlerFunc(deps.Users.ListSessions)))
	mux.Handle("DELETE /v1/auth/sessions/{id}", authenticated(http.HandlerFunc(deps.Users.RevokeSession)))
	mux.Handle("GET /v1/security/mfa", authenticated(http.HandlerFunc(deps.Users.MFAStatus)))
	mux.Handle("POST /v1/security/mfa/totp/setup", authenticated(http.HandlerFunc(deps.Users.SetupTOTP)))
	mux.Handle("POST /v1/security/mfa/totp/enable", authenticated(http.HandlerFunc(deps.Users.EnableTOTP)))
	mux.Handle("POST /v1/security/mfa/recovery-codes", selfStepUp(http.HandlerFunc(deps.Users.GenerateRecoveryCodes)))
	mux.Handle("GET /v1/security/passkeys", authenticated(http.HandlerFunc(deps.Users.Passkeys)))
	mux.Handle("POST /v1/security/passkeys/registration-options", selfStepUp(http.HandlerFunc(deps.Users.PasskeyRegistrationOptions)))
	mux.Handle("POST /v1/security/passkeys", selfStepUp(http.HandlerFunc(deps.Users.RegisterPasskey)))
	mux.Handle("DELETE /v1/security/passkeys/{id}", selfStepUp(http.HandlerFunc(deps.Users.DeletePasskey)))
	mux.Handle("POST /v1/auth/step-up", authenticated(http.HandlerFunc(deps.Users.StepUp)))
	mux.Handle("GET /v1/kyc/profile", authenticated(http.HandlerFunc(deps.KYC.GetProfile)))
	mux.Handle("PUT /v1/kyc/profile", authenticated(http.HandlerFunc(deps.KYC.UpsertProfile)))
	mux.Handle("POST /v1/kyc/documents", authenticated(http.HandlerFunc(deps.KYC.AddDocument)))
	mux.Handle("GET /v1/kyc/documents", authenticated(http.HandlerFunc(deps.KYC.ListDocuments)))
	mux.Handle("POST /v1/kyc/submit", authenticated(http.HandlerFunc(deps.KYC.Submit)))
	mux.Handle("GET /v1/aml/screenings", authenticated(http.HandlerFunc(deps.AML.MyScreenings)))
	mux.Handle("POST /v1/accounts", authenticated(http.HandlerFunc(deps.Accounts.Create)))
	mux.Handle("GET /v1/accounts", authenticated(http.HandlerFunc(deps.Accounts.List)))
	mux.Handle("GET /v1/accounts/{id}", authenticated(http.HandlerFunc(deps.Accounts.Get)))
	mux.Handle("POST /v1/wallets", authenticated(http.HandlerFunc(deps.Wallets.Create)))
	mux.Handle("GET /v1/wallets", authenticated(http.HandlerFunc(deps.Wallets.List)))
	mux.Handle("GET /v1/wallets/{id}", authenticated(http.HandlerFunc(deps.Wallets.Get)))
	mux.Handle("POST /v1/wallets/{id}/balances", authenticated(http.HandlerFunc(deps.Wallets.AddCurrency)))
	mux.Handle("GET /v1/wallets/{id}/balances", authenticated(http.HandlerFunc(deps.Wallets.ListBalances)))
	mux.Handle("POST /v1/crypto/wallets", cryptoWrite(http.HandlerFunc(deps.Crypto.CreateWallet)))
	mux.Handle("GET /v1/crypto/wallets", cryptoRead(http.HandlerFunc(deps.Crypto.ListWallets)))
	mux.Handle("GET /v1/crypto/wallets/{id}", cryptoRead(http.HandlerFunc(deps.Crypto.GetWallet)))
	mux.Handle("POST /v1/crypto/wallets/{id}/assets", cryptoWrite(http.HandlerFunc(deps.Crypto.AddAsset)))
	mux.Handle("GET /v1/crypto/wallets/{id}/balances", cryptoRead(http.HandlerFunc(deps.Crypto.ListBalances)))
	mux.Handle("POST /v1/crypto/wallets/{id}/addresses", cryptoWrite(http.HandlerFunc(deps.Crypto.CreateAddress)))
	mux.Handle("GET /v1/crypto/wallets/{id}/addresses", cryptoRead(http.HandlerFunc(deps.Crypto.ListAddresses)))
	mux.Handle("GET /v1/crypto/chain-transactions", cryptoRead(http.HandlerFunc(deps.Crypto.MyChainTransactions)))
	mux.Handle("POST /v1/savings-goals", savingsRead(http.HandlerFunc(deps.Savings.Create)))
	mux.Handle("GET /v1/savings-goals", savingsRead(http.HandlerFunc(deps.Savings.List)))
	mux.Handle("GET /v1/savings-goals/{id}", savingsRead(http.HandlerFunc(deps.Savings.Get)))
	mux.Handle("PATCH /v1/savings-goals/{id}", savingsRead(http.HandlerFunc(deps.Savings.Update)))
	mux.Handle("POST /v1/savings-goals/{id}/contributions", savingsWrite(http.HandlerFunc(deps.Savings.Contribute)))
	mux.Handle("POST /v1/savings-goals/{id}/withdrawals", savingsWrite(http.HandlerFunc(deps.Savings.Withdraw)))
	mux.Handle("POST /v1/savings-goals/{id}/pause", savingsRead(http.HandlerFunc(deps.Savings.Pause)))
	mux.Handle("POST /v1/savings-goals/{id}/resume", savingsRead(http.HandlerFunc(deps.Savings.Resume)))
	mux.Handle("POST /v1/savings-goals/{id}/close", savingsWrite(http.HandlerFunc(deps.Savings.Close)))
	mux.Handle("GET /v1/savings-goals/{id}/transactions", savingsRead(http.HandlerFunc(deps.Savings.ListTransactions)))
	mux.Handle("GET /v1/fx/rates", fxRead(http.HandlerFunc(deps.FX.ListRates)))
	mux.Handle("POST /v1/fx/quotes", fxRead(http.HandlerFunc(deps.FX.CreateQuote)))
	mux.Handle("POST /v1/fx/conversions", fxWrite(http.HandlerFunc(deps.FX.ConvertQuote)))
	mux.Handle("POST /v1/virtual-cards", cardsWrite(http.HandlerFunc(deps.Cards.Create)))
	mux.Handle("GET /v1/virtual-cards", cardsRead(http.HandlerFunc(deps.Cards.List)))
	mux.Handle("GET /v1/virtual-cards/{id}", cardsRead(http.HandlerFunc(deps.Cards.Get)))
	mux.Handle("POST /v1/virtual-cards/{id}/freeze", cardsWrite(http.HandlerFunc(deps.Cards.Freeze)))
	mux.Handle("POST /v1/virtual-cards/{id}/unfreeze", cardsWrite(http.HandlerFunc(deps.Cards.Unfreeze)))
	mux.Handle("POST /v1/virtual-cards/{id}/cancel", cardsWrite(http.HandlerFunc(deps.Cards.Cancel)))
	mux.Handle("PATCH /v1/virtual-cards/{id}/spending-limit", cardsWrite(http.HandlerFunc(deps.Cards.UpdateSpendingLimit)))
	mux.Handle("POST /v1/virtual-cards/{id}/sync-spending-limit", cardsWrite(http.HandlerFunc(deps.Cards.SyncSpendingLimit)))
	mux.Handle("GET /v1/virtual-cards/{id}/authorizations", cardsRead(http.HandlerFunc(deps.Cards.ListAuthorizations)))
	mux.Handle("POST /v1/virtual-cards/{id}/authorizations/{authorizationID}/disputes", cardsWrite(http.HandlerFunc(deps.Cards.CreateDispute)))
	mux.Handle("GET /v1/card-disputes", cardsRead(http.HandlerFunc(deps.Cards.MyDisputes)))
	mux.Handle("GET /v1/card-notifications", cardsRead(http.HandlerFunc(deps.Cards.Notifications)))
	mux.Handle("PATCH /v1/card-notifications/{id}/read", cardsRead(http.HandlerFunc(deps.Cards.MarkNotificationRead)))
	mux.Handle("POST /v1/transfers", paymentsWrite(http.HandlerFunc(deps.Transfers.Create)))
	mux.Handle("GET /v1/transfers", paymentsRead(http.HandlerFunc(deps.Transfers.List)))
	mux.Handle("GET /v1/payment-notifications", paymentsRead(http.HandlerFunc(deps.Transfers.Notifications)))
	mux.Handle("POST /v1/beneficiaries", paymentsRead(http.HandlerFunc(deps.Beneficiaries.Create)))
	mux.Handle("GET /v1/beneficiaries", paymentsRead(http.HandlerFunc(deps.Beneficiaries.List)))
	mux.Handle("DELETE /v1/beneficiaries/{id}", paymentsRead(http.HandlerFunc(deps.Beneficiaries.Delete)))
	mux.Handle("GET /v1/admin/dashboard/summary", adminOnly(http.HandlerFunc(deps.Admin.Summary)))
	mux.Handle("GET /v1/admin/dashboard/activity", adminOnly(http.HandlerFunc(deps.Admin.Activity)))
	mux.Handle("GET /v1/admin/dashboard/compliance-queue", adminOnly(http.HandlerFunc(deps.Admin.ComplianceQueue)))
	mux.Handle("GET /v1/admin/settlements/sepa", featureAdminRead("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.SEPASettlements)))
	mux.Handle("GET /v1/admin/settlements/sepa/events", featureAdminRead("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.SEPASettlementEvents)))
	mux.Handle("GET /v1/admin/settlements/sepa/provider-reports", featureAdminRead("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Settlement.ProviderReports)))
	mux.Handle("POST /v1/admin/settlements/sepa/provider-reports", featureAdminWrite("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Settlement.IngestProviderReport)))
	mux.Handle("GET /v1/admin/settlements/sepa/inbound-payments", featureAdminRead("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Settlement.InboundPayments)))
	mux.Handle("POST /v1/admin/settlements/sepa/inbound-payments", featureAdminWrite("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Settlement.ProcessInboundPayment)))
	mux.Handle("GET /v1/admin/payments/reviews", featureAdminRead("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.PaymentReviews)))
	mux.Handle("POST /v1/admin/payments/{id}/hold", featureAdminWrite("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.HoldPaymentForReview)))
	mux.Handle("POST /v1/admin/payments/reviews/{id}/release", featureAdminWrite("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.ReleasePaymentReview)))
	mux.Handle("POST /v1/admin/payments/reviews/{id}/reject", featureAdminWrite("payments", deps.Config.Features.Payments, http.HandlerFunc(deps.Admin.RejectPaymentReview)))
	mux.Handle("GET /v1/admin/fx/rates", featureAdminRead("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.ListRates)))
	mux.Handle("POST /v1/admin/fx/rates", featureAdminWrite("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminCreateRate)))
	mux.Handle("GET /v1/admin/fx/rate-change-requests", featureAdminRead("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminRateChangeRequests)))
	mux.Handle("POST /v1/admin/fx/rate-change-requests/{id}/approve", featureAdminWrite("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminApproveRateChangeRequest)))
	mux.Handle("POST /v1/admin/fx/rate-change-requests/{id}/reject", featureAdminWrite("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminRejectRateChangeRequest)))
	mux.Handle("POST /v1/admin/fx/rate-change-requests/{id}/cancel", featureAdminWrite("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminCancelRateChangeRequest)))
	mux.Handle("GET /v1/admin/fx/monitoring", featureAdminRead("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminMonitoring)))
	mux.Handle("GET /v1/admin/fx/treasury", featureAdminRead("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminTreasury)))
	mux.Handle("GET /v1/admin/fx/audit-export", featureAdminRead("fx", deps.Config.Features.FX, http.HandlerFunc(deps.FX.AdminAuditExport)))
	mux.Handle("GET /v1/admin/reconciliation/dashboard", adminOnly(http.HandlerFunc(deps.Reconciliation.Dashboard)))
	mux.Handle("POST /v1/admin/reconciliation/runs", adminWrite(http.HandlerFunc(deps.Reconciliation.Run)))
	mux.Handle("POST /v1/admin/reconciliation/daily-runs", adminWrite(http.HandlerFunc(deps.Reconciliation.RunDaily)))
	mux.Handle("POST /v1/admin/reconciliation/provider-balances", adminWrite(http.HandlerFunc(deps.Reconciliation.CreateProviderBalanceSnapshot)))
	mux.Handle("GET /v1/admin/reconciliation/eod-snapshots", adminOnly(http.HandlerFunc(deps.Reconciliation.EndOfDaySnapshots)))
	mux.Handle("POST /v1/admin/reconciliation/eod-snapshots", adminWrite(http.HandlerFunc(deps.Reconciliation.CreateEndOfDaySnapshots)))
	mux.Handle("PATCH /v1/admin/reconciliation/breaks/{id}", adminWrite(http.HandlerFunc(deps.Reconciliation.DecideBreak)))
	mux.Handle("GET /v1/admin/risk/dashboard", adminOnly(http.HandlerFunc(deps.Risk.Dashboard)))
	mux.Handle("GET /v1/admin/risk/rules", adminOnly(http.HandlerFunc(deps.Risk.ListRules)))
	mux.Handle("POST /v1/admin/risk/rules", adminWrite(http.HandlerFunc(deps.Risk.CreateRule)))
	mux.Handle("PATCH /v1/admin/risk/rules/{id}", adminWrite(http.HandlerFunc(deps.Risk.UpdateRule)))
	mux.Handle("GET /v1/admin/risk/events", adminOnly(http.HandlerFunc(deps.Risk.ListEvents)))
	mux.Handle("GET /v1/admin/security/dashboard", adminOnly(http.HandlerFunc(deps.AuthGuard.Dashboard)))
	mux.Handle("GET /v1/admin/security/events", adminOnly(http.HandlerFunc(deps.AuthGuard.Events)))
	mux.Handle("PATCH /v1/admin/security/events/{id}", adminWrite(http.HandlerFunc(deps.AuthGuard.DecideEvent)))
	mux.Handle("GET /v1/admin/appsec/dashboard", adminOnly(http.HandlerFunc(deps.AppSec.Dashboard)))
	mux.Handle("GET /v1/admin/appsec/controls", adminOnly(http.HandlerFunc(deps.AppSec.Controls)))
	mux.Handle("POST /v1/admin/appsec/controls", adminWrite(http.HandlerFunc(deps.AppSec.UpsertControl)))
	mux.Handle("GET /v1/admin/appsec/key-rotation-runs", adminOnly(http.HandlerFunc(deps.AppSec.KeyRotationRuns)))
	mux.Handle("POST /v1/admin/appsec/key-rotation-runs", adminWrite(http.HandlerFunc(deps.AppSec.CreateKeyRotationRun)))
	mux.Handle("PATCH /v1/admin/appsec/key-rotation-runs/{id}", adminWrite(http.HandlerFunc(deps.AppSec.UpdateKeyRotationRun)))
	mux.Handle("GET /v1/admin/backoffice/dashboard", adminOnly(http.HandlerFunc(deps.Backoffice.Dashboard)))
	mux.Handle("POST /v1/admin/backoffice/queues/sync", adminWrite(http.HandlerFunc(deps.Backoffice.SyncQueues)))
	mux.Handle("GET /v1/admin/backoffice/cases", adminOnly(http.HandlerFunc(deps.Backoffice.Cases)))
	mux.Handle("POST /v1/admin/backoffice/cases", adminWrite(http.HandlerFunc(deps.Backoffice.CreateCase)))
	mux.Handle("PATCH /v1/admin/backoffice/cases/{id}", adminWrite(http.HandlerFunc(deps.Backoffice.UpdateCase)))
	mux.Handle("GET /v1/admin/backoffice/cases/{id}/notes", adminOnly(http.HandlerFunc(deps.Backoffice.CaseNotes)))
	mux.Handle("POST /v1/admin/backoffice/cases/{id}/notes", adminWrite(http.HandlerFunc(deps.Backoffice.AddCaseNote)))
	mux.Handle("GET /v1/admin/backoffice/cases/{id}/attachments", adminOnly(http.HandlerFunc(deps.Backoffice.CaseAttachments)))
	mux.Handle("POST /v1/admin/backoffice/cases/{id}/attachments", adminWrite(http.HandlerFunc(deps.Backoffice.AddCaseAttachment)))
	mux.Handle("GET /v1/admin/backoffice/cases/{id}/history", adminOnly(http.HandlerFunc(deps.Backoffice.CaseHistory)))
	mux.Handle("GET /v1/admin/backoffice/config-change-requests", adminOnly(http.HandlerFunc(deps.Backoffice.ConfigChanges)))
	mux.Handle("POST /v1/admin/backoffice/config-change-requests", adminWrite(http.HandlerFunc(deps.Backoffice.CreateConfigChange)))
	mux.Handle("POST /v1/admin/backoffice/config-change-requests/{id}/approve", adminWrite(http.HandlerFunc(deps.Backoffice.ApproveConfigChange)))
	mux.Handle("POST /v1/admin/backoffice/config-change-requests/{id}/reject", adminWrite(http.HandlerFunc(deps.Backoffice.RejectConfigChange)))
	mux.Handle("POST /v1/admin/backoffice/config-change-requests/{id}/cancel", adminWrite(http.HandlerFunc(deps.Backoffice.CancelConfigChange)))
	mux.Handle("POST /v1/admin/backoffice/config-change-requests/{id}/implement", adminWrite(http.HandlerFunc(deps.Backoffice.ImplementConfigChange)))
	mux.Handle("GET /v1/admin/backoffice/action-export", adminOnly(http.HandlerFunc(deps.Backoffice.ActionExport)))
	mux.Handle("GET /v1/admin/backoffice/evidence-package", adminOnly(http.HandlerFunc(deps.Backoffice.EvidencePackage)))
	mux.Handle("GET /v1/admin/backoffice/impersonation-sessions", adminOnly(http.HandlerFunc(deps.Backoffice.ImpersonationSessions)))
	mux.Handle("POST /v1/admin/backoffice/impersonation-sessions", adminWrite(http.HandlerFunc(deps.Backoffice.StartImpersonation)))
	mux.Handle("POST /v1/admin/backoffice/impersonation-sessions/{id}/end", adminWrite(http.HandlerFunc(deps.Backoffice.EndImpersonation)))
	mux.Handle("GET /v1/admin/cards/processor-configs", featureAdminRead("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminProcessorConfigs)))
	mux.Handle("POST /v1/admin/cards/processor-configs", featureAdminWrite("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminUpsertProcessorConfig)))
	mux.Handle("GET /v1/admin/cards/pci-scope-assessments", featureAdminRead("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminPCIAssessments)))
	mux.Handle("POST /v1/admin/cards/pci-scope-assessments", featureAdminWrite("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminUpsertPCIAssessment)))
	mux.Handle("GET /v1/admin/cards/tokenization-policies", featureAdminRead("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminTokenizationPolicies)))
	mux.Handle("POST /v1/admin/cards/tokenization-policies", featureAdminWrite("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminUpsertTokenizationPolicy)))
	mux.Handle("POST /v1/admin/cards/authorizations/expire", featureAdminWrite("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminExpireAuthorizations)))
	mux.Handle("GET /v1/admin/cards/disputes", featureAdminRead("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminDisputes)))
	mux.Handle("PATCH /v1/admin/cards/disputes/{id}", featureAdminWrite("cards", deps.Config.Features.Cards, http.HandlerFunc(deps.Cards.AdminDecideDispute)))
	mux.Handle("GET /v1/admin/mfa-reset-requests", adminOnly(http.HandlerFunc(deps.Admin.MFAResetRequests)))
	mux.Handle("POST /v1/admin/mfa-reset-requests", adminWrite(http.HandlerFunc(deps.Admin.CreateMFAResetRequest)))
	mux.Handle("POST /v1/admin/mfa-reset-requests/{id}/approve", adminWrite(http.HandlerFunc(deps.Admin.ApproveMFAResetRequest)))
	mux.Handle("POST /v1/admin/mfa-reset-requests/{id}/reject", adminWrite(http.HandlerFunc(deps.Admin.RejectMFAResetRequest)))
	mux.Handle("POST /v1/admin/mfa-reset-requests/{id}/cancel", adminWrite(http.HandlerFunc(deps.Admin.CancelMFAResetRequest)))
	mux.Handle("GET /v1/admin/role-change-requests", adminOnly(http.HandlerFunc(deps.Admin.RoleChangeRequests)))
	mux.Handle("POST /v1/admin/role-change-requests", adminWrite(http.HandlerFunc(deps.Admin.CreateRoleChangeRequest)))
	mux.Handle("POST /v1/admin/role-change-requests/{id}/approve", adminWrite(http.HandlerFunc(deps.Admin.ApproveRoleChangeRequest)))
	mux.Handle("POST /v1/admin/role-change-requests/{id}/reject", adminWrite(http.HandlerFunc(deps.Admin.RejectRoleChangeRequest)))
	mux.Handle("POST /v1/admin/role-change-requests/{id}/cancel", adminWrite(http.HandlerFunc(deps.Admin.CancelRoleChangeRequest)))
	mux.Handle("GET /v1/admin/jit-elevation-requests", adminOnly(http.HandlerFunc(deps.Admin.JITElevationRequests)))
	mux.Handle("POST /v1/admin/jit-elevation-requests", adminWrite(http.HandlerFunc(deps.Admin.CreateJITElevationRequest)))
	mux.Handle("POST /v1/admin/jit-elevation-requests/{id}/approve", adminWrite(http.HandlerFunc(deps.Admin.ApproveJITElevationRequest)))
	mux.Handle("POST /v1/admin/jit-elevation-requests/{id}/reject", adminWrite(http.HandlerFunc(deps.Admin.RejectJITElevationRequest)))
	mux.Handle("POST /v1/admin/jit-elevation-requests/{id}/cancel", adminWrite(http.HandlerFunc(deps.Admin.CancelJITElevationRequest)))
	mux.Handle("POST /v1/admin/jit-elevation-requests/{id}/revoke", adminWrite(http.HandlerFunc(deps.Admin.RevokeJITElevationRequest)))
	mux.Handle("GET /v1/admin/routing-code-requirements", adminOnly(http.HandlerFunc(deps.Admin.RoutingCodeRequirements)))
	mux.Handle("POST /v1/admin/routing-code-requirements", adminWrite(http.HandlerFunc(deps.Admin.CreateRoutingCodeRequirement)))
	mux.Handle("PATCH /v1/admin/routing-code-requirements/{id}", adminWrite(http.HandlerFunc(deps.Admin.UpdateRoutingCodeRequirement)))
	mux.Handle("GET /v1/admin/crypto/custody-scope-decisions", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminCustodyScopeDecisions)))
	mux.Handle("POST /v1/admin/crypto/custody-scope-decisions", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminCreateCustodyScopeDecision)))
	mux.Handle("GET /v1/admin/crypto/legal-memos", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminLegalMemos)))
	mux.Handle("POST /v1/admin/crypto/legal-memos", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertLegalMemo)))
	mux.Handle("GET /v1/admin/crypto/custody-provider-configs", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminCustodyProviderConfigs)))
	mux.Handle("POST /v1/admin/crypto/custody-provider-configs", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertCustodyProviderConfig)))
	mux.Handle("GET /v1/admin/crypto/address-screenings", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminAddressScreenings)))
	mux.Handle("POST /v1/admin/crypto/address-screenings", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminScreenAddress)))
	mux.Handle("GET /v1/admin/crypto/chain-transactions", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminChainTransactions)))
	mux.Handle("POST /v1/admin/crypto/chain-transactions", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminCreateChainTransaction)))
	mux.Handle("PATCH /v1/admin/crypto/chain-transactions/{id}", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpdateChainTransactionStatus)))
	mux.Handle("GET /v1/admin/crypto/travel-rule-transfers", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminTravelRuleTransfers)))
	mux.Handle("POST /v1/admin/crypto/travel-rule-transfers", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertTravelRuleTransfer)))
	mux.Handle("GET /v1/admin/crypto/custody-responsibilities", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminCustodyResponsibilities)))
	mux.Handle("POST /v1/admin/crypto/custody-responsibilities", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertCustodyResponsibility)))
	mux.Handle("GET /v1/admin/crypto/stablecoin-issuer-monitoring", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminStablecoinIssuerMonitoring)))
	mux.Handle("POST /v1/admin/crypto/stablecoin-issuer-monitoring", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertStablecoinIssuerMonitoring)))
	mux.Handle("GET /v1/admin/crypto/stablecoin-network-controls", featureAdminRead("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminStablecoinNetworkControls)))
	mux.Handle("POST /v1/admin/crypto/stablecoin-network-controls", featureAdminWrite("crypto", deps.Config.Features.Crypto, http.HandlerFunc(deps.Crypto.AdminUpsertStablecoinNetworkControl)))
	mux.Handle("GET /v1/admin/users", adminOnly(http.HandlerFunc(deps.Admin.ListUsers)))
	mux.Handle("GET /v1/admin/users/{id}", adminOnly(http.HandlerFunc(deps.Admin.GetUser)))
	mux.Handle("GET /v1/admin/users/{id}/scopes", adminOnly(http.HandlerFunc(deps.Admin.AdminScopes)))
	mux.Handle("POST /v1/admin/users/{id}/scopes", adminWrite(http.HandlerFunc(deps.Admin.GrantAdminScope)))
	mux.Handle("DELETE /v1/admin/users/{id}/scopes/{scope}", adminWrite(http.HandlerFunc(deps.Admin.RevokeAdminScope)))
	mux.Handle("GET /v1/admin/users/{id}/sessions", adminOnly(http.HandlerFunc(deps.Admin.UserSessions)))
	mux.Handle("POST /v1/admin/users/{id}/sessions/{sessionID}/revoke", adminWrite(http.HandlerFunc(deps.Admin.RevokeUserSession)))
	mux.Handle("GET /v1/admin/users/{id}/ledger-entries", adminOnly(http.HandlerFunc(deps.Admin.UserLedgerEntries)))
	mux.Handle("GET /v1/admin/accounts/{id}", adminOnly(http.HandlerFunc(deps.Admin.GetAccount)))
	mux.Handle("GET /v1/admin/accounts/{id}/ledger-entries", adminOnly(http.HandlerFunc(deps.Admin.AccountLedgerEntries)))
	mux.Handle("GET /v1/admin/wallets/{id}/balance-adjustments", adminMoneyRead(http.HandlerFunc(deps.Admin.WalletAdjustments)))
	mux.Handle("POST /v1/admin/wallets/{id}/balance-adjustment-requests", adminMoneyWrite(http.HandlerFunc(deps.Admin.CreateWalletAdjustmentRequest)))
	mux.Handle("GET /v1/admin/wallet-balance-adjustment-requests", adminMoneyRead(http.HandlerFunc(deps.Admin.WalletAdjustmentRequests)))
	mux.Handle("POST /v1/admin/wallet-balance-adjustment-requests/{id}/approve", adminMoneyWrite(http.HandlerFunc(deps.Admin.ApproveWalletAdjustmentRequest)))
	mux.Handle("POST /v1/admin/wallet-balance-adjustment-requests/{id}/reject", adminMoneyWrite(http.HandlerFunc(deps.Admin.RejectWalletAdjustmentRequest)))
	mux.Handle("POST /v1/admin/wallet-balance-adjustment-requests/{id}/cancel", adminMoneyWrite(http.HandlerFunc(deps.Admin.CancelWalletAdjustmentRequest)))
	mux.Handle("GET /v1/admin/kyc/profiles", adminOnly(http.HandlerFunc(deps.KYC.ListProfiles)))
	mux.Handle("PATCH /v1/admin/kyc/profiles/{id}/status", complianceWrite(http.HandlerFunc(deps.KYC.UpdateProfileStatus)))
	mux.Handle("GET /v1/admin/kyc/profiles/{id}/evidence", adminOnly(http.HandlerFunc(deps.KYC.ProfileEvidence)))
	mux.Handle("GET /v1/admin/kyc/profiles/{id}/provider-decisions", adminOnly(http.HandlerFunc(deps.KYC.ProfileProviderDecisions)))
	mux.Handle("GET /v1/admin/aml/screenings", adminOnly(http.HandlerFunc(deps.AML.ListScreenings)))
	mux.Handle("GET /v1/admin/aml/cases", adminOnly(http.HandlerFunc(deps.AML.ListCases)))
	mux.Handle("PATCH /v1/admin/aml/cases/{id}", complianceWrite(http.HandlerFunc(deps.AML.UpdateCase)))
	mux.Handle("GET /v1/admin/aml/cases/{id}/notes", adminOnly(http.HandlerFunc(deps.AML.CaseNotes)))
	mux.Handle("POST /v1/admin/aml/cases/{id}/notes", complianceWrite(http.HandlerFunc(deps.AML.AddCaseNote)))
	mux.Handle("GET /v1/admin/aml/cases/{id}/attachments", adminOnly(http.HandlerFunc(deps.AML.CaseAttachments)))
	mux.Handle("POST /v1/admin/aml/cases/{id}/attachments", complianceWrite(http.HandlerFunc(deps.AML.AddCaseAttachment)))
	mux.Handle("GET /v1/admin/aml/cases/{id}/history", adminOnly(http.HandlerFunc(deps.AML.CaseHistory)))
	mux.Handle("POST /v1/admin/aml/cases/{id}/false-positive", complianceWrite(http.HandlerFunc(deps.AML.RecordFalsePositive)))
	mux.Handle("GET /v1/admin/compliance/dashboard", adminOnly(http.HandlerFunc(deps.Compliance.Dashboard)))
	mux.Handle("GET /v1/admin/compliance/customer-risk", adminOnly(http.HandlerFunc(deps.Compliance.CustomerRiskProfiles)))
	mux.Handle("POST /v1/admin/compliance/customer-risk/{id}/recalculate", complianceWrite(http.HandlerFunc(deps.Compliance.RecalculateCustomerRisk)))
	mux.Handle("POST /v1/admin/compliance/monitoring-runs", complianceWrite(http.HandlerFunc(deps.Compliance.RunMonitoring)))
	mux.Handle("GET /v1/admin/compliance/edd-reviews", adminOnly(http.HandlerFunc(deps.Compliance.EDDReviews)))
	mux.Handle("POST /v1/admin/compliance/edd-reviews", complianceWrite(http.HandlerFunc(deps.Compliance.CreateEDDReview)))
	mux.Handle("PATCH /v1/admin/compliance/edd-reviews/{id}", complianceWrite(http.HandlerFunc(deps.Compliance.DecideEDDReview)))
	mux.Handle("GET /v1/admin/compliance/regulatory-reports", adminOnly(http.HandlerFunc(deps.Compliance.RegulatoryReports)))
	mux.Handle("POST /v1/admin/compliance/regulatory-reports", complianceWrite(http.HandlerFunc(deps.Compliance.CreateRegulatoryReport)))
	mux.Handle("PATCH /v1/admin/compliance/regulatory-reports/{id}", complianceWrite(http.HandlerFunc(deps.Compliance.UpdateRegulatoryReport)))
	mux.Handle("GET /v1/admin/compliance/tuning-rules", adminOnly(http.HandlerFunc(deps.Compliance.TuningRules)))
	mux.Handle("POST /v1/admin/compliance/tuning-rules", complianceWrite(http.HandlerFunc(deps.Compliance.CreateTuningRule)))
	mux.Handle("PATCH /v1/admin/compliance/tuning-rules/{id}", complianceWrite(http.HandlerFunc(deps.Compliance.UpdateTuningRule)))
	mux.Handle("GET /v1/admin/ledger/trial-balance", adminOnly(http.HandlerFunc(deps.Ledger.TrialBalance)))
	mux.Handle("GET /v1/admin/ledger/export", adminOnly(http.HandlerFunc(deps.Ledger.Export)))
	mux.Handle("GET /v1/admin/ledger/rounding-policies", adminOnly(http.HandlerFunc(deps.Ledger.RoundingPolicies)))
	mux.Handle("GET /v1/admin/ledger/reversals", adminMoneyRead(http.HandlerFunc(deps.Ledger.ListReversals)))
	mux.Handle("GET /v1/admin/ledger/journal-entries", adminOnly(http.HandlerFunc(deps.Ledger.ListJournalEntries)))
	mux.Handle("GET /v1/admin/ledger/journal-entries/{id}", adminOnly(http.HandlerFunc(deps.Ledger.GetJournalEntry)))
	mux.Handle("POST /v1/admin/ledger/journal-entries/{id}/reversals", adminMoneyWrite(http.HandlerFunc(deps.Ledger.CreateJournalReversal)))
	mux.Handle("GET /v1/admin/privacy/dashboard", adminOnly(http.HandlerFunc(deps.Privacy.Dashboard)))
	mux.Handle("GET /v1/admin/privacy/inventory", adminOnly(http.HandlerFunc(deps.Privacy.Inventory)))
	mux.Handle("GET /v1/admin/privacy/retention-policies", adminOnly(http.HandlerFunc(deps.Privacy.RetentionPolicies)))
	mux.Handle("POST /v1/admin/privacy/retention-policies", complianceWrite(http.HandlerFunc(deps.Privacy.UpsertRetentionPolicy)))
	mux.Handle("GET /v1/admin/privacy/data-subject-requests", adminOnly(http.HandlerFunc(deps.Privacy.DataSubjectRequests)))
	mux.Handle("POST /v1/admin/privacy/data-subject-requests", complianceWrite(http.HandlerFunc(deps.Privacy.CreateDataSubjectRequest)))
	mux.Handle("PATCH /v1/admin/privacy/data-subject-requests/{id}", complianceWrite(http.HandlerFunc(deps.Privacy.UpdateDataSubjectRequest)))
	mux.Handle("POST /v1/admin/privacy/data-subject-requests/{id}/process", complianceWrite(http.HandlerFunc(deps.Privacy.ProcessDataSubjectRequest)))
	mux.Handle("GET /v1/admin/privacy/notices", adminOnly(http.HandlerFunc(deps.Privacy.Notices)))
	mux.Handle("POST /v1/admin/privacy/notices", complianceWrite(http.HandlerFunc(deps.Privacy.UpsertNotice)))
	mux.Handle("POST /v1/admin/privacy/notices/{id}/publish", complianceWrite(http.HandlerFunc(deps.Privacy.PublishNotice)))
	mux.Handle("GET /v1/admin/privacy/security-attestations", adminOnly(http.HandlerFunc(deps.Privacy.SecurityAttestations)))
	mux.Handle("POST /v1/admin/privacy/security-attestations", complianceWrite(http.HandlerFunc(deps.Privacy.CreateSecurityAttestation)))
	mux.Handle("GET /v1/admin/privacy/residency-policies", adminOnly(http.HandlerFunc(deps.Privacy.ResidencyPolicies)))
	mux.Handle("POST /v1/admin/privacy/residency-policies", complianceWrite(http.HandlerFunc(deps.Privacy.UpsertResidencyPolicy)))
	mux.Handle("GET /v1/admin/privacy/audit-integrity", adminOnly(http.HandlerFunc(deps.Privacy.AuditIntegrity)))
	mux.Handle("GET /v1/admin/providers/dashboard", adminOnly(http.HandlerFunc(deps.Providers.Dashboard)))
	mux.Handle("GET /v1/admin/providers/integrations", adminOnly(http.HandlerFunc(deps.Providers.Integrations)))
	mux.Handle("POST /v1/admin/providers/integrations", adminWrite(http.HandlerFunc(deps.Providers.UpsertIntegration)))
	mux.Handle("GET /v1/admin/providers/outbound-calls", adminOnly(http.HandlerFunc(deps.Providers.OutboundCalls)))
	mux.Handle("GET /v1/admin/providers/webhook-events", adminOnly(http.HandlerFunc(deps.Providers.WebhookEvents)))
	mux.Handle("GET /v1/admin/providers/sandbox-test-runs", adminOnly(http.HandlerFunc(deps.Providers.SandboxTestRuns)))
	mux.Handle("POST /v1/admin/providers/sandbox-test-runs", adminWrite(http.HandlerFunc(deps.Providers.CreateSandboxTestRun)))
	mux.Handle("GET /v1/admin/legal/dashboard", adminOnly(http.HandlerFunc(deps.Legal.Dashboard)))
	mux.Handle("GET /v1/admin/legal/controls", adminOnly(http.HandlerFunc(deps.Legal.Controls)))
	mux.Handle("POST /v1/admin/legal/controls", adminWrite(http.HandlerFunc(deps.Legal.UpsertControl)))
	mux.Handle("GET /v1/admin/legal/regulated-activities", adminOnly(http.HandlerFunc(deps.Legal.RegulatedActivities)))
	mux.Handle("POST /v1/admin/legal/regulated-activities", adminWrite(http.HandlerFunc(deps.Legal.UpsertRegulatedActivity)))
	mux.Handle("GET /v1/admin/legal/operating-models", adminOnly(http.HandlerFunc(deps.Legal.OperatingModels)))
	mux.Handle("POST /v1/admin/legal/operating-models", adminWrite(http.HandlerFunc(deps.Legal.UpsertOperatingModel)))
	mux.Handle("GET /v1/admin/legal/legal-memos", adminOnly(http.HandlerFunc(deps.Legal.LegalMemos)))
	mux.Handle("POST /v1/admin/legal/legal-memos", adminWrite(http.HandlerFunc(deps.Legal.UpsertLegalMemo)))
	mux.Handle("GET /v1/admin/legal/product-term-approvals", adminOnly(http.HandlerFunc(deps.Legal.ProductTermApprovals)))
	mux.Handle("POST /v1/admin/legal/product-term-approvals", adminWrite(http.HandlerFunc(deps.Legal.UpsertProductTermApproval)))
	mux.Handle("GET /v1/admin/legal/provider-contracts", adminOnly(http.HandlerFunc(deps.Legal.ProviderContracts)))
	mux.Handle("POST /v1/admin/legal/provider-contracts", adminWrite(http.HandlerFunc(deps.Legal.UpsertProviderContract)))
	mux.Handle("GET /v1/admin/legal/safeguarding-models", adminOnly(http.HandlerFunc(deps.Legal.SafeguardingModels)))
	mux.Handle("POST /v1/admin/legal/safeguarding-models", adminWrite(http.HandlerFunc(deps.Legal.UpsertSafeguardingModel)))
	mux.Handle("GET /v1/admin/legal/policy-documents", adminOnly(http.HandlerFunc(deps.Legal.PolicyDocuments)))
	mux.Handle("POST /v1/admin/legal/policy-documents", adminWrite(http.HandlerFunc(deps.Legal.UpsertPolicyDocument)))
	mux.Handle("GET /v1/admin/legal/jurisdiction-rules", adminOnly(http.HandlerFunc(deps.Legal.JurisdictionRules)))
	mux.Handle("POST /v1/admin/legal/jurisdiction-rules", adminWrite(http.HandlerFunc(deps.Legal.UpsertJurisdictionRule)))
	mux.Handle("GET /v1/admin/go-live/dashboard", adminOnly(http.HandlerFunc(deps.GoLive.Dashboard)))
	mux.Handle("GET /v1/admin/go-live/gates", adminOnly(http.HandlerFunc(deps.GoLive.Gates)))
	mux.Handle("POST /v1/admin/go-live/gates", adminWrite(http.HandlerFunc(deps.GoLive.UpsertGate)))
	mux.Handle("GET /v1/admin/go-live/staging", adminOnly(http.HandlerFunc(deps.GoLive.StagingGates)))
	mux.Handle("POST /v1/admin/go-live/staging", adminWrite(http.HandlerFunc(deps.GoLive.UpsertStagingGate)))
	mux.Handle("GET /v1/admin/go-live/beta", adminOnly(http.HandlerFunc(deps.GoLive.BetaGates)))
	mux.Handle("POST /v1/admin/go-live/beta", adminWrite(http.HandlerFunc(deps.GoLive.UpsertBetaGate)))
	mux.Handle("GET /v1/admin/go-live/production", adminOnly(http.HandlerFunc(deps.GoLive.ProductionGates)))
	mux.Handle("POST /v1/admin/go-live/production", adminWrite(http.HandlerFunc(deps.GoLive.UpsertProductionGate)))
	mux.Handle("GET /v1/audit-events", adminOnly(http.HandlerFunc(deps.Audit.List)))

	return middleware.Chain(
		mux,
		requestCorrelation,
		middleware.Metrics(deps.Metrics),
		middleware.RequestLogger(deps.Logger),
		middleware.RateLimit(rateLimiter),
		middleware.EdgeSecurity(middleware.EdgeSecurityOptions{
			TLSRequired:          deps.Config.Security.TLSRequired,
			HSTSEnabled:          deps.Config.Security.HSTSEnabled,
			AllowedHosts:         deps.Config.Security.AllowedHosts,
			TrustedProxyRequired: deps.Config.Security.TrustedProxyRequired,
		}),
		middleware.SecurityHeaders,
		middleware.CSRFProtection(middleware.CSRFOptions{
			Enabled:        deps.Config.Security.CSRFEnabled,
			AuthCookieName: "banking_refresh_token",
			CSRFCookieName: "banking_csrf_token",
			HeaderName:     "X-CSRF-Token",
		}),
		middleware.MaxBodyBytes(deps.Config.HTTP.MaxRequestBytes),
		middleware.Timeout(30*time.Second),
		middleware.Recoverer(deps.Logger),
	)
}

func healthz(cfg config.Config) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		respond.JSON(w, http.StatusOK, map[string]string{
			"status": "ok",
			"name":   cfg.App.Name,
			"env":    cfg.App.Environment,
		})
	}
}

func readyz(db *pgxpool.Pool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
		defer cancel()

		if err := db.Ping(ctx); err != nil {
			respond.Problem(w, http.StatusServiceUnavailable, "not_ready", "database is not ready")
			return
		}

		respond.JSON(w, http.StatusOK, map[string]string{"status": "ready"})
	}
}
