/**
 * Integration verification: parallel directory writes go to the correct .sf
 *
 * This verifies that after the fix, when code resolves paths inside a worktree
 * with symlinked .sf, writes target the project-level .sf (through symlink)
 * rather than the user-level ~/.sf.
 *
 * Covers:
 * 1. resolveProjectRoot() returns the real project, not ~
 * 2. sfRoot() from the resolved project root finds project .sf, not ~/.sf
 * 3. The parallel/ directory would be created under project .sf
 * 4. session-status writes target the correct location
 * 5. orchestrator.json would be written to project .sf
 * 6. assertSafeDirectory blocks ~ as a project root
 */

import { execSync } from "node:child_process";
import {
	existsSync,
	mkdirSync,
	mkdtempSync,
	readFileSync,
	realpathSync,
	symlinkSync,
	writeFileSync,
} from "node:fs";
import { homedir, tmpdir } from "node:os";
import { join, resolve } from "node:path";

// ── Fixed functions (from worktree.ts after fix) ─────────────────────────

function findWorktreeSegment(normalizedPath) {
	const directMarker = "/.sf/worktrees/";
	const idx = normalizedPath.indexOf(directMarker);
	if (idx !== -1) {
		return { sfIdx: idx, afterWorktrees: idx + directMarker.length };
	}
	const symlinkRe = /\/\.sf\/projects\/[a-f0-9]+\/worktrees\//;
	const match = normalizedPath.match(symlinkRe);
	if (match && match.index !== undefined) {
		return {
			sfIdx: match.index,
			afterWorktrees: match.index + match[0].length,
		};
	}
	return null;
}

function resolveProjectRootFromGitFile(worktreePath) {
	try {
		let dir = worktreePath;
		for (let i = 0; i < 10; i++) {
			const gitPath = join(dir, ".git");
			if (existsSync(gitPath)) {
				const content = readFileSync(gitPath, "utf8").trim();
				if (content.startsWith("gitdir: ")) {
					const gitDir = resolve(dir, content.slice(8));
					const dotGitDir = resolve(gitDir, "..", "..");
					if (
						dotGitDir.endsWith(".git") ||
						dotGitDir.endsWith(".git/") ||
						dotGitDir.endsWith(".git\\")
					) {
						return resolve(dotGitDir, "..");
					}
					const commonDirPath = join(gitDir, "commondir");
					if (existsSync(commonDirPath)) {
						const commonDir = readFileSync(commonDirPath, "utf8").trim();
						const resolvedCommonDir = resolve(gitDir, commonDir);
						return resolve(resolvedCommonDir, "..");
					}
				}
				break;
			}
			const parent = resolve(dir, "..");
			if (parent === dir) break;
			dir = parent;
		}
	} catch {}
	return null;
}

function normalizePathForCompare(path) {
	let normalized;
	try {
		normalized = realpathSync(path);
	} catch {
		normalized = resolve(path);
	}
	const slashed = normalized.replaceAll("\\", "/");
	const trimmed = slashed.replace(/\/+$/, "");
	return trimmed || "/";
}

function resolveProjectRoot(basePath) {
	// Layer 1: If the coordinator passed the real project root, use it.
	if (process.env.SF_PROJECT_ROOT || process.env.SF_PROJECT_ROOT) {
		return process.env.SF_PROJECT_ROOT || process.env.SF_PROJECT_ROOT;
	}

	const normalizedPath = basePath.replaceAll("\\", "/");
	const seg = findWorktreeSegment(normalizedPath);
	if (!seg) return basePath;

	const sepChar = basePath.includes("\\") ? "\\" : "/";
	const sfMarker = `${sepChar}.sf${sepChar}`;
	const sfIdx = basePath.indexOf(sfMarker);
	const candidate =
		sfIdx !== -1 ? basePath.slice(0, sfIdx) : basePath.slice(0, seg.sfIdx);
	const sfHome = normalizePathForCompare(
		process.env.SF_HOME || process.env.SF_HOME || join(homedir(), ".sf"),
	);
	const candidateSfPath = normalizePathForCompare(join(candidate, ".sf"));
	if (candidateSfPath === sfHome || candidateSfPath.startsWith(sfHome + "/")) {
		const realRoot = resolveProjectRootFromGitFile(basePath);
		if (realRoot) return realRoot;
		return basePath;
	}
	return candidate;
}

