/**
 * Reproduction: Parallel Worktree Path Resolution Escapes to Home Directory
 *
 * This script reproduces the bug where resolveProjectRoot() returns the
 * user's home directory (~) when the project .sf is a symlink into
 * ~/.sf/projects/<hash> and worktree isolation is enabled.
 *
 * Layout mimics pi's default:
 *   /root/.sf/projects/<hash>/          ← user-level SF storage
 *   /tmp/myproject/.sf → symlink to ↑   ← project's .sf
 *   /tmp/myproject/.sf/worktrees/M001/  ← worktree (logical path through symlink)
 *
 * When a worker spawns with cwd = /tmp/myproject/.sf/worktrees/M001,
 * process.cwd() resolves symlinks → /root/.sf/projects/<hash>/worktrees/M001.
 * findWorktreeSegment() then matches /.sf/ at the WRONG boundary (the
 * user-level ~/.sf), causing resolveProjectRoot() to return /root (home dir).
 */

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

// ── Reproduce the exact functions from worktree.ts ──────────────────────

function findWorktreeSegment(normalizedPath) {
	// Direct layout: /.sf/worktrees/<name>
	const directMarker = "/.sf/worktrees/";
	const idx = normalizedPath.indexOf(directMarker);
	if (idx !== -1) {
		return { sfIdx: idx, afterWorktrees: idx + directMarker.length };
	}
	// Symlink-resolved layout: /.sf/projects/<hash>/worktrees/<name>
	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 resolveProjectRoot(basePath) {
	const normalizedPath = basePath.replaceAll("\\", "/");
	const seg = findWorktreeSegment(normalizedPath);
	if (!seg) return basePath;
	// Return the original path up to the /.sf/ boundary
	const sep = basePath.includes("\\") ? "\\" : "/";
	const sfMarker = `${sep}.sf${sep}`;
	const sfIdx = basePath.indexOf(sfMarker);
	if (sfIdx !== -1) return basePath.slice(0, sfIdx);
	return basePath.slice(0, seg.sfIdx);
}

// ── Set up the filesystem layout ────────────────────────────────────────

const HASH = "abc123def456";
const TEST_ROOT = mkdtempSync(join(tmpdir(), "sf-repro-"));
const USER_SF = 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`;

console.log("=== Setting up filesystem layout ===\n");

// 1. Create user-level SF structure
mkdirSync(`${PROJECT_SF_STORAGE}/worktrees/M001`, { recursive: true });
mkdirSync(`${PROJECT_SF_STORAGE}/milestones`, { recursive: true });
console.log(`Created: ${PROJECT_SF_STORAGE}/worktrees/M001`);

// 2. Create project directory
mkdirSync(PROJECT_DIR, { recursive: true });
console.log(`Created: ${PROJECT_DIR}`);

// 3. Create symlink: project/.sf → user-level storage
symlinkSync(PROJECT_SF_STORAGE, PROJECT_SF_LINK);
console.log(`Symlink: ${PROJECT_SF_LINK} → ${PROJECT_SF_STORAGE}`);

// 4. Init git in project dir
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",
});
execSync("git commit --allow-empty -m init", {
	cwd: PROJECT_DIR,
	stdio: "pipe",
});
console.log(`Git init: ${PROJECT_DIR}`);

console.log("\n=== Path Resolution Tests ===\n");

// ── Test 1: Logical path (through symlink) ──────────────────────────────

const logicalPath = `${PROJECT_DIR}/.sf/worktrees/M001`;
console.log(`Test 1: Logical path (through symlink)`);
console.log(`  Input:    ${logicalPath}`);
console.log(`  Expected: ${PROJECT_DIR}`);
const result1 = resolveProjectRoot(logicalPath);
console.log(`  Got:      ${result1}`);
console.log(
	`  Status:   ${result1 === PROJECT_DIR ? "✅ PASS" : "❌ FAIL — BUG NOT TRIGGERED (logical path)"}`,
);

// ── Test 2: Resolved path (what process.cwd() returns) ──────────────────

const resolvedPath = realpathSync(logicalPath);
console.log(
	`\nTest 2: Resolved path (what process.cwd() returns after chdir to worktree)`,
);
console.log(`  Input:    ${resolvedPath}`);
console.log(`  Expected: ${PROJECT_DIR}`);
const result2 = resolveProjectRoot(resolvedPath);
console.log(`  Got:      ${result2}`);
const isBuggy = result2 !== PROJECT_DIR;
console.log(
	`  Status:   ${isBuggy ? "🐛 BUG REPRODUCED — resolves to wrong directory!" : "✅ PASS"}`,
);

// ── Test 3: Simulate what actually happens in a worker ──────────────────

console.log(`\nTest 3: Simulating worker process.cwd() resolution`);
process.chdir(logicalPath);
const workerCwd = process.cwd(); // This resolves symlinks!
console.log(`  chdir to: ${logicalPath}`);
console.log(`  cwd():    ${workerCwd}`);
console.log(`  Expected project root: ${PROJECT_DIR}`);
const result3 = resolveProjectRoot(workerCwd);
console.log(`  resolveProjectRoot():  ${result3}`);
const workerBuggy = result3 !== PROJECT_DIR;
console.log(
	`  Status:   ${workerBuggy ? "🐛 BUG REPRODUCED — worker would use wrong project root!" : "✅ PASS"}`,
);

// ── Test 4: Show the cascade ────────────────────────────────────────────

if (workerBuggy) {
	console.log(`\n=== Cascade Analysis ===\n`);
	console.log(`The worker thinks project root is: ${result3}`);
	console.log(`It would look for .sf at:         ${result3}/.sf`);
	console.log(
		`That path exists:                   ${existsSync(join(result3, ".sf"))}`,
	);

	if (existsSync(join(result3, ".sf"))) {
		const resolvedSf = realpathSync(join(result3, ".sf"));
		console.log(`It resolves to:                    ${resolvedSf}`);
		console.log(`\nThis is the USER-LEVEL .sf directory!`);
		console.log(`The worker would:`);
		console.log(`  1. Write session status to ~/.sf/parallel/`);
		console.log(`  2. Write orchestrator.json to ~/.sf/`);
		console.log(`  3. Potentially git init in ${result3} (the home directory)`);
		console.log(`  4. Corrupt the user-level SF configuration`);
	}
}

// ── Test 5: Verify findWorktreeSegment matches at the wrong /.sf/ ──────

console.log(`\n=== Root Cause Detail ===\n`);
const seg = findWorktreeSegment(resolvedPath);
if (seg) {
	console.log(`findWorktreeSegment() matched:`);
	console.log(`  sfIdx:         ${seg.sfIdx}`);
	console.log(`  afterWorktrees: ${seg.afterWorktrees}`);
	console.log(`  Path before /.sf/: "${resolvedPath.slice(0, seg.sfIdx)}"`);
	console.log(
		`  This is: ${resolvedPath.slice(0, seg.sfIdx) === USER_HOME ? "THE HOME DIRECTORY (bug!)" : "some other directory"}`,
	);

	// Show which regex matched
	const directMarker = "/.sf/worktrees/";
	const directIdx = resolvedPath.indexOf(directMarker);
	if (directIdx !== -1) {
		console.log(
			`\n  Matched by: direct marker "/.sf/worktrees/" at index ${directIdx}`,
		);
		console.log(
			`  The /.sf/ it found is at: "${resolvedPath.slice(0, directIdx + 5)}"`,
		);
		console.log(`  This /.sf/ is the USER-LEVEL ~/.sf, not the project .sf!`);
	} else {
		console.log(`\n  Matched by: symlink regex`);
	}
}

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

console.log(`\n${"=".repeat(60)}`);
if (workerBuggy) {
	console.log(`\n🐛 BUG CONFIRMED: resolveProjectRoot() returns "${result3}"`);
	console.log(`   when it should return "${PROJECT_DIR}"`);
	console.log(`   because findWorktreeSegment() matches the /.sf/ in the`);
	console.log(`   user-level ~/.sf path, not the project-level .sf symlink.`);
	process.exit(1);
} else {
	console.log(`\n✅ Bug not reproduced — may be fixed.`);
	process.exit(0);
}
