package localapi

import (
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// fork-4 — the controller pushes the offsite restic repo password here so the escrow-create ceremony can
// wrap it under the customer recovery code R (age-under-R, alongside the IdentityBundle). This is TRANSIENT
// custody, not storage: the value is written 0600, NEVER logged (field name only), overwritten on re-push,
// and wiped by the ceremony after a successful escrow. The transport is the already-proven withGuest POST
// channel; the new part is the staging hygiene.

type stageEscrowSecretRequest struct {
	VMID               int    `json:"vmid"`
	ResticRepoPassword string `json:"restic_repo_password"`
}

// handleStageEscrowSecret stages the pushed offsite restic repo password (0600) for escrow-create. The
// secret value never touches a log line or the response.
func (s *Server) handleStageEscrowSecret(w http.ResponseWriter, r *http.Request, vmid int) {
	var req stageEscrowSecretRequest
	if !decodeBody(w, r, &req) {
		return
	}
	if !s.scopedFromBody(w, req.VMID, vmid, r.URL.Path) {
		return
	}
	pw := strings.TrimSpace(req.ResticRepoPassword)
	if pw == "" {
		writeErr(w, http.StatusBadRequest, "restic_repo_password is required")
		return
	}
	path := s.escrowStagePath
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		s.logger.Error("local-api: escrow stage dir", "vmid", vmid, "err", err)
		writeErr(w, http.StatusInternalServerError, "could not stage the secret")
		return
	}
	// Write via a 0600 temp + rename so a re-push is atomic and no partial file is ever readable.
	tmp := path + ".tmp"
	if err := os.WriteFile(tmp, []byte(pw), 0o600); err != nil {
		s.logger.Error("local-api: escrow stage write", "vmid", vmid, "err", err) // err carries no secret
		writeErr(w, http.StatusInternalServerError, "could not stage the secret")
		return
	}
	if err := os.Rename(tmp, path); err != nil {
		_ = os.Remove(tmp)
		s.logger.Error("local-api: escrow stage rename", "vmid", vmid, "err", err)
		writeErr(w, http.StatusInternalServerError, "could not stage the secret")
		return
	}
	s.logger.Info("local-api: staged offsite restic repo password for escrow (field name only)", "vmid", vmid)
	writeOK(w, map[string]any{"staged": true})
}

// handleWipeStagedEscrowSecret removes the staged offsite secret once its escrow is confirmed (or on
// operator cleanup). Idempotent: an absent file is a clean 200 {removed:false}. Closes the fork-4 hygiene
// gap where a confirm WITHOUT a fresh ceremony (the password was already escrowed) left the staged 0600
// file behind indefinitely.
func (s *Server) handleWipeStagedEscrowSecret(w http.ResponseWriter, r *http.Request, vmid int) {
	path := s.escrowStagePath
	_ = os.Remove(path + ".tmp") // a stale partial from an interrupted stage, if any
	err := os.Remove(path)
	switch {
	case err == nil:
		s.logger.Info("local-api: wiped staged escrow secret", "vmid", vmid)
		writeOK(w, map[string]any{"removed": true})
	case os.IsNotExist(err):
		writeOK(w, map[string]any{"removed": false})
	default:
		s.logger.Error("local-api: wipe staged escrow secret", "vmid", vmid, "err", err)
		writeErr(w, http.StatusInternalServerError, "could not remove the staged secret")
	}
}
