package web

// R-323 — THE THIRD NAME. One secret, one name, and none of them a near-homograph of another.
//
// Three secrets a customer can hold, and they must be tellable apart by a hurried reader:
//
//   three words  takes control of the dashboard              „Beállító kód"
//   ten words    opens the sealed off-site backups           „Helyreállítási kód"
//   five words   proves the account owns the box being bound „Tulajdonosi jelmondat"  ← this one
//
// The five-word phrase used to be „Visszaállító jelszó" — one word away from „Visszaállító kód",
// which R-295 had just retired for colliding with „Helyreállítási kód". Found while shipping that
// rename and deliberately NOT swept in with it; ruled on separately by the operator 2026-08-13.
//
// WHY NOT THE OBVIOUS CANDIDATES, since a test is where the reasoning survives:
//
//   „Összekötési jelszó" — the OTHER factor on this very page is the „Párosító kód". Naming this one
//        after the same act would leave the two factors a customer types in one sitting separated
//        only by kód-versus-jelszó, which is structurally the „Visszaállító kód"/„Visszaállító
//        jelszó" trap being removed.
//   „Fiókjelszó"        — there IS an account password (the dashboard login). This would collide
//        with a DIFFERENT real secret, i.e. trade one homograph for a worse one.
//
// „Tulajdonosi jelmondat" is distinct on BOTH axes — stem (Tulajdonosi vs Beállító / Helyreállítási
// / Párosító) and noun (jelmondat vs kód / jelszó) — and it says what the phrase actually does.

import (
	"net/http"
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-hub/internal/notify"
	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)

// retiredThirdName is the name this change removes. It is a SUBSTRING check on the stem so that a
// possessive or accusative form („visszaállító jelszavadat") cannot slip past a check written
// against the nominative — the R-299 lesson, where a guard matched one inflection of a Hungarian
// verb and the plural walked straight by.
const retiredThirdName = "isszaállító jelsz"

// The binding page names the secret „Tulajdonosi jelmondat" and nowhere carries the retired name.
// Driven through the real handler, so a template that never renders is visible here.
func TestSelfBind_ThirdSecretNaming(t *testing.T) {
	s, st := newTestServer(t)
	selfBindSetup(t, st, "acme", testCode)
	token := mintLink(t, st, "acme", selfBindTTL)

	body := bindGET(t, s, token).Body.String()

	if !strings.Contains(body, "Tulajdonosi jelmondat") {
		t.Errorf("the field label must name the secret „Tulajdonosi jelmondat”:\n%s", body)
	}
	if strings.Contains(body, retiredThirdName) {
		t.Error("the retired name „Visszaállító jelszó” is back on the binding page")
	}
	// The two factors on this page must not be near-homographs of each other. The pairing code keeps
	// its name; what matters is that the SECOND factor no longer differs from it by one noun.
	if !strings.Contains(body, "Párosító kód") {
		t.Error("the pairing code lost its name — the two factors must both be named")
	}
	// Neither of the OTHER two secrets may be named on this page: a customer who reads „kód" here
	// twice is the failure this whole arc exists to prevent.
	for _, other := range []string{"Beállító kód", "Helyreállítási kód"} {
		if strings.Contains(body, other) {
			t.Errorf("the binding page names a different secret (%q) — that is the collision", other)
		}
	}
}

// The failure banner must name the secret the same way the label does. A page that asks for a
// „Tulajdonosi jelmondat" and then complains about „a jelszó" has two names for one secret again,
// which is the defect in miniature.
func TestSelfBind_FailureBannerUsesTheSameName(t *testing.T) {
	s, st := newTestServer(t)
	selfBindSetup(t, st, "acme", testCode)
	token := mintLink(t, st, "acme", selfBindTTL)

	rr := bindPOST(t, s, token, testCodeFmt, "wrong wrong wrong wrong wrong")
	body := rr.Body.String()

	if !strings.Contains(body, "tulajdonosi jelmondat") {
		t.Errorf("the failure banner does not name the secret the way the label does:\n%s", body)
	}
	if strings.Contains(body, retiredThirdName) {
		t.Error("the retired name is on the failure banner")
	}
}

// The e-mail that carries the link names it identically. The mail is read BEFORE the page, so a
// mismatch here is the customer's first impression of two different secrets.
func TestSelfBindEmail_UsesTheSameName(t *testing.T) {
	subject, body := notify.FormatSelfBindEmail("acme", "https://hub.example/bind/tok")

	if !strings.Contains(body, "tulajdonosi jelmondatodat") {
		t.Errorf("the self-bind mail does not name the secret „Tulajdonosi jelmondat”:\n%s", body)
	}
	if strings.Contains(subject+body, retiredThirdName) {
		t.Error("the retired name is still in the self-bind mail")
	}
	// It must still say what the thing IS — a rename that leaves the customer unable to recognise
	// what they are holding has fixed a collision and broken the delivery.
	if !strings.Contains(body, "5 szóból álló kifejezést") {
		t.Error("the mail no longer says the phrase is five words")
	}
}

// THE ACCEPTANCE PIN — the whole point of calling this naming rather than function.
//
// The SAME phrase, typed the same messy way a human types it (odd spacing, mixed case, mixed
// separators), still binds the appliance after the rename. If this ever goes red, the rename stopped
// being a rename.
func TestSelfBindPassphrase_StillAcceptedAfterTheRename(t *testing.T) {
	s, st := newTestServer(t)
	id := selfBindSetup(t, st, "acme", testCode)
	token := mintLink(t, st, "acme", selfBindTTL)

	rr := bindPOST(t, s, token, testCodeFmt, "  Alpha  Beta gamma-delta epsilon ")

	if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "egy percen belül") {
		t.Fatalf("the same passphrase stopped being accepted: code=%d body=%q", rr.Code, rr.Body.String())
	}
	if a, _ := st.GetAppliance(id); a == nil || a.Status != store.ApplianceBound {
		t.Fatalf("the appliance did not bind: %+v", a)
	}
	// The form field NAME is deliberately still `passphrase`: renaming customer copy must not touch
	// the wire. A changed field name would be an acceptance change wearing a rename's clothes.
	body := bindGET(t, s, mintLink(t, st, "acme", selfBindTTL)).Body.String()
	if !strings.Contains(body, `name="passphrase"`) {
		t.Error("the form field name changed — that is not a rename, that is a wire change")
	}
}
