package web

import (
	"strings"
	"testing"

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

// v0.126.0 Part A — the shared app_list_row partial (app_row.html) is the ONE row source on
// the list surfaces. Per-surface render tests: the partial's marker classes are present, the
// old hand-rolled structures (storage-path-item rows / stack-card rows) are gone, and the
// caller-provided action blocks (behavior) are untouched.
// COMPANION red-proof (recorded in REPORT): paste one pre-0.126.0 storage-path-item row block
// back into backups_remote.html → scripts/app_row_dedup_gate.py exits 1.

func appRowSplitData() map[string]interface{} {
	d := splitTestData()
	d["OffboxApps"] = []OffboxAppRow{
		{Name: "calibre-web", DisplayName: "Calibre-Web", Slug: "calibre-web", Enabled: true},
		{Name: "radarr", DisplayName: "Radarr", Slug: "radarr", Enabled: false},
	}
	return d
}

func TestAppRow_RemoteToggleList(t *testing.T) {
	html := renderBackupPage(t, "backups_remote", appRowSplitData())
	if !strings.Contains(html, `class="app-row"`) || !strings.Contains(html, `app-row-name`) {
		t.Error("remote toggle list must render through the app_list_row partial")
	}
	if strings.Contains(html, "storage-path-item") {
		t.Error("old storage-path-item row structure survives on the remote page")
	}
	// Behavior unchanged: the toggle form + both label variants render as before.
	if !strings.Contains(html, `action="/backup/offbox/toggle"`) {
		t.Error("toggle form missing")
	}
	if !strings.Contains(html, "Távoli mentés kikapcsolása") || !strings.Contains(html, "Távoli mentés bekapcsolása") {
		t.Error("toggle button labels changed")
	}
}

func TestAppRow_RestoreLists(t *testing.T) {
	html := renderBackupPage(t, "backups_restore", appRowSplitData())
	if !strings.Contains(html, `class="app-row"`) {
		t.Error("restore-page lists must render through the app_list_row partial")
	}
	if strings.Contains(html, "storage-path-item") {
		t.Error("old storage-path-item row structure survives on the restore page")
	}
	// Behavior unchanged: restore-to-verify form + .fab download button.
	if !strings.Contains(html, `action="/backup/offbox/restore"`) {
		t.Error("restore-to-verify form missing")
	}
	if !strings.Contains(html, `fab-dl-btn`) || !strings.Contains(html, "Letöltés (.fab)") {
		t.Error(".fab download action missing")
	}
	// The disabled app (radarr) must NOT appear in the restore-to-verify list (Enabled filter),
	// but MUST appear in the .fab list — the partial must not have changed the filters.
	if strings.Count(html, `value="radarr"`) != 0 {
		t.Error("restore-to-verify must list only offbox-enabled apps")
	}
	if !strings.Contains(html, `data-stack="radarr"`) {
		t.Error(".fab list must list all apps")
	}
}

func TestAppRow_Dashboard(t *testing.T) {
	data := map[string]interface{}{
		"Page": "dashboard", "Title": "Vezérlőpult",
		"Stacks": []stacks.Stack{
			{Name: "radarr", Deployed: true, State: stacks.StateRunning,
				Meta: stacks.Metadata{Slug: "radarr", DisplayName: "Radarr", Description: "Filmgyűjtemény kezelése"}},
		},
		"MissingStorage":  map[string]string{},
		"NetworkWarnings": map[string]string{},
		"NetworkStubs":    map[string]string{},
		"Subdomains":      map[string]string{"radarr": "radarr"},
		"RunningCount":    1, "StoppedCount": 0, "TotalCount": 1,
		"SystemInfo":    system.SystemInfo{},
		"BackupEnabled": false,
		"Domain":        "demo-felhom.eu",
	}
	html := renderBackupPage(t, "dashboard", data)
	if !strings.Contains(html, `class="app-row stack-state-run"`) {
		t.Error("dashboard rows must render through app_list_row with the state RowClass")
	}
	if !strings.Contains(html, `data-href="/apps/radarr"`) {
		t.Error("dashboard row lost its data-href")
	}
	if !strings.Contains(html, "Filmgyűjtemény kezelése") {
		t.Error("dashboard row lost the description secondary line")
	}
	if strings.Contains(html, "stack-card") || strings.Contains(html, "stack-info") {
		t.Error("old stack-card row structure survives on the dashboard")
	}
}

// The Alkalmazások collapsed header is ALIGNED to the row grammar (icon + name left,
// status + chevron right) while keeping its own expander structure.
func TestAppRow_AppsHeaderAligned(t *testing.T) {
	d := appRowSplitData()
	d["AppBackupRows"] = []AppBackupRow{
		{StackName: "calibre-web", DisplayName: "Calibre-Web", Slug: "calibre-web",
			Status: "green", Tier3State: "active"},
	}
	d["Backup"] = &backup.FullBackupStatus{
		AppDataInfo: []backup.AppBackupInfo{{StackName: "calibre-web", DisplayName: "Calibre-Web"}},
	}
	d["Offbox"] = &settings.OffboxTarget{Enabled: true, Host: "nas.local", LastStatus: "ok", EscrowState: "escrowed"}
	html := renderBackupPage(t, "backups_apps", d)

	hdr := html[strings.Index(html, `class="app-backup-row-header"`):]
	hdr = hdr[:strings.Index(hdr, "app-backup-row-detail")]
	iIcon := strings.Index(hdr, "app-row-icon")
	iName := strings.Index(hdr, "app-backup-row-name")
	iDot := strings.Index(hdr, "status-dot")
	iChevron := strings.Index(hdr, "expand-icon")
	if iIcon < 0 || iName < 0 || iDot < 0 || iChevron < 0 {
		t.Fatalf("aligned header pieces missing (icon=%d name=%d dot=%d chevron=%d)", iIcon, iName, iDot, iChevron)
	}
	if !(iIcon < iName && iName < iDot && iDot < iChevron) {
		t.Error("header grammar must be icon, name, ..., status, chevron (left→right)")
	}
	if !strings.Contains(hdr, `onclick="toggleBackupDetail(this)"`) {
		t.Error("expander behavior must be untouched")
	}
}
