package web

import (
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
	"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)

// appInfoWithCards overlays the R-75 folder card onto the shared app_info fixture. It reuses
// appInfoData (lifecycle_test.go) deliberately — that helper passes a *Metadata for a reason
// documented there (value vs pointer receivers at render time); do not fork it.
func appInfoWithCards(st stacks.Stack, cards []DataPathCard) map[string]interface{} {
	d := appInfoData(st)
	if len(cards) > 0 {
		d["DataPathCards"] = cards
	}
	return d
}

// SEAM-WIRING RULE: a conditional affordance ships with a render test PER BRANCH of its gate.
// Branch 1 — cards present: the block renders, with the label, the deep link and the copy.
func TestAppInfo_DataPathCardRenders(t *testing.T) {
	st := stacks.Stack{Name: "paperless-ngx", Deployed: true, State: stacks.StateRunning,
		Meta: stacks.Metadata{Slug: "paperless-ngx", DisplayName: "Paperless-ngx"}}
	cards := []DataPathCard{{
		Label:       "Beolvasandó dokumentumok",
		Link:        importFolderLink("demo-felhom.eu", "paperless"),
		Consequence: consequenceFor(appbackup.ClassExcluded, stacks.RoleImport),
		IsImport:    true,
		FreeSpace:   "44.8 GB szabad",
	}}
	html := renderBackupPage(t, "app_info", appInfoWithCards(st, cards))

	if !strings.Contains(html, "Hova tegyem a fájlokat?") {
		t.Error("the folder card heading must render")
	}
	if !strings.Contains(html, "Beolvasandó dokumentumok") {
		t.Error("the catalog label must render")
	}
	// The deep link must reach the page INTACT — this is the assertion the brief asks for, and it is
	// what would have caught the v0.150.0 class of bug (a key rendered where another belonged).
	if !strings.Contains(html, "https://files.demo-felhom.eu/files/Beolvas%C3%A1s/paperless") {
		t.Errorf("the FileBrowser deep link must render intact; body:\n%s", excerpt(html, "datapath"))
	}
	if !strings.Contains(html, "nem készül róla biztonsági mentés") {
		t.Error("an excluded drop-zone must say it is unbacked")
	}
	if !strings.Contains(html, "44.8 GB szabad") {
		t.Error("the system-drive free space must render on an import row")
	}
	// The copy must not promise a single click — a cold deep link goes through the FileBrowser login.
	if !strings.Contains(html, "be kell jelentkezned") {
		t.Error("the card must warn that a FileBrowser sign-in may be needed")
	}
}

// Branch 2 — no cards: nothing renders, and in particular no empty heading.
func TestAppInfo_NoDataPathCardsRendersNothing(t *testing.T) {
	st := stacks.Stack{Name: "docmost", Deployed: true, State: stacks.StateRunning,
		Meta: stacks.Metadata{Slug: "docmost", DisplayName: "Docmost"}}
	html := renderBackupPage(t, "app_info", appInfoWithCards(st, nil))
	if strings.Contains(html, "Hova tegyem a fájlokat?") {
		t.Error("an app with no data_paths must not render the folder card")
	}
	if strings.Contains(html, "datapath-row") {
		t.Error("no folder rows must render")
	}
}

// Fork-4: the consequence line is CLASS-driven, so the UI can never promise a backup the engines do
// not make. `excluded` is dropped at every tier — it must say so.
func TestConsequenceIsClassDriven(t *testing.T) {
	imp := consequenceFor(appbackup.ClassExcluded, stacks.RoleImport)
	if !strings.Contains(imp, "törli innen") || !strings.Contains(imp, "nem készül róla biztonsági mentés") {
		t.Errorf("an excluded import folder must say it is temporary AND unbacked: %q", imp)
	}
	for _, cls := range []appbackup.BindClass{appbackup.ClassMandatory, appbackup.ClassOptional} {
		lib := consequenceFor(cls, stacks.RoleLibrary)
		if !strings.Contains(lib, "Biztonsági mentés készül") {
			t.Errorf("class %q must promise a backup: %q", cls, lib)
		}
		if strings.Contains(lib, "nem készül") {
			t.Errorf("class %q must NOT say unbacked: %q", cls, lib)
		}
	}
	// An unclassified bind says NOTHING rather than guessing — an unverified backup promise about a
	// customer's files is worse than no sentence.
	if got := consequenceFor("", stacks.RoleLibrary); got != "" {
		t.Errorf("an unclassified path must produce no promise, got %q", got)
	}
}

// A card is only built for a DEPLOYED app (Fork-2: deployed-only in the UI).
func TestBuildDataPathCards_UndeployedYieldsNothing(t *testing.T) {
	s := testServer(t)
	st := &stacks.Stack{Name: "paperless-ngx", Deployed: false,
		Meta: stacks.Metadata{Slug: "paperless-ngx", DataPaths: []stacks.DataPath{
			{Path: "paperless", Root: appbackup.RootImport, Role: stacks.RoleImport, Label: "x"},
		}}}
	if got := s.buildDataPathCards(st); got != nil {
		t.Errorf("an undeployed app must get no folder card, got %v", got)
	}
}

func excerpt(html, needle string) string {
	i := strings.Index(html, needle)
	if i < 0 {
		return "(needle not found)"
	}
	end := i + 400
	if end > len(html) {
		end = len(html)
	}
	return html[i:end]
}
