package web

import (
	"context"
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
	"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
)

// R-114 — the third state: CONFIGURED, and its drive is GONE.
//
// THE BUG THESE PIN, measured live in E-2d (felhom.eu audits/E2D-fresh-vm-2026-07-29.md §5.3). With
// the assigned target's drive detached, the endpoint returned:
//
//	{"degraded":true,"target":"felhom-backup",
//	 "message":"A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer …",
//	 "offer_path":"/mnt/felhom-drives/mentes2","offer_label":"Mentés meghajtó"}
//
// Two falsehoods in one payload. The backup was NOT on the system disk — it was on a drive that had
// vanished. And the remedy offered was THE DRIVE THAT JUST DISAPPEARED. resolveBackupTargetState had
// only two outcomes (a disk claims the target, or nothing does) so the third state fell into the
// second and inherited its message and its offer.
//
// The fixtures below are the observed shapes, not invented ones: the drive is still listed with a
// MountPath (the registry keeps the configured path), Role user-data, but BackupTarget is false —
// which is exactly why the healthy branch missed it and the offer branch matched it.

// absentHarness wires a Server whose agent reports `primary` as the tier target and `disks` as the
// drive list, with one registered storage path so firstOfferableDrive has something to find. Both
// halves come through the production seams (tiersFn / disksFn), so the resolver under test is the
// real one.
func absentHarness(t *testing.T, primary string, disks []agentapi.DiskInfo, registerPath string) *Server {
	t.Helper()
	s := testServer(t)
	if registerPath != "" {
		if err := s.settings.AddStoragePath(settings.StoragePath{Path: registerPath, Label: "Mentés meghajtó"}); err != nil {
			t.Fatalf("register storage path: %v", err)
		}
	}
	s.tiersFn = func(context.Context) (agentapi.TiersResponse, error) {
		return agentapi.TiersResponse{Tiers: []agentapi.BackupTierInfo{{Target: primary, Primary: true}}}, nil
	}
	s.disksFn = func(context.Context) (agentapi.DisksResponse, error) {
		return agentapi.DisksResponse{Disks: disks}, nil
	}
	return s
}

// theVanishedDrive is the E-2d shape: registered, still carrying its configured MountPath, user-data,
// but no longer claiming the backup target because its device is gone.
var theVanishedDrive = agentapi.DiskInfo{
	Name:         "mentes2",
	MountPath:    "/mnt/mentes2",
	GuestPath:    "/mnt/felhom-drives/mentes2",
	Role:         "user-data",
	BackupTarget: false,
}

// ── Scenario C — configured, drive absent ───────────────────────────────────────────────────────

// RED-PROOF: delete the `if targetIsConfiguredDrive(primary)` branch from resolveBackupTargetState
// and this fails twice — the message becomes the system-disk copy and OfferPath becomes non-empty.
func TestConfiguredButAbsentTargetDoesNotClaimTheSystemDisk(t *testing.T) {
	s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
	st := s.resolveBackupTargetState(context.Background())

	if !st.Known {
		t.Fatal("state is unknown — the fixture answers both agent calls")
	}
	if !st.TargetAbsent {
		t.Fatalf("TargetAbsent is false for target %q with no disk claiming it — the configured-but-gone "+
			"state fell back into never-configured, which is the R-114 bug", st.TargetID)
	}
	msg := degradedMessageFor(st)
	if strings.Contains(msg, "ugyanazon a lemezen") {
		t.Errorf("the customer is told the backup is on the SYSTEM DISK, which is false — the target is "+
			"%q, a drive that has vanished. Got: %s", st.TargetID, msg)
	}
	if !strings.Contains(msg, "nem érhető el") {
		t.Errorf("the absent-drive copy is missing; got: %s", msg)
	}
}

// The remedy must be "reconnect THAT drive", never "pick a different one" — and above all never the
// drive that just disappeared, which is what E-2d actually observed being offered.
func TestConfiguredButAbsentTargetOffersNothing(t *testing.T) {
	s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
	st := s.resolveBackupTargetState(context.Background())

	if st.OfferPath != "" {
		t.Errorf("offered %q while the configured target is absent — E-2d observed this exact payload "+
			"offering the drive that had just vanished", st.OfferPath)
	}
	if st.OfferLabel != "" {
		t.Errorf("offer label %q leaked with no offer path", st.OfferLabel)
	}
}

