// Vault seal/unseal REST handlers for MCIAS.
package server

import (
	"net/http"

	"git.wntrmute.dev/mc/mcias/internal/audit"
	"git.wntrmute.dev/mc/mcias/internal/middleware"
	"git.wntrmute.dev/mc/mcias/internal/model"
	"git.wntrmute.dev/mc/mcias/internal/vault"
)

// unsealRequest is the request body for POST /v1/vault/unseal.
type unsealRequest struct {
	Passphrase string `json:"passphrase"`
}

// handleUnseal accepts a passphrase, derives the master key, decrypts the
// signing key, and unseals the vault. Rate-limited to 3/s burst 5.
//
// Security: The passphrase is never logged. A generic error is returned on
// any failure to prevent information leakage about the vault state.
func (s *Server) handleUnseal(w http.ResponseWriter, r *http.Request) {
	if !s.vault.IsSealed() {
		writeJSON(w, http.StatusOK, map[string]string{"status": "already unsealed"})
		return
	}

	var req unsealRequest
	if !decodeJSON(w, r, &req) {
		return
	}
	if req.Passphrase == "" {
		middleware.WriteError(w, http.StatusBadRequest, "passphrase is required", "bad_request")
		return
	}

	// Derive master key from passphrase.
	masterKey, err := vault.DeriveFromPassphrase(req.Passphrase, s.db)
	if err != nil {
		s.logger.Error("vault unseal: derive key", "error", err)
		middleware.WriteError(w, http.StatusUnauthorized, "unseal failed", "unauthorized")
		return
	}

	// Decrypt the signing key.
	privKey, pubKey, err := vault.DecryptSigningKey(s.db, masterKey)
	if err != nil {
		// Zero derived master key on failure.
		for i := range masterKey {
			masterKey[i] = 0
		}
		s.logger.Error("vault unseal: decrypt signing key", "error", err)
		middleware.WriteError(w, http.StatusUnauthorized, "unseal failed", "unauthorized")
		return
	}

	if err := s.vault.Unseal(masterKey, privKey, pubKey); err != nil {
		s.logger.Error("vault unseal: state transition", "error", err)
		middleware.WriteError(w, http.StatusConflict, "vault is already unsealed", "conflict")
		return
	}

	ip := middleware.ClientIP(r, nil)
	s.writeAudit(r, model.EventVaultUnsealed, nil, nil, audit.JSON("source", "api", "ip", ip))
	s.logger.Info("vault unsealed via API", "ip", ip)

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

// handleSeal seals the vault, zeroing all key material. Admin-only.
//
// Security: The caller's token becomes invalid after sealing because the
// public key needed to validate it is no longer available.
func (s *Server) handleSeal(w http.ResponseWriter, r *http.Request) {
	if s.vault.IsSealed() {
		writeJSON(w, http.StatusOK, map[string]string{"status": "already sealed"})
		return
	}

	claims := middleware.ClaimsFromContext(r.Context())
	var actorID *int64
	if claims != nil {
		acct, err := s.db.GetAccountByUUID(claims.Subject)
		if err == nil {
			actorID = &acct.ID
		}
	}

	s.vault.Seal()

	ip := middleware.ClientIP(r, nil)
	s.writeAudit(r, model.EventVaultSealed, actorID, nil, audit.JSON("source", "api", "ip", ip))
	s.logger.Info("vault sealed via API", "ip", ip)

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

// handleVaultStatus returns the current seal state of the vault.
func (s *Server) handleVaultStatus(w http.ResponseWriter, _ *http.Request) {
	writeJSON(w, http.StatusOK, map[string]bool{"sealed": s.vault.IsSealed()})
}
