package localapi

import (
	"context"
	"testing"

	"gitea.dooplex.hu/admin/felhom-agent/internal/storage"
)

// fakeNetOpsReassert is fakeNetOps plus the type-asserted reassert capability.
type fakeNetOpsReassert struct {
	fakeNetOps
	reassertN int
	rearmed   []string
	results   []storage.NetReassertResult
}

func (f *fakeNetOpsReassert) ReassertNetworkAutomounts(context.Context) []storage.NetReassertResult {
	f.reassertN++
	return f.results
}

func (f *fakeNetOpsReassert) RearmNetworkAutomount(_ context.Context, where string) error {
	f.rearmed = append(f.rearmed, where)
	return nil
}

// seeingAttacher records GuestSeesMount calls and controls per-vmid running state.
type seeingAttacher struct {
	fakeGuestAttacher
	running map[int]bool
	seen    []struct {
		vmid int
		path string
	}
	sees bool
}

func (s *seeingAttacher) GuestBootID(_ context.Context, vmid int) string {
	if s.running[vmid] {
		return "boot-1"
	}
	return ""
}

func (s *seeingAttacher) GuestSeesMount(_ context.Context, vmid int, path string) bool {
	s.seen = append(s.seen, struct {
		vmid int
		path string
	}{vmid, path})
	return s.sees
}

// The daemon leg: reassert runs once, then visibility is verified per RUNNING guest per acted
// share; stopped guests are skipped entirely.
func TestReassertNetworkMounts_VerifiesRunningGuestsOnly(t *testing.T) {
	n := &fakeNetOpsReassert{results: []storage.NetReassertResult{
		{Name: "media", Where: "/mnt/felhom-drives/media", Action: storage.NetReassertRearmed},
	}}
	srv := newNetServer(t, n, t.TempDir())
	ga := &seeingAttacher{running: map[int]bool{8200: true, 9300: false}, sees: true}
	srv.guestAttach = ga
	gb := tempBindStore(t)
	if err := gb.Record(8200, "uuid:aaa"); err != nil {
		t.Fatal(err)
	}
	if err := gb.Record(9300, "uuid:bbb"); err != nil {
		t.Fatal(err)
	}
	srv.guestBinds = gb

	srv.ReassertNetworkMounts(context.Background())

	if n.reassertN != 1 {
		t.Fatalf("host-side reassert must run exactly once (host-global), ran %d", n.reassertN)
	}
	if len(ga.seen) != 1 || ga.seen[0].vmid != 8200 || ga.seen[0].path != "/mnt/felhom-drives/media" {
		t.Fatalf("visibility verify must cover the RUNNING guest only, got %+v", ga.seen)
	}
}

// A guest that does NOT see the share after reassert triggers a re-arm + re-verify (F11
// matrix-correction), stays non-fatal, and the pass still ran. sees=false throughout → the re-verify
// also misses, logs STILL-not-visible, never errors.
func TestReassertNetworkMounts_InvisibleShareNonFatal(t *testing.T) {
	n := &fakeNetOpsReassert{results: []storage.NetReassertResult{
		{Name: "media", Where: "/mnt/felhom-drives/media", Action: storage.NetReassertRearmed},
	}}
	srv := newNetServer(t, n, t.TempDir())
	ga := &seeingAttacher{running: map[int]bool{8200: true}, sees: false}
	srv.guestAttach = ga
	gb := tempBindStore(t)
	if err := gb.Record(8200, "uuid:aaa"); err != nil {
		t.Fatal(err)
	}
	srv.guestBinds = gb

	srv.ReassertNetworkMounts(context.Background()) // must not panic / abort

	// A blind guest must trigger exactly one re-arm, and GuestSeesMount is checked twice (before + after).
	if n.reassertN != 1 || len(n.rearmed) != 1 || len(ga.seen) != 2 {
		t.Fatalf("blind guest must re-arm once + re-verify: reassert=%d rearmed=%v seen=%+v", n.reassertN, n.rearmed, ga.seen)
	}
}

// A netStorage surface WITHOUT the reassert capability (the lean interface, e.g. plain fakes) is a
// clean no-op — the type-assert gate.
func TestReassertNetworkMounts_NoCapabilityNoOp(t *testing.T) {
	srv := newNetServer(t, &fakeNetOps{}, t.TempDir())
	srv.ReassertNetworkMounts(context.Background()) // must not panic
}
