package stacks

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

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

// newAppJob builds a scope="app" job (no non-app merge unit — app scope has no merge walk).
func newAppJob(srcNS, dstNS, source, target string, apps ...string) *MigrationJob {
	j := &MigrationJob{
		Scope: "app", Phase: PhaseStop, Source: source, Target: target,
		SourceNS: srcNS, TargetNS: dstNS, Apps: apps, Units: map[string]*MigUnit{},
	}
	for _, a := range apps {
		j.Units[a] = &MigUnit{App: a, State: UnitPending}
	}
	return j
}

// TestMigrate_PaperlessShape_ScopeApp is Scenario D / RP-4: a scope="app" migration of paperless-ngx
// (stack name) must copy, verify, and clean the REAL appdata dir (paperless), never appdata/paperless-ngx.
// Every leg is asserted by captured src/dst pairs and on-disk effects — never mere absence of error.
// Companion RP-4: dropping the resolveNames loop (keying by stack name) makes the copy seam receive
// appdata/paperless-ngx and every assertion below fails.
func TestMigrate_PaperlessShape_ScopeApp(t *testing.T) {
	m := newMigManager(t, "/mnt/target")
	srcNS := t.TempDir()
	dstNS := t.TempDir()
	// Real source data under the REAL dir name (paperless), plus the recovery unit.
	writeFile(t, filepath.Join(appbackup.AppDataDir(srcNS, "paperless"), "media", "doc.pdf"), "PDF")
	writeFile(t, filepath.Join(appbackup.RecoveryUnitPath(srcNS, "paperless-ngx"), "manifest.json"), "{}")

	type pair struct{ src, dst string }
	var copied, verified []pair
	m.testSeams = &migSeams{
		resolveNames: func(app string) []string {
			if app == "paperless-ngx" {
				return []string{"paperless"}
			}
			return []string{app}
		},
		stop: func(string) error { return nil },
		copy: func(_ context.Context, s, d string, _ func(int64)) error {
			copied = append(copied, pair{s, d})
			return nil
		},
		verify: func(_ context.Context, s, d string) error {
			verified = append(verified, pair{s, d})
			return nil
		},
		flipRedeploy: func(string, string) error { return nil },
	}

	// --- direct-call assertions (accounting legs) before the destructive run ---
	wantSrc := appbackup.AppDataDir(srcNS, "paperless")
	// migSourceSize must probe appdata/paperless (non-zero: the PDF exists there).
	if got := m.migSourceSize(newAppJob(srcNS, dstNS, "/s", "/t", "paperless-ngx")); got == 0 {
		t.Error("migSourceSize = 0 — it probed appdata/paperless-ngx (empty) instead of appdata/paperless")
	}
	// appDataSkipSet must contain the resolved source dir.
	skip := m.appDataSkipSet(newAppJob(srcNS, dstNS, "/s", "/t", "paperless-ngx"))
	if !skip[filepath.Clean(wantSrc)] {
		t.Errorf("skip-set = %v, want it to contain %q", skip, filepath.Clean(wantSrc))
	}
	// Collision check must probe appdata/paperless at the target. Target must be the schedulable path
	// so migValidate reaches the collision check (TargetNS stays the temp dir where the dir exists).
	writeFile(t, filepath.Join(appbackup.AppDataDir(dstNS, "paperless"), "x"), "x")
	cj := newAppJob(srcNS, dstNS, "/s", "/mnt/target", "paperless-ngx")
	if err := m.migValidate(cj); err == nil || !contains(err.Error(), "paperless-ngx") {
		t.Errorf("collision must fire on the resolved target dir, got %v", err)
	}

	// --- full pipeline run (copy → verify → cleanup) ---
	j := newAppJob(srcNS, dstNS, "/s", "/t", "paperless-ngx")
	m.runJobSync(j)
	if j.Phase != PhaseDone {
		t.Fatalf("phase = %s (err=%s), want done", j.Phase, j.Error)
	}

	wantDst := appbackup.AppDataDir(dstNS, "paperless")
	assertHasPair := func(name string, got []pair) {
		t.Helper()
		for _, p := range got {
			if p.src == wantSrc && p.dst == wantDst {
				return
			}
		}
		t.Errorf("%s seam never saw appdata/paperless: src=%q dst=%q, got %v", name, wantSrc, wantDst, got)
	}
	assertHasPair("copy", copied)
	assertHasPair("verify", verified)

	// Cleanup removed the REAL source dir.
	if pathExists(wantSrc) {
		t.Errorf("cleanup left the source appdata/paperless behind: %s", wantSrc)
	}
	// And it never probed / created appdata/paperless-ngx.
	if pathExists(appbackup.AppDataDir(srcNS, "paperless-ngx")) {
		t.Errorf("stale appdata/paperless-ngx dir should never exist")
	}
}