// Simplified sfRoot — matches paths.ts probeSfRoot logic
function sfRoot(basePath) {
	const local = join(basePath, ".sf");
	if (existsSync(local)) return local;
	return local; // fallback
}

// Simplified validateDirectory — matches validate-directory.ts
function validateDirectory(dirPath) {
	let resolved;
	try {
		resolved = realpathSync(resolve(dirPath));
	} catch {
		resolved = resolve(dirPath);
	}
	let normalized = resolved.replace(/[/\\]+$/, "");
	if (normalized === "") normalized = "/";

	let resolvedHome;
	try {
		resolvedHome = realpathSync(resolve(homedir())).replace(/[/\\]+$/, "");
	} catch {
		resolvedHome = resolve(homedir()).replace(/[/\\]+$/, "");
	}

	if (normalized === resolvedHome) {
		return {
			safe: false,
			severity: "blocked",
			reason: `Refusing to run in home directory: ${normalized}`,
		};
	}
	return { safe: true, severity: "ok" };
}

// ── Setup ────────────────────────────────────────────────────────────────

const HASH = "abc123def456";
const TEST_ROOT = mkdtempSync(join(tmpdir(), "sf-verify-integration-"));
const USER_SF =
	process.env.SF_HOME || process.env.SF_HOME || join(TEST_ROOT, ".sf");
const USER_HOME = homedir();
const PROJECT_SF_STORAGE = `${USER_SF}/projects/${HASH}`;
const PROJECT_DIR = mkdtempSync(join(tmpdir(), "myproject-"));
const PROJECT_SF_LINK = `${PROJECT_DIR}/.sf`;
const PROJECT_REAL = normalizePathForCompare(PROJECT_DIR);
let PROJECT_STORAGE_REAL = "";

process.env.SF_HOME = USER_SF;
process.env.SF_HOME = USER_SF;

console.log("=== Setup ===\n");

mkdirSync(`${PROJECT_SF_STORAGE}/worktrees`, { recursive: true });
mkdirSync(`${PROJECT_SF_STORAGE}/milestones`, { recursive: true });
mkdirSync(PROJECT_DIR, { recursive: true });
symlinkSync(PROJECT_SF_STORAGE, PROJECT_SF_LINK);
PROJECT_STORAGE_REAL = normalizePathForCompare(PROJECT_SF_STORAGE);

execSync("git init -b main", { cwd: PROJECT_DIR, stdio: "pipe" });
execSync('git config user.name "Test"', { cwd: PROJECT_DIR, stdio: "pipe" });
execSync('git config user.email "test@test.com"', {
	cwd: PROJECT_DIR,
	stdio: "pipe",
});
writeFileSync(join(PROJECT_DIR, "README.md"), "hello\n");
execSync("git add -A && git commit -m init", {
	cwd: PROJECT_DIR,
	stdio: "pipe",
});
execSync("git worktree add .sf/worktrees/M001 -b worktree/M001", {
	cwd: PROJECT_DIR,
	stdio: "pipe",
});
console.log("Created project with symlinked .sf and real git worktree\n");

let passed = 0;
let failed = 0;
function test(name, actual, expected) {
	if (actual === expected) {
		console.log(`  ✅ ${name}`);
		passed++;
	} else {
		console.log(
			`  ❌ ${name}\n     Expected: ${expected}\n     Got:      ${actual}`,
		);
		failed++;
	}
}

// ── Simulate worker environment ──────────────────────────────────────────

process.chdir(`${PROJECT_DIR}/.sf/worktrees/M001`);
const workerCwd = process.cwd(); // Resolves symlinks → /root/.sf/projects/.../worktrees/M001

console.log("=== Test 1: resolveProjectRoot returns real project ===\n");
console.log(`  Worker cwd (resolved): ${workerCwd}`);

const projectRoot = resolveProjectRoot(workerCwd);
console.log(`  Resolved project root: ${projectRoot}`);
test("resolveProjectRoot returns real project root", projectRoot, PROJECT_REAL);
test(
	"resolveProjectRoot does NOT return home dir",
	projectRoot !== USER_HOME,
	true,
);

console.log("\n=== Test 2: sfRoot finds project .sf ===\n");

const sfDir = sfRoot(projectRoot);
console.log(`  sfRoot result: ${sfDir}`);
test("sfRoot points to project .sf", sfDir, `${PROJECT_REAL}/.sf`);

