package web

// v0.70.1 — the ghost customer's Delete button must EXIST. v0.70.0 made ghost customers
// deletable server-side, but the Danger-zone card sat inside {{if .HasConfig}}, so the whole
// ghost path was implemented and unreachable (dead UI — the fourth inert-seam defect, found
// live on demo-vm-felhom). Handler tests that POST directly prove nothing about reachability:
// these tests assert the RENDERED page, per branch of the new gate.
//
// Assertions anchor on the delete form's action attribute and the customerDeleteOpen( call
// site — not free-text strings a copy edit would break.

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

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

const (
	deleteFormMarker = `action="/configs/ghost/delete"`
	deleteOpenMarker = `customerDeleteOpen('ghost')`
	deleteFuncMarker = `function customerDeleteOpen(`
	resetFormMarker  = `action="/configs/ghost/reset"`
	blockFormMarker  = `action="/customers/ghost/block"`
	unblockFormMkr   = `action="/customers/ghost/unblock"`
)

// Scenario A — ghost with residue (the demo-vm-felhom shape): no config row, 0 hosts,
// residue.Total() > 0 (report rows). The Danger zone must render — delete form, opener button,
// and its script — while RESET and Block (config-coupled affordances) must NOT.
func TestCustomerPage_GhostWithResidue_RendersDeleteOnly(t *testing.T) {
	s, st := newTestServer(t)
	// Reports only — NO SaveCustomerConfig. This is the ghost: config gone, residue keeps the
	// customer on the list and raising staleness alerts.
	if err := st.SaveReport("ghost", []byte(tabsTestReportJSON)); err != nil {
		t.Fatal(err)
	}
	if residue, err := st.CustomerResidue("ghost"); err != nil || residue.Total() == 0 {
		t.Fatalf("fixture must have residue (got total=%v err=%v)", residue, err)
	}

	html := renderCustomerPage(t, s, "ghost")

	if !strings.Contains(html, deleteFormMarker) {
		t.Errorf("ghost page must render the delete form (%s) — without it the entire v0.70.0 ghost-delete path is dead UI", deleteFormMarker)
	}
	if !strings.Contains(html, deleteOpenMarker) {
		t.Errorf("ghost page must render the Delete button's %s opener", deleteOpenMarker)
	}
	if !strings.Contains(html, deleteFuncMarker) {
		t.Errorf("the customerDeleteOpen script must exist whenever the button does (was inside the old HasConfig gate)")
	}
	// The WRONG outcome this split must not introduce: RESET rendering for a ghost. RESET is
	// identity-preserving re-onboarding; a ghost has no identity to preserve.
	if strings.Contains(html, resetFormMarker) {
		t.Errorf("RESET card must NOT render for a ghost (found %s)", resetFormMarker)
	}
	// Blocking gates dashboard visibility of a CONFIGURED customer — meaningless for a ghost.
	if strings.Contains(html, blockFormMarker) || strings.Contains(html, unblockFormMkr) {
		t.Error("Block/Unblock forms must NOT render for a ghost")
	}
}

// Scenario B — normal configured customer: byte-for-byte the affordances of today — RESET card
// + full Danger zone incl. Block, and the delete machinery.
func TestCustomerPage_ConfiguredCustomer_KeepsAllAffordances(t *testing.T) {
	s, st := newTestServer(t)
	if err := st.SaveCustomerConfig(&store.CustomerConfig{
		CustomerID: "ghost", CustomerName: "Ghost", Domain: "ghost.hu",
		RetrievalPassword: "pw", APIKey: "k", Status: "active",
	}); err != nil {
		t.Fatal(err)
	}
	if err := st.SaveReport("ghost", []byte(tabsTestReportJSON)); err != nil {
		t.Fatal(err)
	}

	html := renderCustomerPage(t, s, "ghost")

	for _, marker := range []string{resetFormMarker, blockFormMarker, deleteFormMarker, deleteOpenMarker, deleteFuncMarker} {
		if !strings.Contains(html, marker) {
			t.Errorf("configured customer lost an affordance: %s absent — the gate split must not change the normal shape", marker)
		}
	}
}

// Scenario B variant — a BLOCKED configured customer renders Unblock (the IsBlocked branch
// survived the extra HasConfig wrapper around the block forms).
func TestCustomerPage_BlockedCustomer_RendersUnblock(t *testing.T) {
	s, st := newTestServer(t)
	if err := st.SaveCustomerConfig(&store.CustomerConfig{
		CustomerID: "ghost", CustomerName: "Ghost", Domain: "ghost.hu",
		RetrievalPassword: "pw", APIKey: "k", Status: "active",
	}); err != nil {
		t.Fatal(err)
	}
	// SaveCustomerConfig never writes status — blocking goes through SetCustomerConfigStatus
	// (the same call the /block handler makes).
	if err := st.SetCustomerConfigStatus("ghost", "blocked"); err != nil {
		t.Fatal(err)
	}
	html := renderCustomerPage(t, s, "ghost")
	if !strings.Contains(html, unblockFormMkr) {
		t.Errorf("blocked configured customer must render the Unblock form (%s)", unblockFormMkr)
	}
}

// Scenario C — nothing left: no config, 0 hosts, residue.Total() == 0. There is no renderable
// Deletable=false state with real store reads (customer != nil implies a report row implies
// residue > 0), so "no Danger zone" manifests as the page-level 404 — the same semantics as the
// delete preview's 404 ("there is nothing here").
func TestCustomerPage_NothingLeft_404(t *testing.T) {
	s, _ := newTestServer(t)
	rr := httptest.NewRecorder()
	s.handleCustomerUnified(rr, httptest.NewRequest("GET", "/customers/ghost", nil), "ghost")
	if rr.Code != 404 {
		t.Fatalf("a customer with no config, no hosts and no residue must 404 (mirrors the preview) — got %d", rr.Code)
	}
}
