package api

// R-1159 MEASUREMENT HARNESS (temporary, not committed): what a holder of the household's retrieval passphrase ALONE
// can reach through the real hub router. Field names and status codes only.

import (
	"context"
	"encoding/json"
	"net/http"
	"sort"
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-hub/internal/offsitekeys"
	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
	"gopkg.in/yaml.v3"
)

type r1159Tpl struct{}

func (r1159Tpl) Template() string { return "hub: {}\n" }

type r1159Keys struct{ calls []string }

func (f *r1159Keys) RegisterKey(_ context.Context, c, _ string) (offsitekeys.InstallResult, error) {
	f.calls = append(f.calls, "RegisterKey")
	return offsitekeys.InstallResult{Fingerprint: "SHA256:x"}, nil
}
func (f *r1159Keys) ConfirmKey(_ context.Context, c, _ string) (int, error) {
	f.calls = append(f.calls, "ConfirmKey")
	return 1, nil
}
func (f *r1159Keys) MoveAside(_ context.Context, c string) (string, error) {
	f.calls = append(f.calls, "MoveAside")
	return "repo.orphaned-x", nil
}
func (f *r1159Keys) OpenWindowFor(_ context.Context, c string, _ int) (offsitekeys.WindowGrant, error) {
	f.calls = append(f.calls, "OpenWindowFor")
	return offsitekeys.WindowGrant{}, nil
}
func (f *r1159Keys) CloseWindowFor(_ context.Context, c string, _ offsitekeys.WindowResult) error {
	f.calls = append(f.calls, "CloseWindowFor")
	return nil
}
func (f *r1159Keys) RequestAbandon(_ context.Context, c, _ string) (offsitekeys.AbandonStatus, error) {
	f.calls = append(f.calls, "RequestAbandon")
	return offsitekeys.AbandonStatus{State: "pending"}, nil
}
func (f *r1159Keys) CancelAbandon(c, _ string) (int, error) {
	f.calls = append(f.calls, "CancelAbandon")
	return 1, nil
}
func (f *r1159Keys) AbandonStatusFor(c string) (offsitekeys.AbandonStatus, error) {
	f.calls = append(f.calls, "AbandonStatusFor")
	return offsitekeys.AbandonStatus{State: "none"}, nil
}

func TestR1159_Measure(t *testing.T) {
	h, st, _ := newTestHandler(t)
	h.templateProvider = r1159Tpl{}
	fk := &r1159Keys{}
	h.SetOffsiteKeyService(fk)
	if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey-1159", RetrievalPassword: "owner-pass"}); err != nil {
		t.Fatal(err)
	}
	if err := st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY-1159"}); err != nil {
		t.Fatal(err)
	}

	// Step 1: the passphrase alone.
	rr := r879Get(h, "/config/c1", "owner-pass")
	var y map[string]any
	_ = yaml.Unmarshal(rr.Body.Bytes(), &y)
	var hubFields []string
	if hm, ok := y["hub"].(map[string]any); ok {
		for k := range hm {
			hubFields = append(hubFields, k)
		}
	}
	sort.Strings(hubFields)
	key := ""
	if hm, ok := y["hub"].(map[string]any); ok {
		key, _ = hm["api_key"].(string)
	}
	t.Logf("GET /config/c1 (passphrase)          -> %d; hub fields: %v; carries the controller key: %v", rr.Code, hubFields, key == "ckey-1159")
	t.Logf("GET /config/c1 (wrong passphrase)    -> %d", r879Get(h, "/config/c1", "nope").Code)
	er := doEnroll(h, "c1", "owner-pass")
	var e enrollResp
	_ = json.Unmarshal(er.Body.Bytes(), &e)
	t.Logf("POST /host-enroll (passphrase)       -> %d; returns the EXISTING host's key: %v", er.Code, e.APIKey == "HKEY-1159")

	// Step 2: the controller key taken from the config.
	routes := []struct{ path, body string }{
		{"/offsite/register-key/c1", `{"public_key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl x"}`},
		{"/offsite/confirm-key/c1", `{"fingerprint":"SHA256:x"}`},
		{"/offsite/move-aside/c1", `{}`},
		{"/offsite/window-open/c1", `{"count_before":10}`},
		{"/offsite/abandon-request/c1", `{"path":"repo.orphaned-x"}`},
		{"/offsite/abandon-cancel/c1", `{}`},
		{"/offsite/abandon-status/c1", `{}`},
	}
	for _, r := range routes {
		ok := do(h, http.MethodPost, r.path, key, r.body)
		bad := do(h, http.MethodPost, r.path, "ckey-wrong", r.body)
		t.Logf("POST %-30s controller key -> %d, wrong key -> %d", r.path, ok.Code, bad.Code)
	}
	hk := e.APIKey
	for _, r := range []struct{ m, path, body string }{
		{"GET", "/hosts/h1/escrow", ""},
		{"GET", "/hosts/h1/escrow/retained", ""},
		{"PUT", "/hosts/h1/escrow", `{}`},
		{"POST", "/hosts/h1/wg", `{}`},
		{"POST", "/hosts/h1/pbs/consume-token", `{}`},
		{"GET", "/hosts/h1/desired-state", ""},
		{"GET", "/hosts/h1/jobs", ""},
		{"PUT", "/hosts/h1/recovery-credential", `{}`},
		{"POST", "/host-report", validReportBody("h1")},
	} {
		ok := do(h, r.m, r.path, hk, r.body)
		bad := do(h, r.m, r.path, "HKEY-wrong", r.body)
		t.Logf("%-4s %-32s host key -> %d, wrong key -> %d", r.m, r.path, ok.Code, bad.Code)
	}
	t.Logf("off-site service calls reached with the passphrase-derived key: %s", strings.Join(fk.calls, ", "))
}