// Verify it's a symlink to the right place
const sfDirReal = realpathSync(sfDir);
console.log(`  sfRoot resolves to: ${sfDirReal}`);
test("sfRoot resolves to project storage", sfDirReal, PROJECT_STORAGE_REAL);
test(
	"sfRoot does NOT resolve to user-level ~/.sf",
	sfDirReal !== USER_SF,
	true,
);

console.log("\n=== Test 3: parallel/ directory targets project .sf ===\n");

const parallelDir = join(sfDir, "parallel");
console.log(`  Parallel dir would be: ${parallelDir}`);
const parallelReal = join(sfDirReal, "parallel");
console.log(`  Resolves physically to: ${parallelReal}`);
test(
	"parallel dir is under project .sf",
	parallelDir.startsWith(PROJECT_REAL),
	true,
);
test(
	"parallel dir is NOT under ~/.sf root",
	!parallelDir.startsWith(USER_SF) ||
		parallelDir.startsWith(`${USER_SF}/projects/`),
	true,
);

// Actually create it and verify
mkdirSync(parallelDir, { recursive: true });
test("parallel dir was created", existsSync(parallelDir), true);
test(
	"parallel dir physically exists in project storage",
	existsSync(parallelReal),
	true,
);

// Write a session status file
const statusFile = join(parallelDir, "M001.status.json");
writeFileSync(
	statusFile,
	JSON.stringify({ milestoneId: "M001", pid: 12345, state: "running" }),
);
test(
	"session status file written to project parallel/",
	existsSync(statusFile),
	true,
);

console.log("\n=== Test 4: orchestrator.json targets project .sf ===\n");

const orchestratorPath = join(sfDir, "orchestrator.json");
console.log(`  orchestrator.json would be at: ${orchestratorPath}`);
writeFileSync(orchestratorPath, JSON.stringify({ active: true }));
test(
	"orchestrator.json written to project .sf",
	existsSync(orchestratorPath),
	true,
);

// Verify nothing leaked to user-level ~/.sf root
const userParallelDir = join(USER_SF, "parallel");
const userOrchestratorPath = join(USER_SF, "orchestrator.json");
test(
	"NO parallel/ dir at user-level ~/.sf root",
	!existsSync(userParallelDir),
	true,
);
test(
	"NO orchestrator.json at user-level ~/.sf root",
	!existsSync(userOrchestratorPath),
	true,
);

console.log("\n=== Test 5: validateDirectory blocks ~ as project root ===\n");

const homeValidation = validateDirectory(USER_HOME);
test("validateDirectory blocks home dir", homeValidation.safe, false);
test(
	"validateDirectory blocks with 'blocked' severity",
	homeValidation.severity,
	"blocked",
);

const projectValidation = validateDirectory(PROJECT_DIR);
test("validateDirectory allows project dir", projectValidation.safe, true);

console.log("\n=== Test 6: SF_PROJECT_ROOT env var path ===\n");

process.env.SF_PROJECT_ROOT = PROJECT_DIR;
const envResult = resolveProjectRoot(workerCwd);
test("SF_PROJECT_ROOT short-circuits resolution", envResult, PROJECT_DIR);
delete process.env.SF_PROJECT_ROOT;
delete process.env.SF_PROJECT_ROOT;

console.log("\n=== Test 7: Non-worktree paths unaffected ===\n");

test(
	"Regular project path unchanged",
	resolveProjectRoot("/some/project"),
	"/some/project",
);
test(
	"Direct worktree layout still works",
	resolveProjectRoot("/foo/.sf/worktrees/M001"),
	"/foo",
);

// ── Summary ──────────────────────────────────────────────────────────────

console.log(`\n${"=".repeat(60)}`);
console.log(`\nResults: ${passed} passed, ${failed} failed`);
if (failed > 0) {
	console.log("\n🔴 INTEGRATION VERIFICATION FAILED");
	process.exit(1);
} else {
	console.log("\n✅ ALL INTEGRATION TESTS PASSED");
	console.log("  - resolveProjectRoot returns real project, not ~");
	console.log("  - sfRoot finds project .sf through symlink");
	console.log("  - parallel/ dir created in project .sf, not ~/.sf");
	console.log("  - session status writes land in correct location");
	console.log("  - orchestrator.json lands in correct location");
	console.log("  - validateDirectory blocks ~ as fallback safety net");
	console.log("  - SF_PROJECT_ROOT env var works as primary layer");
	console.log("  - Non-worktree paths are unaffected by the fix");
	process.exit(0);
}
