// The readiness handshake makes ownership and confirmation races deterministic.
// E2E cannot pause Go's apply call between helper capture and authorization.
package storage

import (
	"albedo/cli/internal/daemon"
	"context"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestMaintenanceOwnershipAndRelease(t *testing.T) {
	for _, ending := range []string{"success", "cancel", "helper killed", "input EOF", "replacement", "vacuum failure", "deletion failure", "no free pages"} {
		t.Run(ending, func(t *testing.T) {
			if ending == "deletion failure" && os.Geteuid() == 0 {
				t.Skip("root can unlink files in a directory without write permission")
			}
			home := t.TempDir()
			directory := filepath.Join(home, "candidates")
			if err := os.Mkdir(directory, 0700); err != nil {
				t.Fatal(err)
			}
			path := filepath.Join(directory, "candidate")
			if err := os.WriteFile(path, []byte("data"), 0600); err != nil {
				t.Fatal(err)
			}
			ctx, cancel := context.WithCancel(t.Context())
			defer cancel()
			helper, err := startMaintenance(ctx, home, []daemon.StorageFile{{Path: path, Bytes: 4}})
			if err != nil {
				t.Fatal(err)
			}
			defer helper.close()
			if other, acquireErr := startMaintenance(t.Context(), home, nil); acquireErr == nil {
				other.close()
				t.Fatal("second cleanup acquired ownership")
			} else if !strings.Contains(acquireErr.Error(), "storage is in use") {
				t.Fatal(acquireErr)
			}
			switch ending {
			case "success":
				_, err = helper.apply(ctx, false, daemon.StorageReport{})
			case "cancel":
				cancel()
				_, err = helper.apply(ctx, false, daemon.StorageReport{})
				if !errors.Is(err, context.Canceled) {
					t.Fatalf("lost cancellation: %v", err)
				}
			case "helper killed":
				if err = helper.command.Process.Kill(); err != nil {
					t.Fatal(err)
				}
				_, err = helper.apply(ctx, false, daemon.StorageReport{})
			case "input EOF":
				if err = helper.input.Close(); err != nil {
					t.Fatal(err)
				}
				err = helper.command.Wait()
				helper.waited = true
			case "replacement":
				replacement := filepath.Join(home, "replacement")
				if err = os.WriteFile(replacement, []byte("else"), 0600); err != nil {
					t.Fatal(err)
				}
				if err = os.Rename(replacement, path); err != nil {
					t.Fatal(err)
				}
				_, err = helper.apply(ctx, false, daemon.StorageReport{})
			case "deletion failure":
				if err = os.Chmod(directory, 0500); err != nil {
					t.Fatal(err)
				}
				defer os.Chmod(directory, 0700)
				_, err = helper.apply(ctx, false, daemon.StorageReport{})
			case "vacuum failure":
				_, err = helper.apply(ctx, true, daemon.StorageReport{Database: 4096, DB: daemon.StorageDatabase{FreePages: 1}})
			case "no free pages":
				var result CleanupResult
				result, err = helper.apply(ctx, true, daemon.StorageReport{Database: 4096})
				if !result.VacuumSkipped || result.Vacuumed {
					t.Fatalf("unexpected vacuum result: %+v", result)
				}
			}
			wantFailure := ending == "cancel" || ending == "helper killed" || ending == "replacement" || ending == "vacuum failure" || ending == "deletion failure"
			if (err != nil) != wantFailure {
				t.Fatalf("%s: %v", ending, err)
			}
			helper.close()
			deleted := ending == "success" || ending == "vacuum failure" || ending == "no free pages"
			_, statErr := os.Lstat(path)
			if os.IsNotExist(statErr) != deleted {
				t.Fatalf("unexpected candidate state: %v", statErr)
			}
			next, err := startMaintenance(t.Context(), home, nil)
			if err != nil {
				t.Fatalf("ownership not released: %v", err)
			}
			next.close()
		})
	}
}

func TestCleanupRequiresOwnershipBeforeRevalidation(t *testing.T) {
	home := t.TempDir()
	service := testService(home)
	plan, err := service.PlanCleanup(t.Context(), CleanupOptions{Backups: true})
	if err != nil {
		t.Fatal(err)
	}
	helper, err := startMaintenance(t.Context(), home, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer helper.close()
	// Revalidation would fail on this directory. Contention must win first.
	if err := os.Mkdir(filepath.Join(home, "albedo.sqlite"), 0700); err != nil {
		t.Fatal(err)
	}
	if _, err := service.ApplyCleanup(t.Context(), plan); err == nil || !strings.Contains(err.Error(), "storage is in use") {
		t.Fatalf("did not acquire before report: %v", err)
	}
}
