package web

import (
	"context"
	"os"
	"runtime"
	"strings"
	"testing"

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

// The stub trap (RCA fix 2): a probe dir that is NOT a mounted network filesystem must exit
// not-network-fs — writability alone can never verify a share again. Seam-modeled (cross-platform);
// the real-statfs leg is TestNetProbeChild_RealLocalDirRefused below.
// Companion red-proof: remove the fstype assertion from NetProbeChild → this expects exit 5 but
// gets 0 (the stub VERIFIES) → FAIL — the exact regression this task exists to prevent.
func TestNetProbeChild_StubRefused(t *testing.T) {
	orig := netProbeFSClass
	t.Cleanup(func() { netProbeFSClass = orig })
	netProbeFSClass = func(string) string { return system.FSClassStub }

	dir := t.TempDir()
	if got := NetProbeChild(dir); got != netProbeExitNotNetFS {
		t.Fatalf("probe against a stub dir: exit = %d, want %d (not_network_fs)", got, netProbeExitNotNetFS)
	}
	// The probe file must not linger after the refusal.
	entries, err := os.ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	if len(entries) != 0 {
		t.Errorf("probe file not cleaned up after stub refusal: %v", entries)
	}
}

// After a create, an AUTOFS answer means the mount did not materialize — mounted network forms
// only pass the child (idle-trigger tolerance belongs to the deploy gate, not the probe).
func TestNetProbeChild_AutofsAfterCreateRefused(t *testing.T) {
	orig := netProbeFSClass
	t.Cleanup(func() { netProbeFSClass = orig })
	netProbeFSClass = func(string) string { return system.FSClassAutofs }

	if got := NetProbeChild(t.TempDir()); got != netProbeExitNotNetFS {
		t.Fatalf("autofs-after-create: exit = %d, want %d", got, netProbeExitNotNetFS)
	}
}

// Real-IO (linux): the platform classifier against a plain local dir — no seam. THE state the RCA
// remediation left behind must never verify again.
func TestNetProbeChild_RealLocalDirRefused(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("statfs f_type is linux-only; exercised on the Linux build server")
	}
	if got := NetProbeChild(t.TempDir()); got != netProbeExitNotNetFS {
		t.Fatalf("real local dir: exit = %d, want %d (the stub verified!)", got, netProbeExitNotNetFS)
	}
}

// The parent verdict map: exit 5 → failed category not_network_fs.
func TestNetProbeVerdict_NotNetworkFS(t *testing.T) {
	o := netProbeVerdict(netProbeExitNotNetFS, "detail")
	if o.OK || o.Category != "not_network_fs" {
		t.Fatalf("verdict = %+v, want !OK + not_network_fs", o)
	}
}

// Job-level: a stub probe verdict rolls the agent install back, registers NOTHING, and surfaces the
// §3.2 Hungarian message for not_network_fs.
func TestNetAdd_StubProbe_RollsBackNotRegistered(t *testing.T) {
	s := testServer(t)
	agent := &fakeNetAgent{addRes: okAddRes("media"), verify: agentapi.NetVerifyStatus{Phase: "done", JobID: "job-1"}}
	s.netProbeFn = func(_ context.Context, _ string) probeOutcome {
		// The real chain: child exit → verdict (the seam models the stub without a real mount).
		orig := netProbeFSClass
		defer func() { netProbeFSClass = orig }()
		netProbeFSClass = func(string) string { return system.FSClassStub }
		return netProbeVerdict(NetProbeChild(t.TempDir()), "")
	}

	if !s.startNetAdd(agent, netAddReq("media"), "NAS media") {
		t.Fatal("startNetAdd refused")
	}
	job := waitNetAdd(t, s)
	if job.Phase != netAddPhaseFailed || job.Category != "not_network_fs" {
		t.Fatalf("phase/category = %s/%s, want failed/not_network_fs", job.Phase, job.Category)
	}
	if !strings.Contains(job.Message, "nem jött létre megfelelően") {
		t.Errorf("not_network_fs Hungarian message missing, got %q", job.Message)
	}
	if got := agent.removed(); len(got) != 1 || got[0] != "media" {
		t.Errorf("stub probe must roll the agent install back: removes=%v", got)
	}
	if got := networkPathCount(s); got != 0 {
		t.Errorf("a stub share must NOT be registered (got %d paths)", got)
	}
}
