package storage

import (
	"context"
	"os"
	"path/filepath"
	"testing"
)

// fakeIntent is a minimal IntentReader for the registry tests.
type fakeIntent struct{ m map[string]DriveIntent }

func (f *fakeIntent) Get(durableID string) DriveIntent { return f.m[durableID] }

func writeUnit(t *testing.T, dir, name, content string) {
	t.Helper()
	if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
		t.Fatalf("write unit %s: %v", name, err)
	}
}

func TestRegistryKnownTargets(t *testing.T) {
	dir := t.TempDir()
	writeUnit(t, dir, "mnt-a.mount", renderMountUnit(MountSpec{Name: "felhom-a", UUID: "UUID-A", Where: "/mnt/felhom-a", FSType: "ext4"}))
	writeUnit(t, dir, "mnt-b.mount", renderMountUnit(MountSpec{Name: "felhom-b", UUID: "UUID-B", Where: "/mnt/felhom-b", FSType: "ext4"}))
	writeUnit(t, dir, "other.mount", "[Mount]\nWhat=/dev/x\nWhere=/mnt/x\n") // not one of ours → ignored

	// felhom-a enrolled, felhom-b has NO intent (new).
	intent := &fakeIntent{m: map[string]DriveIntent{"uuid:UUID-A": IntentEnrolled}}
	r := NewRegistryKnownTargets(dir, intent, quietLogger())

	got, err := r.Known(context.Background())
	if err != nil {
		t.Fatalf("Known: %v", err)
	}
	if len(got) != 1 || got[0].Name != "felhom-a" {
		t.Fatalf("want only felhom-a (enrolled); new felhom-b excluded, non-felhom ignored — got %+v", got)
	}
	kt := got[0]
	if kt.DurableID != "uuid:UUID-A" || kt.UUID != "UUID-A" || !kt.MountBacked || kt.MountPath != "/mnt/felhom-a" {
		t.Fatalf("KnownTarget fields wrong: %+v", kt)
	}

	// An EJECTED drive is still tracked (known) — the watchdog's IntentReader gate prevents remount.
	intent.m["uuid:UUID-B"] = IntentEjected
	got2, _ := r.Known(context.Background())
	if len(got2) != 2 {
		t.Fatalf("ejected drive must still be a Known target: %+v", got2)
	}
}

// The decoupling red-proof: with NO PVE storage for a drive, the OLD Observe-based Known() misses it,
// while the registry+units provider tracks it. This is exactly the 3b-fix class the decoupling closes.
func TestRegistryVsObserve_RedProof(t *testing.T) {
	dir := t.TempDir()
	writeUnit(t, dir, "mnt-a.mount", renderMountUnit(MountSpec{Name: "felhom-a", UUID: "UUID-A", Where: "/mnt/felhom-a", FSType: "ext4"}))
	intent := &fakeIntent{m: map[string]DriveIntent{"uuid:UUID-A": IntentEnrolled}}

	// OLD path: Observer with an API that has NO storages → Known() is empty (drive invisible).
	obs := NewObserver(&fakeStorageAPI{node: "n"}, &fakeHostReader{}, nil, quietLogger())
	oldKnown, _ := obs.Known(context.Background())
	if len(oldKnown) != 0 {
		t.Fatalf("precondition: Observe-based Known should be empty with no PVE storage, got %+v", oldKnown)
	}

	// NEW path: the registry provider tracks the drive from unit + intent, no PVE storage needed.
	newKnown, _ := NewRegistryKnownTargets(dir, intent, quietLogger()).Known(context.Background())
	if len(newKnown) != 1 || newKnown[0].Name != "felhom-a" {
		t.Fatalf("registry provider must track the registry-only drive: %+v", newKnown)
	}
}

func TestReconcileExistingDrives_Idempotent(t *testing.T) {
	dir := t.TempDir()
	writeUnit(t, dir, "mnt-a.mount", renderMountUnit(MountSpec{Name: "felhom-a", UUID: "UUID-A", Where: "/mnt/felhom-a", FSType: "ext4"}))
	store, err := OpenIntentStore(filepath.Join(t.TempDir(), "intents.json"))
	if err != nil {
		t.Fatalf("intent store: %v", err)
	}
	mounts := []Mount{{Device: "/dev/disk/by-uuid/UUID-A", MountPoint: "/mnt/felhom-a"}}

	// First reconcile: an un-recorded mounted drive → becomes enrolled.
	ReconcileExistingDrives(dir, mounts, store, quietLogger())
	if store.Get("uuid:UUID-A") != IntentEnrolled {
		t.Fatalf("reconcile must record the mounted drive enrolled, got %q", store.Get("uuid:UUID-A"))
	}
	// Idempotent: a re-run doesn't change/downgrade it.
	ReconcileExistingDrives(dir, mounts, store, quietLogger())
	if store.Get("uuid:UUID-A") != IntentEnrolled {
		t.Fatalf("re-run must be a no-op, got %q", store.Get("uuid:UUID-A"))
	}
	// An UNMOUNTED drive is NOT auto-enrolled by the migration.
	writeUnit(t, dir, "mnt-c.mount", renderMountUnit(MountSpec{Name: "felhom-c", UUID: "UUID-C", Where: "/mnt/felhom-c", FSType: "ext4"}))
	ReconcileExistingDrives(dir, mounts, store, quietLogger())
	if store.Get("uuid:UUID-C") != IntentNew {
		t.Fatalf("unmounted drive must NOT be migrated, got %q", store.Get("uuid:UUID-C"))
	}
}
