package server

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"strings"

	"github.com/go-chi/chi/v5"



	"git.wntrmute.dev/mc/mcdsl/health"
	"git.wntrmute.dev/mc/metacrypt/internal/audit"
	"git.wntrmute.dev/mc/metacrypt/internal/auth"
	"git.wntrmute.dev/mc/metacrypt/internal/barrier"
	"git.wntrmute.dev/mc/metacrypt/internal/crypto"
	"git.wntrmute.dev/mc/metacrypt/internal/engine"
	"git.wntrmute.dev/mc/metacrypt/internal/engine/ca"
	"git.wntrmute.dev/mc/metacrypt/internal/engine/sshca"
	"git.wntrmute.dev/mc/metacrypt/internal/policy"
	"git.wntrmute.dev/mc/metacrypt/internal/seal"
)

func (s *Server) registerRoutes(r chi.Router) {
	// Health check (database ping, no auth required).
	r.Get("/healthz", health.Handler(s.database))

	// REST API routes — web UI served by metacrypt-web.
	r.Get("/v1/status", s.handleStatus)
	r.Post("/v1/init", s.handleInit)
	r.Post("/v1/unseal", s.handleUnseal)
	r.Post("/v1/seal", s.requireAdmin(s.handleSeal))

	r.Post("/v1/auth/login", s.handleLogin)
	r.Post("/v1/auth/logout", s.requireAuth(s.handleLogout))
	r.Get("/v1/auth/tokeninfo", s.requireAuth(s.handleTokenInfo))

	r.Get("/v1/engine/mounts", s.requireAuth(s.handleEngineMounts))
	r.Post("/v1/engine/mount", s.requireAdmin(s.handleEngineMount))
	r.Post("/v1/engine/unmount", s.requireAdmin(s.handleEngineUnmount))
	r.Post("/v1/engine/request", s.requireAuth(s.handleEngineRequest))

	// CA certificate routes (auth required).
	r.Get("/v1/ca/{mount}/cert/{serial}", s.requireAuth(s.handleGetCert))
	r.Post("/v1/ca/{mount}/cert/{serial}/revoke", s.requireAdmin(s.handleRevokeCert))
	r.Delete("/v1/ca/{mount}/cert/{serial}", s.requireAdmin(s.handleDeleteCert))

	// Public SSH CA routes (no auth required, but must be unsealed).
	r.Get("/v1/sshca/{mount}/ca", s.requireUnseal(s.handleSSHCAPubkey))
	r.Get("/v1/sshca/{mount}/krl", s.requireUnseal(s.handleSSHCAKRL))

	// SSH CA auth-required routes.
	r.Post("/v1/sshca/{mount}/sign-host", s.requireAuth(s.handleSSHCASignHost))
	r.Post("/v1/sshca/{mount}/sign-user", s.requireAuth(s.handleSSHCASignUser))
	r.Post("/v1/sshca/{mount}/profiles", s.requireAdmin(s.handleSSHCACreateProfile))
	r.Put("/v1/sshca/{mount}/profiles/{name}", s.requireAdmin(s.handleSSHCAUpdateProfile))
	r.Get("/v1/sshca/{mount}/profiles/{name}", s.requireAuth(s.handleSSHCAGetProfile))
	r.Get("/v1/sshca/{mount}/profiles", s.requireAuth(s.handleSSHCAListProfiles))
	r.Delete("/v1/sshca/{mount}/profiles/{name}", s.requireAdmin(s.handleSSHCADeleteProfile))
	r.Get("/v1/sshca/{mount}/cert/{serial}", s.requireAuth(s.handleSSHCAGetCert))
	r.Get("/v1/sshca/{mount}/certs", s.requireAuth(s.handleSSHCAListCerts))
	r.Post("/v1/sshca/{mount}/cert/{serial}/revoke", s.requireAdmin(s.handleSSHCARevokeCert))
	r.Delete("/v1/sshca/{mount}/cert/{serial}", s.requireAdmin(s.handleSSHCADeleteCert))

	// Public PKI routes (no auth required, but must be unsealed).
	r.Get("/v1/pki/{mount}/ca", s.requireUnseal(s.handlePKIRoot))
	r.Get("/v1/pki/{mount}/ca/chain", s.requireUnseal(s.handlePKIChain))
	r.Get("/v1/pki/{mount}/issuer/{name}", s.requireUnseal(s.handlePKIIssuer))
	r.Get("/v1/pki/{mount}/issuer/{name}/crl", s.requireUnseal(s.handlePKICRL))

	r.Get("/v1/barrier/keys", s.requireAdmin(s.handleBarrierKeys))
	r.Post("/v1/barrier/rotate-mek", s.requireAdmin(s.handleRotateMEK))
	r.Post("/v1/barrier/rotate-key", s.requireAdmin(s.handleRotateKey))
	r.Post("/v1/barrier/migrate", s.requireAdmin(s.handleBarrierMigrate))

	// User-to-user encryption routes (auth required).
	r.Post("/v1/user/{mount}/register", s.requireAuth(s.handleUserRegister))
	r.Post("/v1/user/{mount}/provision", s.requireAdmin(s.handleUserProvision))
	r.Get("/v1/user/{mount}/keys", s.requireAuth(s.handleUserListUsers))
	r.Get("/v1/user/{mount}/keys/{username}", s.requireAuth(s.handleUserGetPublicKey))
	r.Delete("/v1/user/{mount}/keys/{username}", s.requireAdmin(s.handleUserDeleteUser))
	r.Post("/v1/user/{mount}/encrypt", s.requireAuth(s.handleUserEncrypt))
	r.Post("/v1/user/{mount}/decrypt", s.requireAuth(s.handleUserDecrypt))
	r.Post("/v1/user/{mount}/re-encrypt", s.requireAuth(s.handleUserReEncrypt))
	r.Post("/v1/user/{mount}/rotate", s.requireAuth(s.handleUserRotateKey))

	r.HandleFunc("/v1/policy/rules", s.requireAuth(s.handlePolicyRules))
	r.HandleFunc("/v1/policy/rule", s.requireAuth(s.handlePolicyRule))

	// Transit engine routes.
	r.Post("/v1/transit/{mount}/keys", s.requireAdmin(s.handleTransitCreateKey))
	r.Get("/v1/transit/{mount}/keys", s.requireAuth(s.handleTransitListKeys))
	r.Get("/v1/transit/{mount}/keys/{name}", s.requireAuth(s.handleTransitGetKey))
	r.Delete("/v1/transit/{mount}/keys/{name}", s.requireAdmin(s.handleTransitDeleteKey))
	r.Post("/v1/transit/{mount}/keys/{name}/rotate", s.requireAdmin(s.handleTransitRotateKey))
	r.Patch("/v1/transit/{mount}/keys/{name}/config", s.requireAdmin(s.handleTransitUpdateKeyConfig))
	r.Post("/v1/transit/{mount}/keys/{name}/trim", s.requireAdmin(s.handleTransitTrimKey))
	r.Post("/v1/transit/{mount}/encrypt/{key}", s.requireAuth(s.handleTransitEncrypt))
	r.Post("/v1/transit/{mount}/decrypt/{key}", s.requireAuth(s.handleTransitDecrypt))
	r.Post("/v1/transit/{mount}/rewrap/{key}", s.requireAuth(s.handleTransitRewrap))
	r.Post("/v1/transit/{mount}/batch/encrypt/{key}", s.requireAuth(s.handleTransitBatchEncrypt))
	r.Post("/v1/transit/{mount}/batch/decrypt/{key}", s.requireAuth(s.handleTransitBatchDecrypt))
	r.Post("/v1/transit/{mount}/batch/rewrap/{key}", s.requireAuth(s.handleTransitBatchRewrap))
	r.Post("/v1/transit/{mount}/sign/{key}", s.requireAuth(s.handleTransitSign))
	r.Post("/v1/transit/{mount}/verify/{key}", s.requireAuth(s.handleTransitVerify))
	r.Post("/v1/transit/{mount}/hmac/{key}", s.requireAuth(s.handleTransitHmac))
	r.Get("/v1/transit/{mount}/keys/{name}/public-key", s.requireAuth(s.handleTransitGetPublicKey))

	s.registerACMERoutes(r)
}

