import assert from "node:assert/strict";
import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, test } from "vitest";

import {
	bootstrapProject,
	buildAutoBootstrapContext,
	hasMilestones,
	hasProjectMilestones,
} from "../headless-context.js";
import {
	closeDatabase,
	insertMilestone,
	openDatabase,
} from "../resources/extensions/sf/sf-db.js";

afterEach(() => {
	closeDatabase();
});

test("buildAutoBootstrapContext includes purpose docs and source inventory", () => {
	const root = mkdtempSync(join(tmpdir(), "sf-headless-bootstrap-"));
	mkdirSync(join(root, ".sf"), { recursive: true });
	mkdirSync(join(root, "docs"), { recursive: true });
	mkdirSync(join(root, "src"), { recursive: true });

	writeFileSync(
		join(root, ".sf", "PROJECT.md"),
		"# Project\n\nSF owns state.\n",
	);
	writeFileSync(join(root, "VISION.md"), "# Vision\n\nBuild the runner.\n");
	writeFileSync(join(root, "TODO.md"), "# TODO\n\nWire ACE.\n");
	writeFileSync(join(root, "docs", "architecture.md"), "# Architecture\n");
	writeFileSync(join(root, "src", "main.ts"), "export const main = true;\n");

	const context = buildAutoBootstrapContext(root);

	assert.match(context, /Autonomous Repo Bootstrap/);
	assert.match(context, /SF working specs first/);
	assert.match(context, /\.sf working model/);
	assert.match(context, /product category and representative competitors/);
	assert.match(context, /purpose, vision, architecture/);
	assert.match(context, /ACE spec-first TDD/);
	assert.match(context, /explorer-style subagents/);
	assert.match(context, /harness-engineering principles/);
	assert.match(context, /review\/export or recovery surfaces/);
	assert.match(context, /canonical structured runtime state/);
	assert.match(context, /## \.sf\/PROJECT\.md/);
	assert.match(context, /## VISION\.md/);
	assert.match(context, /## TODO\.md/);
	assert.match(context, /## docs\/architecture\.md/);
	assert.match(context, /Source File Inventory/);
	assert.match(context, /src\/main\.ts/);
});

test("buildAutoBootstrapContext prioritizes SF specs over orientation and legacy spec docs", () => {
	const root = mkdtempSync(join(tmpdir(), "sf-headless-spec-priority-"));
	mkdirSync(join(root, ".sf"), { recursive: true });
	mkdirSync(join(root, "docs", "specs"), { recursive: true });

	writeFileSync(
		join(root, ".sf", "PROJECT.md"),
		"# Project\n\nCanonical SF project spec.\n",
	);
	writeFileSync(join(root, "BASE_SPEC.md"), "# Base Spec\n\nImported seed.\n");
	writeFileSync(
		join(root, "docs", "specs", "README.md"),
		"# Specs\n\nImported durable specs.\n",
	);
	writeFileSync(join(root, "README.md"), "# Readme\n\nOrientation.\n");

	const context = buildAutoBootstrapContext(root);

	const projectIndex = context.indexOf("## .sf/PROJECT.md");
	const baseSpecIndex = context.indexOf("## BASE_SPEC.md");
	const docsSpecIndex = context.indexOf("## docs/specs/README.md");
	const readmeIndex = context.indexOf("## README.md");

	assert.notEqual(projectIndex, -1);
	assert.notEqual(baseSpecIndex, -1);
	assert.notEqual(docsSpecIndex, -1);
	assert.notEqual(readmeIndex, -1);
	assert.ok(projectIndex < baseSpecIndex);
	assert.ok(projectIndex < readmeIndex);
	assert.ok(readmeIndex < baseSpecIndex);
	assert.ok(readmeIndex < docsSpecIndex);
});

test("bootstrapProject_keeps_old_state_dirs_separate_and_does_not_auto_add_serena_mcp", () => {
	const root = mkdtempSync(join(tmpdir(), "sf-headless-no-compat-"));
	const home = mkdtempSync(join(tmpdir(), "sf-headless-home-"));
	const oldHome = process.env.HOME;
	process.env.HOME = home;
	mkdirSync(join(root, ".gsd"), { recursive: true });
	writeFileSync(join(root, ".gsd", "state.md"), "# old state\n");

	try {
		bootstrapProject(root);
	} finally {
		if (oldHome === undefined) {
			delete process.env.HOME;
		} else {
			process.env.HOME = oldHome;
		}
	}

	assert.equal(existsSync(join(root, ".gsd", "state.md")), true);
	assert.equal(existsSync(join(root, ".sf", "milestones")), true);
	assert.equal(existsSync(join(root, ".sf", "mcp.json")), false);
	assert.equal(existsSync(join(home, ".serena", "serena_config.yml")), false);
});

test("hasMilestones only reports true when milestone directories exist", () => {
	const root = mkdtempSync(join(tmpdir(), "sf-headless-milestones-"));

	assert.equal(hasMilestones(root), false);

	mkdirSync(join(root, ".sf", "milestones"), { recursive: true });
	assert.equal(hasMilestones(root), false);

	mkdirSync(join(root, ".sf", "milestones", "M001"), { recursive: true });
	assert.equal(hasMilestones(root), true);
});

test("hasProjectMilestones_when_db_contains_milestone_without_disk_dir_reports_true", async () => {
	const root = mkdtempSync(join(tmpdir(), "sf-headless-db-milestones-"));
	mkdirSync(join(root, ".sf", "milestones"), { recursive: true });
	openDatabase(join(root, ".sf", "sf.db"));
	insertMilestone({
		id: "M001-6377a4",
		title: "DB-only milestone",
		status: "queued",
	});
	closeDatabase();

	assert.equal(hasMilestones(root), false);
	assert.equal(await hasProjectMilestones(root), true);
});
