package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/kami/maven/internal/ipc"
	"github.com/kami/maven/internal/webauthn"
)

// The two fact-writing API routes: POST /api/signal appends a presence
// observation, POST /api/revert voids the latest fact for a key. Neither
// renders a page.

// presenceSignals — the only fact keys /api/signal may write. mavweb is a
// network-facing surface inside wg; an allowlist keeps a compromised caller
// boxed to forging weak presence signals (reachability, multi-source, never
// truth) — it can't write arbitrary facts. ponytail: floor auth (wg-only); a
// per-signal token belongs here if the tunnel ever hosts untrusted devices.
var presenceSignals = map[string]string{
	"desk_active":    "infer:hyprland",
	"page_heartbeat": "infer:heartbeat",
	"wg_handshake":   "infer:wg",
}

// handleSignal ingests one presence signal and writes a fresh fact through
// CoreAPI. The fact's timestamp (now) is all the presence scorer reads; value
// is a marker. Only allowlisted keys are accepted (see presenceSignals).
func handleSignal(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) {
	if r.Method != http.MethodPost {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	if !requireCore(w, core, "presence ingest") {
		return
	}
	key := r.URL.Query().Get("key")
	source, ok := presenceSignals[key]
	if !ok {
		http.Error(w, "unknown signal key", http.StatusBadRequest)
		return
	}
	// kind=env: an observation about the device/surface, NOT a self-fact — a
	// passive signal never writes truth about you (spec), it only feeds
	// presence. confidence 1.0: the reading ("input happened") is certain;
	// presence applies its own per-signal weight/decay on top.
	if _, err := core.WriteFact(r.Context(), ipc.WriteFactReq{
		Ts:         time.Now(),
		Kind:       "env",
		Key:        key,
		Value:      `"active"`,
		Source:     source,
		Confidence: 1.0,
	}); err != nil {
		log.Printf("signal %s: %v", key, err)
		http.Error(w, "write failed", http.StatusBadGateway)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func handleRevert(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI, session *webauthn.PasskeySession, requireStepUp bool) {
	if r.Method != http.MethodPost {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	if !requireCore(w, core, "revert") {
		return
	}
	if !stepUpGate(w, session, requireStepUp) {
		return
	}

	key := strings.TrimSpace(r.FormValue("key"))
	if key == "" {
		http.Error(w, "key required", http.StatusBadRequest)
		return
	}
	newID, err := core.RevertFact(r.Context(), key)
	if err != nil {
		log.Printf("revert %q: %v", key, err)
		if errors.Is(err, ipc.ErrNoFact) {
			http.Error(w, "no fact to revert", http.StatusNotFound)
			return
		}
		http.Error(w, "revert failed", http.StatusBadGateway)
		return
	}
	log.Printf("reverted fact for key=%s, new_id=%d", key, newID)
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"reverted": true, "new_id": newID})
}
