package crypto

import (
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"strconv"
	"strings"

	"github.com/niels/banking-app/backend/internal/audit"
	"github.com/niels/banking-app/backend/internal/domain"
	"github.com/niels/banking-app/backend/internal/httpapi/middleware"
	"github.com/niels/banking-app/backend/internal/integrations/blockchainanalytics"
	"github.com/niels/banking-app/backend/internal/integrations/custody"
	"github.com/niels/banking-app/backend/internal/platform/httputil"
	"github.com/niels/banking-app/backend/internal/respond"
)

type Handler struct {
	repo       *Repository
	audit      *audit.Repository
	custody    custody.Provider
	analytics  blockchainanalytics.Provider
	compliance interface {
		RequireClear(ctx context.Context, userID string) error
	}
	log *slog.Logger
}

type createWalletRequest struct {
	Name string `json:"name"`
}

type addAssetRequest struct {
	CryptoAssetID string `json:"crypto_asset_id"`
}

type createAddressRequest struct {
	CryptoAssetID string `json:"crypto_asset_id"`
}

type custodyScopeDecisionRequest struct {
	ProductScope        string   `json:"product_scope"`
	DecisionStatus      string   `json:"decision_status"`
	RealMovementEnabled bool     `json:"real_movement_enabled"`
	JurisdictionScope   []string `json:"jurisdiction_scope"`
	Rationale           string   `json:"rationale"`
	EvidenceReference   string   `json:"evidence_reference"`
}

type addressScreeningRequest struct {
	UserID         string `json:"user_id"`
	CryptoWalletID string `json:"crypto_wallet_id"`
	CryptoAssetID  string `json:"crypto_asset_id"`
	Address        string `json:"address"`
	AddressTag     string `json:"address_tag"`
	Network        string `json:"network"`
	Symbol         string `json:"symbol"`
	Context        string `json:"context"`
}

type chainTransactionRequest struct {
	UserID             string          `json:"user_id"`
	CryptoWalletID     string          `json:"crypto_wallet_id"`
	CryptoAssetID      string          `json:"crypto_asset_id"`
	CryptoAddressID    string          `json:"crypto_address_id"`
	Direction          string          `json:"direction"`
	Address            string          `json:"address"`
	AddressTag         string          `json:"address_tag"`
	AmountBaseUnits    string          `json:"amount_base_units"`
	FeeBaseUnits       string          `json:"fee_base_units"`
	Provider           string          `json:"provider"`
	ProviderReference  string          `json:"provider_reference"`
	TxHash             string          `json:"tx_hash"`
	Confirmations      int             `json:"confirmations"`
	TravelRuleRequired bool            `json:"travel_rule_required"`
	Metadata           json.RawMessage `json:"metadata"`
}

type chainTransactionStatusRequest struct {
	Status            string          `json:"status"`
	TxHash            string          `json:"tx_hash"`
	Confirmations     int             `json:"confirmations"`
	FailureReason     string          `json:"failure_reason"`
	ProviderReference string          `json:"provider_reference"`
	Metadata          json.RawMessage `json:"metadata"`
}

func NewHandler(repo *Repository, auditRepo *audit.Repository, custodyProvider custody.Provider, analyticsProvider blockchainanalytics.Provider, compliance interface {
	RequireClear(ctx context.Context, userID string) error
}, log *slog.Logger) *Handler {
	if analyticsProvider == nil {
		analyticsProvider = blockchainanalytics.NewLocalProvider()
	}
	return &Handler{repo: repo, audit: auditRepo, custody: custodyProvider, analytics: analyticsProvider, compliance: compliance, log: log}
}

func (h *Handler) ListAssets(w http.ResponseWriter, r *http.Request) {
	assets, err := h.repo.ListAssets(r.Context())
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"crypto_assets": assets})
}

func (h *Handler) CreateWallet(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	if err := h.requireComplianceClear(r.Context(), claims.Subject); err != nil {
		respond.Error(w, err)
		return
	}

	var req createWalletRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		respond.Error(w, fmt.Errorf("%w: name is required", domain.ErrValidation))
		return
	}
	if len(name) > 80 {
		respond.Error(w, fmt.Errorf("%w: name must be 80 characters or fewer", domain.ErrValidation))
		return
	}

	created, err := h.custody.CreateWallet(r.Context(), custody.CreateWalletParams{UserID: claims.Subject, Name: name})
	if err != nil {
		respond.Error(w, err)
		return
	}

	wallet, err := h.repo.CreateWallet(r.Context(), CreateWalletParams{
		UserID:           claims.Subject,
		Name:             name,
		CustodyProvider:  created.Provider,
		ExternalWalletID: created.ExternalWalletID,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	h.recordAudit(r, claims.Subject, "crypto_wallet.created", "crypto_wallet", wallet.ID, map[string]string{"provider": wallet.CustodyProvider})
	respond.Created(w, wallet)
}

func (h *Handler) ListWallets(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	wallets, err := h.repo.ListWallets(r.Context(), claims.Subject)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"crypto_wallets": wallets})
}