// --- API Handlers ---

func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"state": s.seal.State().String(),
	})
}

func (s *Server) handleInit(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Password string `json:"password"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Password == "" {
		http.Error(w, `{"error":"password is required"}`, http.StatusBadRequest)
		return
	}

	params := crypto.Argon2Params{
		Time:    s.cfg.Seal.Argon2Time,
		Memory:  s.cfg.Seal.Argon2Memory,
		Threads: s.cfg.Seal.Argon2Threads,
	}
	if err := s.seal.Initialize(r.Context(), []byte(req.Password), params); err != nil {
		if errors.Is(err, seal.ErrAlreadyInitialized) {
			http.Error(w, `{"error":"already initialized"}`, http.StatusConflict)
			return
		}
		s.logger.Error("init failed", "error", err)
		http.Error(w, `{"error":"initialization failed"}`, http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"state": s.seal.State().String(),
	})
}

func (s *Server) handleUnseal(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Password string `json:"password"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}

	if err := s.seal.Unseal([]byte(req.Password)); err != nil {
		if errors.Is(err, seal.ErrNotInitialized) {
			s.logger.Warn("unseal attempt on uninitialized service", "remote_addr", r.RemoteAddr)
			http.Error(w, `{"error":"not initialized"}`, http.StatusPreconditionFailed)
		} else if errors.Is(err, seal.ErrInvalidPassword) {
			s.logger.Warn("unseal attempt with invalid password", "remote_addr", r.RemoteAddr)
			http.Error(w, `{"error":"invalid password"}`, http.StatusUnauthorized)
		} else if errors.Is(err, seal.ErrRateLimited) {
			s.logger.Warn("unseal attempt rate limited", "remote_addr", r.RemoteAddr)
			http.Error(w, `{"error":"too many attempts, try again later"}`, http.StatusTooManyRequests)
		} else if errors.Is(err, seal.ErrNotSealed) {
			s.logger.Warn("unseal attempt on already-unsealed service", "remote_addr", r.RemoteAddr)
			http.Error(w, `{"error":"already unsealed"}`, http.StatusConflict)
		} else {
			s.logger.Error("unseal failed", "remote_addr", r.RemoteAddr, "error", err)
			http.Error(w, `{"error":"unseal failed"}`, http.StatusInternalServerError)
		}
		return
	}

	if err := s.engines.UnsealAll(r.Context()); err != nil {
		s.logger.Error("engine unseal failed", "error", err)
		http.Error(w, `{"error":"engine unseal failed"}`, http.StatusInternalServerError)
		return
	}

	s.logger.Info("service unsealed", "remote_addr", r.RemoteAddr)
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"state": s.seal.State().String(),
	})
}