// The suppression must be the branch's own doing, NOT a side effect of the drive being flagged
// Disconnected — that flag is set by the agent-side drive gate in another repo (R-113), and this
// state has to be correct before, during and independently of that landing.
func TestAbsentTargetSuppressesOfferWithoutRelyingOnTheDisconnectedFlag(t *testing.T) {
	s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
	for _, sp := range s.settings.GetStoragePaths() {
		if sp.Disconnected {
			t.Fatalf("fixture invalid: %s is already marked Disconnected, so this test would pass "+
				"for the wrong reason", sp.Path)
		}
	}
	if st := s.resolveBackupTargetState(context.Background()); st.OfferPath != "" {
		t.Errorf("offer %q survived with Disconnected UNSET — the suppression is leaning on the "+
			"other repo's flag instead of on this state", st.OfferPath)
	}
}

// ── Scenario A — never configured (must be unchanged) ───────────────────────────────────────────

func TestNeverConfiguredStillSaysSystemDiskAndStillOffers(t *testing.T) {
	s := absentHarness(t, builtinLocalTarget, []agentapi.DiskInfo{{
		Name: "hdd1", MountPath: "/mnt/hdd1", GuestPath: "/mnt/felhom-drives/hdd1", Role: "user-data",
	}}, "/mnt/felhom-drives/hdd1")
	st := s.resolveBackupTargetState(context.Background())

	if st.TargetAbsent {
		t.Fatal("target `local` marked absent — the builtin root-fs storage is the NEVER-CONFIGURED " +
			"state, not a drive that went missing")
	}
	if !st.Degraded {
		t.Fatal("a backup on the system disk must still read degraded")
	}
	if msg := degradedMessageFor(st); !strings.Contains(msg, "ugyanazon a lemezen") {
		t.Errorf("the never-configured copy changed; got: %s", msg)
	}
	if st.OfferPath == "" {
		t.Error("no offer in the never-configured state — this is exactly where an offer belongs")
	}
}

// An unset tier is the same customer situation as `local`: nothing chosen yet, so it must not be
// reported as a drive that vanished.
func TestUnsetTierIsNeverConfiguredNotAbsent(t *testing.T) {
	if targetIsConfiguredDrive("") {
		t.Error("an empty target id was treated as a configured drive")
	}
	if targetIsConfiguredDrive(builtinLocalTarget) {
		t.Error("the builtin `local` was treated as a configured drive")
	}
	if !targetIsConfiguredDrive("felhom-backup") {
		t.Error("a real storage id was not treated as a configured drive")
	}
}

// ── Scenario B — healthy still renders nothing ──────────────────────────────────────────────────

func TestHealthyTargetIsNeitherDegradedNorAbsent(t *testing.T) {
	s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{{
		Name: "mentes2", MountPath: "/mnt/mentes2", GuestPath: "/mnt/felhom-drives/mentes2",
		Role: "user-data", BackupTarget: true,
	}}, "/mnt/felhom-drives/mentes2")
	st := s.resolveBackupTargetState(context.Background())

	if st.Degraded || st.TargetAbsent {
		t.Fatalf("a healthy target reported degraded=%v absent=%v", st.Degraded, st.TargetAbsent)
	}
	if msg := degradedMessageFor(st); msg != "" {
		t.Errorf("healthy produced copy %q — a working box must look normal", msg)
	}
}

// ── the copy contract ───────────────────────────────────────────────────────────────────────────

// The absent copy is VERBATIM the hub's customerMessages["backup_target_absent"]. If either side is
// reworded this fails, which is the only thing currently binding the two repos' strings together.
func TestAbsentCopyMatchesTheHubEmailWordForWord(t *testing.T) {
	const hubCopy = "A rendszermentés meghajtója nem érhető el — amíg vissza nem " +
		"csatlakoztatod, a teljes rendszermentés nem készül el."
	if backupTargetAbsentText != hubCopy {
		t.Errorf("the banner and the email now tell the customer different stories about one drive.\n"+
			" banner: %s\n email : %s", backupTargetAbsentText, hubCopy)
	}
	msg := degradedMessageFor(BackupTargetState{Known: true, Degraded: true, TargetAbsent: true})
	for _, want := range []struct{ frag, why string }{
		{"nem érhető el", "the FACT — the drive cannot be reached"},
		{"nem készül el", "the CONSEQUENCE — the full system backup does not happen"},
		{"vissza nem csatlakoztatod", "the REMEDY — reconnect it"},
	} {
		if !strings.Contains(msg, want.frag) {
			t.Errorf("absent copy is missing %s (%q); got: %s", want.why, want.frag, msg)
		}
	}
}