func (h *Handler) GetWallet(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	walletID := r.PathValue("id")
	if err := domain.ValidateUUID("id", walletID); err != nil {
		respond.Error(w, err)
		return
	}

	wallet, err := h.repo.FindOwned(r.Context(), claims.Subject, walletID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, wallet)
}

func (h *Handler) AddAsset(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	if err := h.requireComplianceClear(r.Context(), claims.Subject); err != nil {
		respond.Error(w, err)
		return
	}

	walletID := r.PathValue("id")
	if err := domain.ValidateUUID("id", walletID); err != nil {
		respond.Error(w, err)
		return
	}

	var req addAssetRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if err := domain.ValidateUUID("crypto_asset_id", req.CryptoAssetID); err != nil {
		respond.Error(w, err)
		return
	}

	balance, err := h.repo.AddAsset(r.Context(), AddAssetParams{
		UserID:         claims.Subject,
		CryptoWalletID: walletID,
		CryptoAssetID:  req.CryptoAssetID,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	h.recordAudit(r, claims.Subject, "crypto_wallet.asset_added", "crypto_wallet", walletID, map[string]string{"crypto_asset_id": req.CryptoAssetID})
	respond.Created(w, balance)
}

func (h *Handler) ListBalances(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	walletID := r.PathValue("id")
	if err := domain.ValidateUUID("id", walletID); err != nil {
		respond.Error(w, err)
		return
	}

	balances, err := h.repo.ListBalances(r.Context(), claims.Subject, walletID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"balances": balances})
}

func (h *Handler) CreateAddress(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	if err := h.requireComplianceClear(r.Context(), claims.Subject); err != nil {
		respond.Error(w, err)
		return
	}

	walletID := r.PathValue("id")
	if err := domain.ValidateUUID("id", walletID); err != nil {
		respond.Error(w, err)
		return
	}

	var req createAddressRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if err := domain.ValidateUUID("crypto_asset_id", req.CryptoAssetID); err != nil {
		respond.Error(w, err)
		return
	}

	wallet, err := h.repo.FindOwned(r.Context(), claims.Subject, walletID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	if wallet.Status != "active" {
		respond.Error(w, fmt.Errorf("%w: crypto wallet must be active", domain.ErrValidation))
		return
	}

	asset, err := h.repo.FindAsset(r.Context(), req.CryptoAssetID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	if err := h.repo.EnsureStablecoinOperationAllowed(r.Context(), asset.ID, "deposit"); err != nil {
		respond.Error(w, err)
		return
	}

	generated, err := h.custody.CreateDepositAddress(r.Context(), custody.CreateDepositAddressParams{
		ExternalWalletID: wallet.ExternalWalletID,
		Symbol:           asset.Symbol,
		Network:          asset.Network,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	if _, err := h.repo.AddAsset(r.Context(), AddAssetParams{
		UserID:         claims.Subject,
		CryptoWalletID: walletID,
		CryptoAssetID:  asset.ID,
	}); err != nil {
		respond.Error(w, err)
		return
	}

	address, err := h.repo.CreateAddress(r.Context(), CreateAddressParams{
		UserID:         claims.Subject,
		CryptoWalletID: walletID,
		CryptoAssetID:  asset.ID,
		Address:        generated.Address,
		AddressTag:     generated.AddressTag,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	h.recordAudit(r, claims.Subject, "crypto_wallet.address_created", "crypto_address", address.ID, map[string]string{
		"symbol":  address.Symbol,
		"network": address.Network,
	})
	respond.Created(w, address)
}

func (h *Handler) ListAddresses(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	walletID := r.PathValue("id")
	if err := domain.ValidateUUID("id", walletID); err != nil {
		respond.Error(w, err)
		return
	}

	addresses, err := h.repo.ListAddresses(r.Context(), claims.Subject, walletID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"addresses": addresses})
}

func (h *Handler) MyChainTransactions(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	items, err := h.repo.ListChainTransactions(r.Context(), claims.Subject, strings.TrimSpace(r.URL.Query().Get("status")), queryLimit(r, 50))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"crypto_chain_transactions": items})
}

func (h *Handler) AdminCustodyScopeDecisions(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListCustodyScopeDecisions(r.Context(), queryLimit(r, 100))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"custody_scope_decisions": items})
}

func (h *Handler) AdminCreateCustodyScopeDecision(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req custodyScopeDecisionRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	item, err := h.repo.CreateCustodyScopeDecision(r.Context(), claims.Subject, req.ProductScope, req.DecisionStatus, req.RealMovementEnabled, req.JurisdictionScope, req.Rationale, req.EvidenceReference)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.custody_scope_decision.created", "crypto_custody_scope_decision", item.ID, map[string]any{
		"product_scope":         item.ProductScope,
		"decision_status":       item.DecisionStatus,
		"real_movement_enabled": item.RealMovementEnabled,
		"jurisdiction_scope":    item.JurisdictionScope,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminLegalMemos(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListLegalMemos(r.Context(), r.URL.Query().Get("status"), r.URL.Query().Get("jurisdiction"), queryLimit(r, 100))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"legal_memos": items})
}

func (h *Handler) AdminUpsertLegalMemo(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req LegalMemo
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if req.Status == "approved" {
		req.ApprovedByAdminUserID = claims.Subject
	}
	item, err := h.repo.UpsertLegalMemo(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.legal_memo.upserted", "crypto_legal_memo", item.ID, map[string]any{
		"jurisdiction": item.Jurisdiction,
		"activity":     item.Activity,
		"status":       item.Status,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminCustodyProviderConfigs(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListCustodyProviderConfigs(r.Context())
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"custody_provider_configs": items})
}

func (h *Handler) AdminUpsertCustodyProviderConfig(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req CustodyProviderConfig
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	item, err := h.repo.UpsertCustodyProviderConfig(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.custody_provider_config.upserted", "crypto_custody_provider_config", item.ID, map[string]any{
		"provider":              item.Provider,
		"status":                item.Status,
		"real_movement_enabled": item.RealMovementEnabled,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminScreenAddress(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req addressScreeningRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	result, err := h.analytics.ScreenAddress(r.Context(), blockchainanalytics.ScreenAddressParams{
		Address: req.Address,
		Network: req.Network,
		Symbol:  req.Symbol,
		Context: req.Context,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	item, err := h.repo.RecordAddressScreening(r.Context(), AddressScreeningParams{
		UserID:         req.UserID,
		CryptoWalletID: req.CryptoWalletID,
		CryptoAssetID:  req.CryptoAssetID,
		Address:        req.Address,
		AddressTag:     req.AddressTag,
		Network:        req.Network,
		Context:        req.Context,
		Result:         result,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.address_screening.created", "crypto_address_screening", item.ID, map[string]any{
		"decision":   item.Decision,
		"risk_score": item.RiskScore,
		"network":    item.Network,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminAddressScreenings(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListAddressScreenings(r.Context(), r.URL.Query().Get("decision"), queryLimit(r, 100))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"address_screenings": items})
}

func (h *Handler) AdminCreateChainTransaction(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req chainTransactionRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	asset, err := h.repo.FindAsset(r.Context(), req.CryptoAssetID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	result, err := h.analytics.ScreenAddress(r.Context(), blockchainanalytics.ScreenAddressParams{
		Address: req.Address,
		Network: asset.Network,
		Symbol:  asset.Symbol,
		Context: req.Direction,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	screening, err := h.repo.RecordAddressScreening(r.Context(), AddressScreeningParams{
		UserID:         req.UserID,
		CryptoWalletID: req.CryptoWalletID,
		CryptoAssetID:  req.CryptoAssetID,
		Address:        req.Address,
		AddressTag:     req.AddressTag,
		Network:        asset.Network,
		Context:        req.Direction,
		Result:         result,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	item, err := h.repo.CreateChainTransaction(r.Context(), ChainTransactionParams{
		AdminUserID:        claims.Subject,
		UserID:             req.UserID,
		CryptoWalletID:     req.CryptoWalletID,
		CryptoAssetID:      req.CryptoAssetID,
		CryptoAddressID:    req.CryptoAddressID,
		AddressScreeningID: screening.ID,
		Direction:          req.Direction,
		Address:            req.Address,
		AddressTag:         req.AddressTag,
		AmountBaseUnits:    req.AmountBaseUnits,
		FeeBaseUnits:       req.FeeBaseUnits,
		Provider:           req.Provider,
		ProviderReference:  req.ProviderReference,
		TxHash:             req.TxHash,
		Confirmations:      req.Confirmations,
		TravelRuleRequired: req.TravelRuleRequired,
		Metadata:           req.Metadata,
		ScreeningDecision:  screening.Decision,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.chain_transaction.created", "crypto_chain_transaction", item.ID, map[string]any{
		"direction":          item.Direction,
		"status":             item.Status,
		"symbol":             item.Symbol,
		"amount_base_units":  item.AmountBaseUnits,
		"screening_decision": screening.Decision,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminChainTransactions(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListChainTransactions(r.Context(), strings.TrimSpace(r.URL.Query().Get("user_id")), r.URL.Query().Get("status"), queryLimit(r, 100))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"crypto_chain_transactions": items})
}

func (h *Handler) AdminUpdateChainTransactionStatus(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req chainTransactionStatusRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	item, err := h.repo.UpdateChainTransactionStatus(r.Context(), ChainTransactionStatusParams{
		AdminUserID:       claims.Subject,
		TransactionID:     r.PathValue("id"),
		Status:            req.Status,
		TxHash:            req.TxHash,
		Confirmations:     req.Confirmations,
		FailureReason:     req.FailureReason,
		ProviderReference: req.ProviderReference,
		Metadata:          req.Metadata,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.chain_transaction.status_updated", "crypto_chain_transaction", item.ID, map[string]any{
		"status": item.Status,
	})
	respond.JSON(w, http.StatusOK, item)
}

func (h *Handler) AdminTravelRuleTransfers(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListTravelRuleTransfers(r.Context(), r.URL.Query().Get("status"), queryLimit(r, 100))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"travel_rule_transfers": items})
}

func (h *Handler) AdminUpsertTravelRuleTransfer(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req TravelRuleParams
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	req.AdminUserID = claims.Subject
	item, err := h.repo.UpsertTravelRuleTransfer(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.travel_rule_transfer.upserted", "crypto_travel_rule_transfer", item.ID, map[string]any{
		"transaction_id": item.CryptoChainTransactionID,
		"status":         item.Status,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminCustodyResponsibilities(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListCustodyResponsibilities(r.Context())
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"custody_responsibilities": items})
}

func (h *Handler) AdminUpsertCustodyResponsibility(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req CustodyResponsibility
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if req.Status == "approved" {
		req.ApprovedByAdminUserID = claims.Subject
	}
	item, err := h.repo.UpsertCustodyResponsibility(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.custody_responsibility.upserted", "crypto_custody_responsibility", item.ID, map[string]any{
		"provider": item.Provider,
		"status":   item.Status,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminStablecoinIssuerMonitoring(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListStablecoinIssuerMonitoring(r.Context())
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"stablecoin_issuer_monitoring": items})
}

func (h *Handler) AdminUpsertStablecoinIssuerMonitoring(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req StablecoinIssuerMonitoring
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	req.UpdatedByAdminUserID = claims.Subject
	item, err := h.repo.UpsertStablecoinIssuerMonitoring(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.stablecoin_issuer_monitoring.upserted", "stablecoin_issuer_monitoring", item.ID, map[string]any{
		"symbol":      item.Symbol,
		"risk_rating": item.RiskRating,
		"peg_status":  item.PegStatus,
	})
	respond.Created(w, item)
}

func (h *Handler) AdminStablecoinNetworkControls(w http.ResponseWriter, r *http.Request) {
	items, err := h.repo.ListStablecoinNetworkControls(r.Context())
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"stablecoin_network_controls": items})
}

func (h *Handler) AdminUpsertStablecoinNetworkControl(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req StablecoinNetworkControl
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	req.UpdatedByAdminUserID = claims.Subject
	item, err := h.repo.UpsertStablecoinNetworkControl(r.Context(), req)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.crypto.stablecoin_network_control.upserted", "stablecoin_network_control", item.ID, map[string]any{
		"symbol":              item.Symbol,
		"network":             item.Network,
		"deposits_enabled":    item.DepositsEnabled,
		"withdrawals_enabled": item.WithdrawalsEnabled,
	})
	respond.Created(w, item)
}

func (h *Handler) requireComplianceClear(ctx context.Context, userID string) error {
	if h.compliance == nil {
		return nil
	}
	return h.compliance.RequireClear(ctx, userID)
}

func (h *Handler) recordAudit(r *http.Request, actorID, eventType, targetType, targetID string, metadata any) {
	if err := h.audit.Record(r.Context(), audit.Event{
		ActorUserID: &actorID,
		EventType:   eventType,
		TargetType:  targetType,
		TargetID:    targetID,
		Metadata:    metadata,
		RemoteIP:    httputil.RemoteIP(r),
		UserAgent:   r.UserAgent(),
	}); err != nil {
		h.log.Error("audit record failed", "error", err)
	}
}

func queryLimit(r *http.Request, fallback int) int {
	limit := fallback
	if raw := r.URL.Query().Get("limit"); raw != "" {
		if parsed, err := strconv.Atoi(raw); err == nil {
			limit = parsed
		}
	}
	return limit
}
