package web

// v0.67.0 — the two "the hub knows but never said so" banners on the customer page, plus the
// self-bind auto-mint. Each test asserts the banner is ABSENT in the nominal case as well as
// present in the deviating one: a banner that renders unconditionally is worse than none, because
// operators stop reading it.

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

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

const staleBannerMarker = "No data since the RESET"
const offsiteBannerMarker = "Offsite is enabled but was never provisioned"

func contains(html, needle string) bool { return strings.Contains(html, needle) }

// renderCustomerPageWithQuery renders the page with a query string (the flash arrives that way).
func renderCustomerPageWithQuery(t *testing.T, s *Server, customerID, query string) string {
	t.Helper()
	rr := httptest.NewRecorder()
	s.handleCustomerUnified(rr, httptest.NewRequest("GET", "/customers/"+customerID+query, nil), customerID)
	if rr.Code != 200 {
		t.Fatalf("customer page status = %d", rr.Code)
	}
	return rr.Body.String()
}

// flashSuccessBox captures the success flash div's inner content so a branch-less flash key
// (which renders the div with nothing in it) can be told apart from one that has text.
var flashSuccessBox = regexp.MustCompile(`(?s)<div class="flash flash-success">(.*?)</div>`)

func flashBoxIsEmpty(html string) bool {
	m := flashSuccessBox.FindStringSubmatch(html)
	if m == nil {
		return true
	}
	return strings.TrimSpace(m[1]) == ""
}

func seedCustomer(t *testing.T, st *store.Store, id, configJSON string) {
	t.Helper()
	cfg := &store.CustomerConfig{
		CustomerID: id, CustomerName: id, Domain: id + ".hu",
		RetrievalPassword: "pw", APIKey: "k", Status: "active",
		Email: id + "@example.com",
	}
	if configJSON != "" {
		cfg.ConfigJSON = configJSON
	}
	if err := st.SaveCustomerConfig(cfg); err != nil {
		t.Fatal(err)
	}
}

// --- R-37: post-RESET staleness -----------------------------------------------------------------

func TestCustomerPage_StaleSinceReset(t *testing.T) {
	t.Run("reset newer than the last report → banner", func(t *testing.T) {
		s, st := newTestServer(t)
		seedCustomer(t, st, "acme", "")
		if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil {
			t.Fatal(err)
		}
		// A reset that STARTED and COMPLETED after that report.
		id, err := st.StartCustomerReset("acme", true)
		if err != nil {
			t.Fatal(err)
		}
		if err := st.FinishCustomerReset(id); err != nil {
			t.Fatal(err)
		}

		html := renderCustomerPage(t, s, "acme")
		if !contains(html, staleBannerMarker) {
			t.Error("a completed RESET newer than the newest report must raise the staleness banner — " +
				"every health figure on the page describes a lifecycle that no longer exists")
		}
		// The Hungarian phrasing the customer-facing surface will use is quoted for the operator.
		if !contains(html, "RESET óta nincs adat") {
			t.Error("banner should quote „RESET óta nincs adat\"")
		}
	})

	t.Run("no reset at all → no banner", func(t *testing.T) {
		s, st := newTestServer(t)
		seedCustomer(t, st, "acme", "")
		if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil {
			t.Fatal(err)
		}
		if html := renderCustomerPage(t, s, "acme"); contains(html, staleBannerMarker) {
			t.Error("a customer that was never reset must not show the staleness banner")
		}
	})

	t.Run("reset still running (never completed) → no banner", func(t *testing.T) {
		s, st := newTestServer(t)
		seedCustomer(t, st, "acme", "")
		if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil {
			t.Fatal(err)
		}
		if _, err := st.StartCustomerReset("acme", true); err != nil { // started, NOT finished
			t.Fatal(err)
		}
		if html := renderCustomerPage(t, s, "acme"); contains(html, staleBannerMarker) {
			t.Error("an in-flight reset must not claim the data is stale — only a COMPLETED one does")
		}
	})

	t.Run("report arrived after the reset → banner clears", func(t *testing.T) {
		s, st := newTestServer(t)
		seedCustomer(t, st, "acme", "")
		id, err := st.StartCustomerReset("acme", true)
		if err != nil {
			t.Fatal(err)
		}
		if err := st.FinishCustomerReset(id); err != nil {
			t.Fatal(err)
		}
		// SQLite second-resolution timestamps: make the report unambiguously later.
		time.Sleep(1100 * time.Millisecond)
		if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil {
			t.Fatal(err)
		}
		if html := renderCustomerPage(t, s, "acme"); contains(html, staleBannerMarker) {
			t.Error("once the box reports again the page is current — the banner must clear itself, " +
				"otherwise it becomes permanent furniture")
		}
	})
}

// --- R-36 interim: offsite enabled but never provisioned ----------------------------------------

func TestCustomerPage_OffsiteUnprovisioned(t *testing.T) {
	cases := []struct {
		name       string
		configJSON string
		wantBanner bool
	}{
		{
			name:       "enabled with no type → the silent state, banner",
			configJSON: `{"offsite":{"enabled":true}}`,
			wantBanner: true,
		},
		{
			name:       "enabled and provisioned → no banner",
			configJSON: `{"offsite":{"enabled":true,"type":"storagebox"}}`,
			wantBanner: false,
		},
		{
			name:       "disabled → no banner (nothing was promised)",
			configJSON: `{"offsite":{"enabled":false}}`,
			wantBanner: false,
		},
		{
			name:       "no offsite key at all → no banner",
			configJSON: `{}`,
			wantBanner: false,
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			s, st := newTestServer(t)
			seedCustomer(t, st, "acme", tc.configJSON)
			html := renderCustomerPage(t, s, "acme")
			if got := contains(html, offsiteBannerMarker); got != tc.wantBanner {
				t.Errorf("banner present = %v, want %v (config %s)", got, tc.wantBanner, tc.configJSON)
			}
		})
	}
}

// --- the pbsdr_reissued flash had no text at all (rendered an empty box) -------------------------

func TestCustomerPage_EveryRedirectFlashRendersText(t *testing.T) {
	// Every flash key the server can redirect with must produce visible text. pbsdr_reissued
	// shipped without a branch, so the operator got an empty success box after re-issuing PBS
	// credentials — observed live 2026-07-18.
	for _, flash := range []string{"pbsdr_reissued", "offsite_reissued", "reset_done", "created"} {
		t.Run(flash, func(t *testing.T) {
			s, st := newTestServer(t)
			seedCustomer(t, st, "acme", "")
			html := renderCustomerPageWithQuery(t, s, "acme", "?flash="+flash)
			if !contains(html, `class="flash flash-success"`) {
				t.Fatalf("flash %q rendered no success box at all", flash)
			}
			if flashBoxIsEmpty(html) {
				t.Errorf("flash %q renders an EMPTY box — the operator is shown a confirmation with no words", flash)
			}
		})
	}
}
