package localapi

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"testing"

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

// ---- Audit D3: the blank-format branch's anti-retarget guard ------------------------------
//
// AGENT-001 closed the classify→mkfs TOCTOU on the data-bearing branch; these tests pin the same
// guard onto the BLANK branch: the format must bind to a durable id, re-resolve it to the CURRENT
// device, and format THAT — never the mutable caller-supplied /dev path.

// Pure-function coverage of antiRetargetResolveBlank (the injected-deps sibling; no real /dev).
func TestFormatBlankPath_AntiRetarget_ReassignedDataBearingRefused(t *testing.T) {
	const durable = "byid:wwn-0xBLANK"
	dataBearing := storage.DeviceProbe{Probed: true, HasFilesystem: true, FSType: "ext4"}
	got, err := antiRetargetResolveBlank(durable,
		func(string) (string, error) { return "/dev/sdb", nil },
		func(string) (string, error) { return durable, nil },
		func(string) (storage.DeviceProbe, error) { return dataBearing, nil },
	)
	if err == nil {
		t.Fatalf("re-inspect found a DATA-BEARING disk on the blank path — must refuse, got device %q", got)
	}
	if !strings.Contains(err.Error(), "now data-bearing") {
		t.Errorf("error %q missing the now-data-bearing explanation", err.Error())
	}
}

func TestFormatBlankPath_AntiRetarget_ReassignedDifferentDiskRefused(t *testing.T) {
	const durable = "byid:wwn-0xBLANK"
	blank := storage.DeviceProbe{Probed: true}
	got, err := antiRetargetResolveBlank(durable,
		func(string) (string, error) { return "/dev/sdb", nil },
		func(string) (string, error) { return "byid:wwn-0xDIFFERENT", nil }, // node reassigned → different id
		func(string) (storage.DeviceProbe, error) { return blank, nil },
	)
	if err == nil || !strings.Contains(err.Error(), "durable-id mismatch") {
		t.Fatalf("want durable-id mismatch refusal, got device %q err %v", got, err)
	}
}

func TestFormatBlankPath_AntiRetarget_UnresolvableRefused(t *testing.T) {
	blank := storage.DeviceProbe{Probed: true}
	// Unresolvable durable id → refuse.
	if got, err := antiRetargetResolveBlank("byid:wwn-0xGONE",
		func(string) (string, error) { return "", errors.New("gone") },
		func(string) (string, error) { return "byid:wwn-0xGONE", nil },
		func(string) (storage.DeviceProbe, error) { return blank, nil },
	); err == nil || !strings.Contains(err.Error(), "no longer resolves") {
		t.Fatalf("want no-longer-resolves refusal, got device %q err %v", got, err)
	}
	// Empty durable id (nothing to bind) → refuse: a path-only blank format is what the guard prevents.
	if got, err := antiRetargetResolveBlank("",
		func(string) (string, error) { return "/dev/sdb", nil },
		func(string) (string, error) { return "", nil },
		func(string) (storage.DeviceProbe, error) { return blank, nil },
	); err == nil || !strings.Contains(err.Error(), "path-only") {
		t.Fatalf("want path-only refusal on empty durable id, got device %q err %v", got, err)
	}
}

func TestFormatBlankPath_AntiRetarget_SameBlankProceeds(t *testing.T) {
	const durable = "byid:wwn-0xBLANK"
	blank := storage.DeviceProbe{Probed: true}
	got, err := antiRetargetResolveBlank(durable,
		func(string) (string, error) { return "/dev/sdb", nil },
		func(string) (string, error) { return durable, nil },
		func(string) (storage.DeviceProbe, error) { return blank, nil },
	)
	if err != nil {
		t.Fatalf("same still-blank disk must proceed, got refusal: %v", err)
	}
	if got != "/dev/sdb" {
		t.Fatalf("device = %q, want the re-resolved /dev/sdb", got)
	}
}

// ---- handler wiring: the blank branch formats the RE-RESOLVED device, never req.Device ----

// The red-proof companion vs the pre-fix handler: pre-fix, the blank branch called
// startFormatDetached(req.Device, ...) directly, so the format hit the caller path even when the
// re-resolve seam says the durable id now lives at a different node. Post-fix, mkfs must run on the
// re-resolved device.
func TestFormat_Blank_FormatsReresolvedDeviceNotCallerPath(t *testing.T) {
	d := &fakeDiskOps{probe: storage.DeviceProbe{Probed: true}} // blank
	srv := newDiskServerRaw(t, d, &fakeGate{}, nil, nil)
	var boundID string
	srv.reresolveBlank = func(_ context.Context, durableID string) (string, error) {
		boundID = durableID
		return "/dev/sdz", nil // the durable id's CURRENT node differs from the caller path
	}
	h := srv.Handler()

	w := do(t, h, "POST", "/disks/format", "A", `{"device":"/dev/sdb","fstype":"ext4"}`)
	if w.Code != http.StatusOK {
		t.Fatalf("blank format: got %d want 200 (%s)", w.Code, w.Body.String())
	}
	if boundID != "byid:wwn-sdb" {
		t.Errorf("blank format bound durable id %q, want the one derived from req.Device", boundID)
	}
	got := d.formatted()
	if len(got) != 1 || got[0] != "/dev/sdz" {
		t.Fatalf("mkfs ran on %v — must run on the RE-RESOLVED device /dev/sdz, never the caller path /dev/sdb", got)
	}
}

// A blank-branch anti-retarget refusal (device changed in the window) must refuse 409 with ZERO mkfs.
func TestFormat_Blank_ReresolveRefusalNoMkfs(t *testing.T) {
	d := &fakeDiskOps{probe: storage.DeviceProbe{Probed: true}} // blank
	srv := newDiskServerRaw(t, d, &fakeGate{}, nil, nil)
	srv.reresolveBlank = func(_ context.Context, durableID string) (string, error) {
		return "", fmt.Errorf("%s is now data-bearing (target changed since blank inspection) — refusing", "/dev/sdb")
	}
	h := srv.Handler()

	w := do(t, h, "POST", "/disks/format", "A", `{"device":"/dev/sdb","fstype":"ext4"}`)
	if w.Code != http.StatusConflict {
		t.Fatalf("refused blank format: got %d want 409 (%s)", w.Code, w.Body.String())
	}
	if got := d.formatted(); len(got) != 0 {
		t.Fatalf("mkfs WAS called after an anti-retarget refusal: %v", got)
	}
}

// A device with no durable id cannot be bound → the blank format is refused (no path-only formats).
func TestFormat_Blank_NoDurableIDRefused(t *testing.T) {
	d := &fakeDiskOps{probe: storage.DeviceProbe{Probed: true}} // blank
	srv := newDiskServerRaw(t, d, &fakeGate{}, nil, nil)
	srv.deviceDurableID = func(string) (string, error) { return "", errors.New("no by-id/by-uuid entry") }
	h := srv.Handler()

	w := do(t, h, "POST", "/disks/format", "A", `{"device":"/dev/sdb","fstype":"ext4"}`)
	if w.Code != http.StatusConflict {
		t.Fatalf("unbindable blank format: got %d want 409 (%s)", w.Code, w.Body.String())
	}
	if got := d.formatted(); len(got) != 0 {
		t.Fatalf("mkfs WAS called with no durable-id binding: %v", got)
	}
}