func (s *Server) handleSeal(w http.ResponseWriter, r *http.Request) {
	if err := s.engines.SealAll(); err != nil {
		s.logger.Error("seal engines failed", "error", err)
	}

	if err := s.seal.Seal(); err != nil {
		s.logger.Error("seal failed", "error", err)
		http.Error(w, `{"error":"seal failed"}`, http.StatusInternalServerError)
		return
	}

	s.auth.ClearCache()
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"state": s.seal.State().String(),
	})
}

func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
	if s.seal.State() != seal.StateUnsealed {
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}

	var req struct {
		Username string `json:"username"`
		Password string `json:"password"`
		TOTPCode string `json:"totp_code"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}

	token, expiresAt, err := s.auth.Login(req.Username, req.Password, req.TOTPCode)
	if err != nil {
		http.Error(w, `{"error":"invalid credentials"}`, http.StatusUnauthorized)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"token":      token,
		"expires_at": expiresAt,
	})
}

func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
	token := extractToken(r)
	_ = auth.Logout(s.auth, token)

	// Clear cookie.
	http.SetCookie(w, &http.Cookie{
		Name:     "metacrypt_token",
		Value:    "",
		Path:     "/",
		MaxAge:   -1,
		HttpOnly: true,
		Secure:   true,
		SameSite: http.SameSiteStrictMode,
	})

	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

func (s *Server) handleTokenInfo(w http.ResponseWriter, r *http.Request) {
	info := TokenInfoFromContext(r.Context())
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"username": info.Username,
		"roles":    info.Roles,
		"is_admin": info.IsAdmin,
	})
}

func (s *Server) handleEngineMounts(w http.ResponseWriter, r *http.Request) {
	mounts := s.engines.ListMounts()
	writeJSON(w, http.StatusOK, mounts)
}

func (s *Server) handleEngineMount(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Config map[string]interface{} `json:"config"`
		Name   string                 `json:"name"`
		Type   string                 `json:"type"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Name == "" || req.Type == "" {
		http.Error(w, `{"error":"name and type are required"}`, http.StatusBadRequest)
		return
	}

	// Inject external_url into CA engine config if available and not already set.
	if req.Config == nil {
		req.Config = make(map[string]interface{})
	}
	if _, ok := req.Config["external_url"]; !ok && s.cfg.Server.ExternalURL != "" {
		req.Config["external_url"] = s.cfg.Server.ExternalURL
	}

	if err := s.engines.Mount(r.Context(), req.Name, engine.EngineType(req.Type), req.Config); err != nil {
		s.logger.Error("mount engine", "name", req.Name, "type", req.Type, "error", err)
		writeJSONError(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

func (s *Server) handleEngineUnmount(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Name string `json:"name"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if err := s.engines.Unmount(r.Context(), req.Name); err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

// adminOnlyOperations lists engine operations that require admin role.
// This enforces the same gates as the typed REST routes, ensuring the
// generic endpoint cannot bypass admin requirements.
var adminOnlyOperations = map[string]bool{
	// CA engine.
	"import-root":   true,
	"create-issuer": true,
	"delete-issuer": true,
	"revoke-cert":   true,
	"delete-cert":   true,
	// Transit engine.
	"create-key":        true,
	"delete-key":        true,
	"rotate-key":        true,
	"update-key-config": true,
	"trim-key":          true,
	// SSH CA engine.
	"create-profile": true,
	"update-profile": true,
	"delete-profile": true,
	// User engine.
	"provision":   true,
	"delete-user": true,
}

// adminExemptOperations lists engineType:operation pairs that are exempt from
// the unqualified admin-only check (e.g. user:rotate-key is user-self, not admin).
var adminExemptOperations = map[string]bool{
	"user:rotate-key": true,
}

func (s *Server) handleEngineRequest(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Data      map[string]interface{} `json:"data"`
		Mount     string                 `json:"mount"`
		Operation string                 `json:"operation"`
		Path      string                 `json:"path"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Mount == "" || req.Operation == "" {
		http.Error(w, `{"error":"mount and operation are required"}`, http.StatusBadRequest)
		return
	}

	info := TokenInfoFromContext(r.Context())

	// Resolve engine type from mount.
	mount, err := s.engines.GetMount(req.Mount)

	// Enforce admin requirement for operations that are admin-only.
	// Check exemptions for engine-specific overrides (e.g. user:rotate-key is user-self).
	if adminOnlyOperations[req.Operation] && !info.IsAdmin {
		exempt := err == nil && adminExemptOperations[string(mount.Type)+":"+req.Operation]
		if !exempt {
			http.Error(w, `{"error":"forbidden: admin required"}`, http.StatusForbidden)
			return
		}
	}

	if err != nil {
		http.Error(w, `{"error":"mount not found"}`, http.StatusNotFound)
		return
	}

	// Evaluate policy before dispatching to the engine.
	policyReq := &policy.Request{
		Username: info.Username,
		Roles:    info.Roles,
		Resource: "engine/" + req.Mount + "/" + req.Operation,
		Action:   operationAction(req.Operation),
	}
	effect, err := s.policy.Evaluate(r.Context(), policyReq)
	if err != nil {
		s.logger.Error("policy evaluation failed", "error", err)
		http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
		return
	}
	if effect != policy.EffectAllow {
		http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
		return
	}

	policyChecker := func(resource, action string) (string, bool) {
		pReq := &policy.Request{
			Username: info.Username,
			Roles:    info.Roles,
			Resource: resource,
			Action:   action,
		}
		eff, matched, pErr := s.policy.Match(r.Context(), pReq)
		if pErr != nil {
			return string(policy.EffectDeny), false
		}
		return string(eff), matched
	}

	engReq := &engine.Request{
		Operation: req.Operation,
		Path:      req.Path,
		Data:      req.Data,
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: policyChecker,
	}

	resp, err := s.engines.HandleRequest(r.Context(), req.Mount, engReq)
	if err != nil {
		status := http.StatusInternalServerError
		switch {
		case errors.Is(err, engine.ErrMountNotFound):
			status = http.StatusNotFound
		case errors.Is(err, ca.ErrIdentifierInUse):
			status = http.StatusConflict
		case strings.Contains(err.Error(), "forbidden"):
			status = http.StatusForbidden
		case strings.Contains(err.Error(), "authentication required"):
			status = http.StatusUnauthorized
		case strings.Contains(err.Error(), "not found"):
			status = http.StatusNotFound
		}
		outcome := "error"
		if status == http.StatusForbidden || status == http.StatusUnauthorized {
			outcome = "denied"
		}
		s.auditOp(r, info, req.Operation, "", req.Mount, outcome, nil, err)
		writeJSONError(w, err.Error(), status)
		return
	}

	s.auditOp(r, info, req.Operation, "", req.Mount, "success", nil, nil)
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handlePolicyRules(w http.ResponseWriter, r *http.Request) {
	info := TokenInfoFromContext(r.Context())
	switch r.Method {
	case http.MethodGet:
		if !info.IsAdmin {
			http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
			return
		}
		rules, err := s.policy.ListRules(r.Context())
		if err != nil {
			s.logger.Error("list policies", "error", err)
			http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
			return
		}
		if rules == nil {
			rules = []policy.Rule{}
		}
		writeJSON(w, http.StatusOK, rules)
	case http.MethodPost:
		if !info.IsAdmin {
			http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
			return
		}
		var rule policy.Rule
		if err := readJSON(r, &rule); err != nil {
			http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
			return
		}
		if rule.ID == "" {
			http.Error(w, `{"error":"id is required"}`, http.StatusBadRequest)
			return
		}
		if err := s.policy.CreateRule(r.Context(), &rule); err != nil {
			s.logger.Error("create policy", "error", err)
			http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
			return
		}
		writeJSON(w, http.StatusCreated, rule)
	default:
		http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
	}
}

func (s *Server) handlePolicyRule(w http.ResponseWriter, r *http.Request) {
	info := TokenInfoFromContext(r.Context())
	if !info.IsAdmin {
		http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
		return
	}

	id := r.URL.Query().Get("id")
	if id == "" {
		http.Error(w, `{"error":"id parameter required"}`, http.StatusBadRequest)
		return
	}

	switch r.Method {
	case http.MethodGet:
		rule, err := s.policy.GetRule(r.Context(), id)
		if err != nil {
			http.Error(w, `{"error":"not found"}`, http.StatusNotFound)
			return
		}
		writeJSON(w, http.StatusOK, rule)
	case http.MethodDelete:
		if err := s.policy.DeleteRule(r.Context(), id); err != nil {
			http.Error(w, `{"error":"not found"}`, http.StatusNotFound)
			return
		}
		writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
	default:
		http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
	}
}

// --- Barrier Key Management Handlers ---

func (s *Server) handleBarrierKeys(w http.ResponseWriter, r *http.Request) {
	keys, err := s.seal.Barrier().ListKeys(r.Context())
	if err != nil {
		s.logger.Error("list barrier keys", "error", err)
		http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
		return
	}
	if keys == nil {
		keys = []barrier.KeyInfo{}
	}
	writeJSON(w, http.StatusOK, keys)
}

func (s *Server) handleRotateMEK(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Password string `json:"password"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Password == "" {
		http.Error(w, `{"error":"password is required"}`, http.StatusBadRequest)
		return
	}

	if err := s.seal.RotateMEK(r.Context(), []byte(req.Password)); err != nil {
		if errors.Is(err, seal.ErrInvalidPassword) {
			http.Error(w, `{"error":"invalid password"}`, http.StatusUnauthorized)
			return
		}
		if errors.Is(err, seal.ErrSealed) {
			http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
			return
		}
		s.logger.Error("rotate MEK", "error", err)
		http.Error(w, `{"error":"rotation failed"}`, http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

func (s *Server) handleRotateKey(w http.ResponseWriter, r *http.Request) {
	var req struct {
		KeyID string `json:"key_id"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.KeyID == "" {
		http.Error(w, `{"error":"key_id is required"}`, http.StatusBadRequest)
		return
	}

	if err := s.seal.Barrier().RotateKey(r.Context(), req.KeyID); err != nil {
		if errors.Is(err, barrier.ErrKeyNotFound) {
			http.Error(w, `{"error":"key not found"}`, http.StatusNotFound)
			return
		}
		s.logger.Error("rotate key", "key_id", req.KeyID, "error", err)
		http.Error(w, `{"error":"rotation failed"}`, http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

func (s *Server) handleBarrierMigrate(w http.ResponseWriter, r *http.Request) {
	migrated, err := s.seal.Barrier().MigrateToV2(r.Context())
	if err != nil {
		s.logger.Error("barrier migration", "error", err)
		http.Error(w, `{"error":"migration failed"}`, http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"migrated": migrated,
	})
}

// --- CA Certificate Handlers ---

func (s *Server) handleGetCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")

	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "get-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		if errors.Is(err, ca.ErrCertNotFound) {
			http.Error(w, `{"error":"certificate not found"}`, http.StatusNotFound)
			return
		}
		writeJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleRevokeCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")

	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "revoke-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		if errors.Is(err, ca.ErrCertNotFound) {
			http.Error(w, `{"error":"certificate not found"}`, http.StatusNotFound)
			return
		}
		if errors.Is(err, ca.ErrForbidden) {
			http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
			return
		}
		writeJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleDeleteCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")

	info := TokenInfoFromContext(r.Context())
	_, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "delete-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		if errors.Is(err, ca.ErrCertNotFound) {
			http.Error(w, `{"error":"certificate not found"}`, http.StatusNotFound)
			return
		}
		if errors.Is(err, ca.ErrForbidden) {
			http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
			return
		}
		writeJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusNoContent, nil)
}

// --- Public PKI Handlers ---

func (s *Server) handlePKIRoot(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	caEng, err := s.getCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}

	certPEM, err := caEng.GetRootCertPEM()
	if err != nil {
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}

	w.Header().Set("Content-Type", "application/x-pem-file")
	_, _ = w.Write(certPEM) //nolint:gosec
}

func (s *Server) handlePKIChain(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	issuerName := r.URL.Query().Get("issuer")
	if issuerName == "" {
		http.Error(w, `{"error":"issuer query parameter required"}`, http.StatusBadRequest)
		return
	}

	caEng, err := s.getCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}

	chainPEM, err := caEng.GetChainPEM(issuerName)
	if err != nil {
		if errors.Is(err, ca.ErrIssuerNotFound) {
			http.Error(w, `{"error":"issuer not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}

	w.Header().Set("Content-Type", "application/x-pem-file")
	_, _ = w.Write(chainPEM) //nolint:gosec
}

func (s *Server) handlePKIIssuer(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	issuerName := chi.URLParam(r, "name")

	caEng, err := s.getCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}

	certPEM, err := caEng.GetIssuerCertPEM(issuerName)
	if err != nil {
		if errors.Is(err, ca.ErrIssuerNotFound) {
			http.Error(w, `{"error":"issuer not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}

	w.Header().Set("Content-Type", "application/x-pem-file")
	_, _ = w.Write(certPEM) //nolint:gosec
}

func (s *Server) handlePKICRL(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	issuerName := chi.URLParam(r, "name")

	caEng, err := s.getCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}

	crlDER, err := caEng.GetCRLDER(r.Context(), issuerName)
	if err != nil {
		if errors.Is(err, ca.ErrIssuerNotFound) {
			http.Error(w, `{"error":"issuer not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}

	w.Header().Set("Content-Type", "application/pkix-crl")
	_, _ = w.Write(crlDER) //nolint:gosec
}

func (s *Server) getCAEngine(mountName string) (*ca.CAEngine, error) {
	mount, err := s.engines.GetMount(mountName)
	if err != nil {
		return nil, err
	}
	if mount.Type != engine.EngineTypeCA {
		return nil, errors.New("mount is not a CA engine")
	}
	caEng, ok := mount.Engine.(*ca.CAEngine)
	if !ok {
		return nil, errors.New("mount is not a CA engine")
	}
	return caEng, nil
}

// --- Transit Engine Handlers ---

func (s *Server) transitRequest(w http.ResponseWriter, r *http.Request, mount, operation string, data map[string]interface{}) {
	info := TokenInfoFromContext(r.Context())

	policyChecker := func(resource, action string) (string, bool) {
		pReq := &policy.Request{
			Username: info.Username,
			Roles:    info.Roles,
			Resource: resource,
			Action:   action,
		}
		eff, matched, pErr := s.policy.Match(r.Context(), pReq)
		if pErr != nil {
			return string(policy.EffectDeny), false
		}
		return string(eff), matched
	}

	resp, err := s.engines.HandleRequest(r.Context(), mount, &engine.Request{
		Operation:   operation,
		CallerInfo:  &engine.CallerInfo{Username: info.Username, Roles: info.Roles, IsAdmin: info.IsAdmin},
		CheckPolicy: policyChecker,
		Data:        data,
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleTransitCreateKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	var req struct {
		Name string `json:"name"`
		Type string `json:"type"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "create-key", map[string]interface{}{"name": req.Name, "type": req.Type})
}

func (s *Server) handleTransitDeleteKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	s.transitRequest(w, r, mount, "delete-key", map[string]interface{}{"name": name})
}

func (s *Server) handleTransitGetKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	s.transitRequest(w, r, mount, "get-key", map[string]interface{}{"name": name})
}

func (s *Server) handleTransitListKeys(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	s.transitRequest(w, r, mount, "list-keys", nil)
}

func (s *Server) handleTransitRotateKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	s.transitRequest(w, r, mount, "rotate-key", map[string]interface{}{"name": name})
}

func (s *Server) handleTransitUpdateKeyConfig(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	var req struct {
		MinDecryptionVersion *float64 `json:"min_decryption_version"`
		AllowDeletion        *bool    `json:"allow_deletion"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	data := map[string]interface{}{"name": name}
	if req.MinDecryptionVersion != nil {
		data["min_decryption_version"] = *req.MinDecryptionVersion
	}
	if req.AllowDeletion != nil {
		data["allow_deletion"] = *req.AllowDeletion
	}
	s.transitRequest(w, r, mount, "update-key-config", data)
}

func (s *Server) handleTransitTrimKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	s.transitRequest(w, r, mount, "trim-key", map[string]interface{}{"name": name})
}

func (s *Server) handleTransitEncrypt(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Plaintext string `json:"plaintext"`
		Context   string `json:"context"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	data := map[string]interface{}{"key": key, "plaintext": req.Plaintext}
	if req.Context != "" {
		data["context"] = req.Context
	}
	s.transitRequest(w, r, mount, "encrypt", data)
}

func (s *Server) handleTransitDecrypt(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Ciphertext string `json:"ciphertext"`
		Context    string `json:"context"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	data := map[string]interface{}{"key": key, "ciphertext": req.Ciphertext}
	if req.Context != "" {
		data["context"] = req.Context
	}
	s.transitRequest(w, r, mount, "decrypt", data)
}

func (s *Server) handleTransitRewrap(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Ciphertext string `json:"ciphertext"`
		Context    string `json:"context"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	data := map[string]interface{}{"key": key, "ciphertext": req.Ciphertext}
	if req.Context != "" {
		data["context"] = req.Context
	}
	s.transitRequest(w, r, mount, "rewrap", data)
}

func (s *Server) handleTransitBatchEncrypt(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Items []interface{} `json:"items"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "batch-encrypt", map[string]interface{}{"key": key, "items": req.Items})
}

func (s *Server) handleTransitBatchDecrypt(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Items []interface{} `json:"items"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "batch-decrypt", map[string]interface{}{"key": key, "items": req.Items})
}

func (s *Server) handleTransitBatchRewrap(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Items []interface{} `json:"items"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "batch-rewrap", map[string]interface{}{"key": key, "items": req.Items})
}

func (s *Server) handleTransitSign(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Input string `json:"input"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "sign", map[string]interface{}{"key": key, "input": req.Input})
}

func (s *Server) handleTransitVerify(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Input     string `json:"input"`
		Signature string `json:"signature"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	s.transitRequest(w, r, mount, "verify", map[string]interface{}{"key": key, "input": req.Input, "signature": req.Signature})
}

func (s *Server) handleTransitHmac(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	key := chi.URLParam(r, "key")
	var req struct {
		Input string `json:"input"`
		HMAC  string `json:"hmac"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	data := map[string]interface{}{"key": key, "input": req.Input}
	if req.HMAC != "" {
		data["hmac"] = req.HMAC
	}
	s.transitRequest(w, r, mount, "hmac", data)
}

func (s *Server) handleTransitGetPublicKey(w http.ResponseWriter, r *http.Request) {
	mount := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	s.transitRequest(w, r, mount, "get-public-key", map[string]interface{}{"name": name})
}

// --- User-to-User Encryption Handlers ---

func (s *Server) handleUserRegister(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "register",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserProvision(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		Username string `json:"username"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Username == "" {
		http.Error(w, `{"error":"username is required"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "provision",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
		Data:         map[string]interface{}{"username": req.Username},
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserListUsers(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "list-users",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserGetPublicKey(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	username := chi.URLParam(r, "username")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "get-public-key",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
		Data:         map[string]interface{}{"username": username},
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserDeleteUser(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	username := chi.URLParam(r, "username")
	info := TokenInfoFromContext(r.Context())
	_, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "delete-user",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
		Data:         map[string]interface{}{"username": username},
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
}

func (s *Server) handleUserEncrypt(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		Plaintext  string   `json:"plaintext"`
		Metadata   string   `json:"metadata"`
		Recipients []string `json:"recipients"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	recipients := make([]interface{}, len(req.Recipients))
	for i, r := range req.Recipients {
		recipients[i] = r
	}
	info := TokenInfoFromContext(r.Context())

	policyChecker := func(resource, action string) (string, bool) {
		pReq := &policy.Request{
			Username: info.Username,
			Roles:    info.Roles,
			Resource: resource,
			Action:   action,
		}
		eff, matched, pErr := s.policy.Match(r.Context(), pReq)
		if pErr != nil {
			return string(policy.EffectDeny), false
		}
		return string(eff), matched
	}

	data := map[string]interface{}{
		"plaintext":  req.Plaintext,
		"recipients": recipients,
	}
	if req.Metadata != "" {
		data["metadata"] = req.Metadata
	}
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "encrypt",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: policyChecker,
		Data:        data,
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserDecrypt(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		Envelope string `json:"envelope"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "decrypt",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
		Data:         map[string]interface{}{"envelope": req.Envelope},
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserReEncrypt(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		Envelope string `json:"envelope"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "re-encrypt",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
		Data:         map[string]interface{}{"envelope": req.Envelope},
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleUserRotateKey(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "rotate-key",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

// operationAction maps an engine operation name to a policy action.
func operationAction(op string) string {
	switch op {
	// Read operations.
	case "list-issuers", "list-certs", "get-cert", "get-root", "get-chain", "get-issuer",
		"list-keys", "get-key", "get-public-key", "list-users", "get-profile", "list-profiles",
		"get-ca-pubkey", "get-krl":
		return policy.ActionRead

	// Granular cryptographic operations (including batch variants).
	case "encrypt", "batch-encrypt":
		return policy.ActionEncrypt
	case "decrypt", "batch-decrypt", "rewrap", "batch-rewrap", "re-encrypt":
		return policy.ActionDecrypt
	case "sign", "sign-host", "sign-user":
		return policy.ActionSign
	case "verify":
		return policy.ActionVerify
	case "hmac":
		return policy.ActionHMAC

	// Everything else is a write.
	default:
		return policy.ActionWrite
	}
}

func writeJSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(v)
}

func writeJSONError(w http.ResponseWriter, msg string, status int) {
	writeJSON(w, status, map[string]string{"error": msg})
}

// auditOp logs an audit event for a completed engine operation.
func (s *Server) auditOp(r *http.Request, info *auth.TokenInfo,
	op, engineType, mount, outcome string, detail map[string]interface{}, err error) {
	e := audit.Event{
		Caller:    info.Username,
		Roles:     info.Roles,
		Operation: op,
		Engine:    engineType,
		Mount:     mount,
		Outcome:   outcome,
		Detail:    detail,
	}
	if err != nil {
		e.Error = err.Error()
	}
	s.audit.Log(r.Context(), e)
}

// newPolicyChecker builds a PolicyChecker closure for a caller, used by typed
// REST handlers to pass service-level policy evaluation into the engine.
func (s *Server) newPolicyChecker(r *http.Request, info *auth.TokenInfo) engine.PolicyChecker {
	return func(resource, action string) (string, bool) {
		pReq := &policy.Request{
			Username: info.Username,
			Roles:    info.Roles,
			Resource: resource,
			Action:   action,
		}
		eff, matched, pErr := s.policy.Match(r.Context(), pReq)
		if pErr != nil {
			return string(policy.EffectDeny), false
		}
		return string(eff), matched
	}
}

func readJSON(r *http.Request, v interface{}) error {
	defer func() { _ = r.Body.Close() }()
	body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) // 1MB limit
	if err != nil {
		return err
	}
	return json.Unmarshal(body, v)
}

// --- SSH CA Handlers ---

func (s *Server) getSSHCAEngine(mountName string) (*sshca.SSHCAEngine, error) {
	mount, err := s.engines.GetMount(mountName)
	if err != nil {
		return nil, err
	}
	if mount.Type != engine.EngineTypeSSHCA {
		return nil, errors.New("mount is not an SSH CA engine")
	}
	eng, ok := mount.Engine.(*sshca.SSHCAEngine)
	if !ok {
		return nil, errors.New("mount is not an SSH CA engine")
	}
	return eng, nil
}

func (s *Server) handleSSHCAPubkey(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	eng, err := s.getSSHCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}
	pubKey, err := eng.GetCAPubkey(r.Context())
	if err != nil {
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}
	w.Header().Set("Content-Type", "text/plain")
	_, _ = w.Write(pubKey) //nolint:gosec
}

func (s *Server) handleSSHCAKRL(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	eng, err := s.getSSHCAEngine(mountName)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusNotFound)
		return
	}
	krlData, err := eng.GetKRL()
	if err != nil {
		http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
		return
	}
	w.Header().Set("Content-Type", "application/octet-stream")
	_, _ = w.Write(krlData) //nolint:gosec
}

func (s *Server) handleSSHCASignHost(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		PublicKey string `json:"public_key"`
		Hostname  string `json:"hostname"`
		TTL       string `json:"ttl"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	data := map[string]interface{}{
		"public_key": req.PublicKey,
		"hostname":   req.Hostname,
	}
	if req.TTL != "" {
		data["ttl"] = req.TTL
	}
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "sign-host",
		Data:      data,
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCASignUser(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		PublicKey  string   `json:"public_key"`
		Principals []string `json:"principals"`
		Profile   string   `json:"profile"`
		TTL       string   `json:"ttl"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	data := map[string]interface{}{
		"public_key": req.PublicKey,
	}
	if len(req.Principals) > 0 {
		principals := make([]interface{}, len(req.Principals))
		for i, p := range req.Principals {
			principals[i] = p
		}
		data["principals"] = principals
	}
	if req.Profile != "" {
		data["profile"] = req.Profile
	}
	if req.TTL != "" {
		data["ttl"] = req.TTL
	}

	policyChecker := func(resource, action string) (string, bool) {
		pReq := &policy.Request{
			Username: info.Username,
			Roles:    info.Roles,
			Resource: resource,
			Action:   action,
		}
		eff, matched, pErr := s.policy.Match(r.Context(), pReq)
		if pErr != nil {
			return string(policy.EffectDeny), false
		}
		return string(eff), matched
	}

	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "sign-user",
		Data:      data,
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: policyChecker,
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCACreateProfile(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	var req struct {
		Name              string            `json:"name"`
		CriticalOptions   map[string]string `json:"critical_options"`
		Extensions        map[string]string `json:"extensions"`
		MaxTTL            string            `json:"max_ttl"`
		AllowedPrincipals []string          `json:"allowed_principals"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	data := map[string]interface{}{"name": req.Name}
	if req.CriticalOptions != nil {
		opts := make(map[string]interface{}, len(req.CriticalOptions))
		for k, v := range req.CriticalOptions {
			opts[k] = v
		}
		data["critical_options"] = opts
	}
	if req.Extensions != nil {
		exts := make(map[string]interface{}, len(req.Extensions))
		for k, v := range req.Extensions {
			exts[k] = v
		}
		data["extensions"] = exts
	}
	if req.MaxTTL != "" {
		data["max_ttl"] = req.MaxTTL
	}
	if len(req.AllowedPrincipals) > 0 {
		principals := make([]interface{}, len(req.AllowedPrincipals))
		for i, p := range req.AllowedPrincipals {
			principals[i] = p
		}
		data["allowed_principals"] = principals
	}
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "create-profile",
		Data:      data,
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, resp.Data)
}

func (s *Server) handleSSHCAUpdateProfile(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	var req struct {
		CriticalOptions   map[string]string `json:"critical_options"`
		Extensions        map[string]string `json:"extensions"`
		MaxTTL            string            `json:"max_ttl"`
		AllowedPrincipals []string          `json:"allowed_principals"`
	}
	if err := readJSON(r, &req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	info := TokenInfoFromContext(r.Context())
	data := map[string]interface{}{"name": name}
	if req.CriticalOptions != nil {
		opts := make(map[string]interface{}, len(req.CriticalOptions))
		for k, v := range req.CriticalOptions {
			opts[k] = v
		}
		data["critical_options"] = opts
	}
	if req.Extensions != nil {
		exts := make(map[string]interface{}, len(req.Extensions))
		for k, v := range req.Extensions {
			exts[k] = v
		}
		data["extensions"] = exts
	}
	if req.MaxTTL != "" {
		data["max_ttl"] = req.MaxTTL
	}
	if len(req.AllowedPrincipals) > 0 {
		principals := make([]interface{}, len(req.AllowedPrincipals))
		for i, p := range req.AllowedPrincipals {
			principals[i] = p
		}
		data["allowed_principals"] = principals
	}
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "update-profile",
		Data:      data,
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCAGetProfile(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "get-profile",
		Data:      map[string]interface{}{"name": name},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCAListProfiles(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "list-profiles",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCADeleteProfile(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	name := chi.URLParam(r, "name")
	info := TokenInfoFromContext(r.Context())
	_, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "delete-profile",
		Data:      map[string]interface{}{"name": name},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusNoContent, nil)
}

func (s *Server) handleSSHCAGetCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "get-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCAListCerts(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "list-certs",
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCARevokeCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")
	info := TokenInfoFromContext(r.Context())
	resp, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "revoke-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, resp.Data)
}

func (s *Server) handleSSHCADeleteCert(w http.ResponseWriter, r *http.Request) {
	mountName := chi.URLParam(r, "mount")
	serial := chi.URLParam(r, "serial")
	info := TokenInfoFromContext(r.Context())
	_, err := s.engines.HandleRequest(r.Context(), mountName, &engine.Request{
		Operation: "delete-cert",
		Data:      map[string]interface{}{"serial": serial},
		CallerInfo: &engine.CallerInfo{
			Username: info.Username,
			Roles:    info.Roles,
			IsAdmin:  info.IsAdmin,
		},
		CheckPolicy: s.newPolicyChecker(r, info),
	})
	if err != nil {
		s.writeEngineError(w, err)
		return
	}
	writeJSON(w, http.StatusNoContent, nil)
}

func (s *Server) writeEngineError(w http.ResponseWriter, err error) {
	status := http.StatusInternalServerError
	switch {
	case errors.Is(err, engine.ErrMountNotFound):
		status = http.StatusNotFound
	case errors.Is(err, sshca.ErrCertNotFound):
		status = http.StatusNotFound
	case errors.Is(err, sshca.ErrProfileNotFound):
		status = http.StatusNotFound
	case errors.Is(err, sshca.ErrProfileExists):
		status = http.StatusConflict
	case errors.Is(err, sshca.ErrForbidden):
		status = http.StatusForbidden
	case errors.Is(err, sshca.ErrUnauthorized):
		status = http.StatusUnauthorized
	case strings.Contains(err.Error(), "forbidden"),
		strings.Contains(err.Error(), "not allowed"):
		status = http.StatusForbidden
	case strings.Contains(err.Error(), "authentication required"):
		status = http.StatusUnauthorized
	case strings.Contains(err.Error(), "not found"):
		status = http.StatusNotFound
	case strings.Contains(err.Error(), "unsupported"),
		strings.Contains(err.Error(), "invalid"),
		strings.Contains(err.Error(), "too many"):
		status = http.StatusBadRequest
	}
	writeJSONError(w, err.Error(), status)
}
