package web

import (
	"strings"
	"testing"

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

// v0.163.1 launcher polish. Two live findings: (A) the launcher monogram bled through every
// transparent white logo because it rendered unconditionally UNDER the img; it must be revealed
// ONLY when the img chain fails. (B) the /static/app-placeholder.svg default landed only in the
// canonical app_list_row — four sibling icon chains still dead-ended in hidden for logo-less apps.

// canonical3Step is the SVG→PNG→data-fallback→hidden onerror grammar every app-logo surface must use.
const canonical3Step = `else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}`

// Group A — the launcher monogram is failure-only.
// COMPANION red-proof (REPORT): drop `classList.add('launch-tile--noimg')` from the else branch →
// the onerror-content assertion FAILS.
func TestLauncher_MonogramRevealOnFailure(t *testing.T) {
	data := map[string]interface{}{
		"Page": "launcher", "Title": "Indítópult", "Domain": "demo-felhom.eu",
		"Apps": []LauncherApp{
			{Name: "mealie", DisplayName: "Mealie", Slug: "mealie", State: stacks.StateRunning, Subdomain: "recept"},
			{Name: "jellyfin", DisplayName: "Jellyfin", Slug: "jellyfin", State: stacks.StateStopped, Subdomain: "media"},
		},
	}
	html := renderBackupPage(t, "launcher", data)

	// The reveal class must never sit in a class attribute in the initial markup — it is added by
	// onerror at runtime only. Its ONLY occurrences are the two onerror `classList.add(...)` calls;
	// a count above 2 would mean it leaked into a class="" attribute.
	if got := strings.Count(html, "launch-tile--noimg"); got != 2 {
		t.Errorf("launch-tile--noimg must appear only in the two onerror hooks (2), got %d — it must not be a static class", got)
	}
	// Both branches (operational <a> + stopped <div>) keep the monogram span AND the reveal hook.
	if got := strings.Count(html, "launch-mono"); got != 2 {
		t.Errorf("expected the monogram span in both tile branches (2), got %d", got)
	}
	if got := strings.Count(html, "classList.add('launch-tile--noimg')"); got != 2 {
		t.Errorf("both tile onerror chains must reveal the monogram on final failure, got %d", got)
	}
}

// Group B — every app-logo surface ends in the placeholder (or infra glyph), never hidden.

// TestBackupsApps_IconPlaceholder — the allowlisted aligned row now matches the canonical chain.
// COMPANION red-proof (REPORT): revert backups_apps.html to the 2-step chain → this FAILS.
func TestBackupsApps_IconPlaceholder(t *testing.T) {
	data := map[string]interface{}{
		"Page": "backups-apps", "Title": "Biztonsági mentés — Alkalmazások",
		"Backup":         &backup.FullBackupStatus{AppDataInfo: []backup.AppBackupInfo{{StackName: "docmost", DisplayName: "Docmost"}}},
		"AppBackupRows":  []AppBackupRow{{StackName: "docmost", DisplayName: "Docmost", Slug: "docmost", Status: "green", Tier3State: "off"}},
		"DBSectionState": "dumps",
	}
	html := renderBackupPage(t, "backups_apps", data)
	if !strings.Contains(html, `data-fallback="/static/app-placeholder.svg"`) {
		t.Error("backups_apps icon must default to the app placeholder")
	}
	if !strings.Contains(html, canonical3Step) {
		t.Error("backups_apps icon must use the canonical 3-step onerror chain")
	}
}

// TestStacks_IconPlaceholderAndInfra — regular apps default to the placeholder; infra keeps the
// infra glyph.
func TestStacks_IconPlaceholderAndInfra(t *testing.T) {
	data := map[string]interface{}{
		"Page": "stacks", "Title": "Alkalmazások",
		"Stacks": []stacks.Stack{
			{Name: "docmost", Deployed: true, State: stacks.StateRunning,
				Meta: stacks.Metadata{Slug: "docmost", DisplayName: "Docmost"}},
			{Name: "filebrowser", Protected: true, State: stacks.StateRunning,
				Meta: stacks.Metadata{Slug: "filebrowser", DisplayName: "Filebrowser"}},
		},
		"MissingStorage":  map[string]string{},
		"NetworkWarnings": map[string]string{},
		"NetworkStubs":    map[string]string{},
		"StorageLabels":   map[string]string{},
		"Subdomains":      map[string]string{"filebrowser": "files"},
		"RunningCount":    2, "StoppedCount": 0, "TotalCount": 2,
		"SystemInfo":    system.SystemInfo{},
		"BackupEnabled": false,
		"Domain":        "demo-felhom.eu",
	}
	html := renderBackupPage(t, "stacks", data)
	if !strings.Contains(html, `data-fallback="/static/app-placeholder.svg"`) {
		t.Error("a regular app card must default to the app placeholder")
	}
	if !strings.Contains(html, `data-fallback="/static/infra-logo.svg"`) {
		t.Error("an infra app card must keep the infra glyph fallback")
	}
	if !strings.Contains(html, canonical3Step) {
		t.Error("stacks card icon must use the canonical 3-step onerror chain")
	}
}

// TestAppInfo_HeroPlaceholder_ScreenshotsUntouched — hero logo aligned; screenshots still vanish.
func TestAppInfo_HeroPlaceholder_ScreenshotsUntouched(t *testing.T) {
	st := stacks.Stack{Name: "docmost", Deployed: true, State: stacks.StateRunning,
		Meta: stacks.Metadata{Slug: "docmost", DisplayName: "Docmost"}}
	data := map[string]interface{}{
		"Page": "stacks", "Title": st.Meta.DisplayName,
		"Stack": &st, "Meta": st.Meta, "AppInfo": st.Meta.AppInfo,
		"HasAppInfo": st.Meta.HasAppInfo(), "EffectiveSubdomain": st.Meta.Subdomain,
		"Domain": "demo-felhom.eu",
	}
	html := renderBackupPage(t, "app_info", data)
	if !strings.Contains(html, `data-fallback="/static/app-placeholder.svg"`) {
		t.Error("app_info hero logo must default to the app placeholder")
	}
	if !strings.Contains(html, canonical3Step) {
		t.Error("app_info hero logo must use the canonical 3-step chain")
	}
	// Screenshots must be byte-identical to before — they still vanish on error, no placeholder.
	if !strings.Contains(html, `class="app-screenshot"`) || !strings.Contains(html, `onerror="this.style.display='none'"`) {
		t.Error("app_info screenshot imgs must keep their original vanish-on-error onerror")
	}
	if strings.Contains(html, `class="app-screenshot" `) && strings.Contains(html, `app-screenshot" data-fallback`) {
		t.Error("app_info screenshots must NOT gain a placeholder fallback")
	}
}

// TestDeploy_LogoPlaceholder — deploy logo aligned, keeping its .LogoURL/.LogoPNGURL data source.
func TestDeploy_LogoPlaceholder(t *testing.T) {
	st := stacks.Stack{Name: "docmost", State: stacks.StateNotDeployed,
		Meta: stacks.Metadata{Slug: "docmost", DisplayName: "Docmost"}}
	data := map[string]interface{}{
		"Page": "deploy", "Title": "Docmost — Telepítés",
		"Stack": &st, "Meta": st.Meta, "AlreadyDeployed": false,
		"LogoURL": "/static/assets/docmost-logo.svg", "LogoPNGURL": "/static/assets/docmost-logo.png",
		"Domain": "demo-felhom.eu",
	}
	html := renderBackupPage(t, "deploy", data)
	if !strings.Contains(html, `data-fallback="/static/app-placeholder.svg"`) {
		t.Error("deploy logo must default to the app placeholder")
	}
	if !strings.Contains(html, canonical3Step) {
		t.Error("deploy logo must use the canonical 3-step chain")
	}
	// Data source preserved: the deploy page keeps .LogoPNGURL (not a funcmap call). The URL is
	// JS-escaped inside the onerror string (\/static\/…), so assert the slash-free filename.
	if !strings.Contains(html, "docmost-logo.png") {
		t.Error("deploy logo must keep its .LogoPNGURL data source in the chain")
	}
}
