import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { clearParseCache } from "./files.js";
import { clearPathCache, sfRoot } from "./paths.js";
import { getProjectResearchStatus } from "./project-research-policy.js";
import { validateArtifact } from "./schemas/validate.js";
import { getActiveRequirements, isDbAvailable } from "./sf-db.js";

const EXPLICIT_RESEARCH_SOURCES = new Set(["research-decision", "user"]);
function clearCaches() {
	clearPathCache();
	clearParseCache();
}
function runtimeDir(basePath) {
	return join(sfRoot(basePath), "runtime");
}
export function researchDecisionPath(basePath) {
	return join(runtimeDir(basePath), "research-decision.json");
}
export function isWorkflowPrefsCaptured(basePath) {
	// Check yaml (canonical) first, fallback to legacy md
	const sfRootPath = sfRoot(basePath);
	const candidates = [join(sfRootPath, "preferences.yaml")];
	for (const prefsPath of candidates) {
		if (!existsSync(prefsPath)) continue;
		let content;
		try {
			content = readFileSync(prefsPath, "utf-8");
		} catch {
			return false;
		}
		if (prefsPath.endsWith(".yaml")) {
			return /^workflow_prefs_captured:\s*true\s*$/m.test(content);
		}
		const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
		if (!match) return false;
		return /^workflow_prefs_captured:\s*true\s*$/m.test(match[1]);
	}
	return false;
}
export function writeDefaultResearchSkipDecision(
	basePath,
	reason = "deterministic-default",
	previousSource,
) {
	mkdirSync(runtimeDir(basePath), { recursive: true });
	const payload = {
		decision: "skip",
		decided_at: new Date().toISOString(),
		source: "workflow-preferences",
		reason,
	};
	if (previousSource) payload.previous_source = previousSource;
	writeFileSync(
		researchDecisionPath(basePath),
		`${JSON.stringify(payload, null, 2)}\n`,
		"utf-8",
	);
	clearCaches();
}
function readDecision(basePath) {
	const path = researchDecisionPath(basePath);
	if (!existsSync(path)) return { exists: false, valid: false };
	try {
		const parsed = JSON.parse(readFileSync(path, "utf-8"));
		const decision =
			parsed.decision === "research" || parsed.decision === "skip"
				? parsed.decision
				: undefined;
		return {
			exists: true,
			valid: decision !== undefined,
			decision,
			source: typeof parsed.source === "string" ? parsed.source : undefined,
		};
	} catch {
		return { exists: true, valid: false };
	}
}
function isExplicitResearchDecision(decision) {
	return (
		decision.decision === "research" &&
		EXPLICIT_RESEARCH_SOURCES.has(decision.source)
	);
}
export function resolveDeepProjectSetupState(prefs, basePath) {
	if (prefs?.planning_depth !== "deep") {
		return {
			status: "not-applicable",
			stage: null,
			reason: "Deep planning mode is not enabled.",
		};
	}
	const root = sfRoot(basePath);
	if (!isWorkflowPrefsCaptured(basePath)) {
		return {
			status: "pending",
			stage: "workflow-preferences",
			reason: ".sf/preferences.yaml is missing workflow_prefs_captured: true.",
		};
	}
	const projectPath = join(root, "PROJECT.md");
	if (!existsSync(projectPath)) {
		return {
			status: "pending",
			stage: "project",
			reason: ".sf/PROJECT.md is missing.",
		};
	}
	if (!validateArtifact(projectPath, "project").ok) {
		return {
			status: "pending",
			stage: "project",
			reason: ".sf/PROJECT.md is invalid.",
		};
	}
	// DB-first: check for requirements in DB; fall back to file for unmigrated projects
	const hasDbRequirements =
		isDbAvailable() && getActiveRequirements().length > 0;
	if (!hasDbRequirements) {
		const requirementsPath = join(root, "REQUIREMENTS.md");
		if (!existsSync(requirementsPath)) {
			return {
				status: "pending",
				stage: "requirements",
				reason:
					"No requirements found (DB empty and .sf/REQUIREMENTS.md is missing).",
			};
		}
		if (!validateArtifact(requirementsPath, "requirements").ok) {
			return {
				status: "pending",
				stage: "requirements",
				reason: ".sf/REQUIREMENTS.md is invalid.",
			};
		}
	}
	const marker = readDecision(basePath);
	if (!marker.exists) {
		writeDefaultResearchSkipDecision(basePath, "missing-default-repair");
		return {
			status: "complete",
			stage: null,
			reason: "Project research is skipped by the deterministic default.",
		};
	}
	if (!marker.valid) {
		writeDefaultResearchSkipDecision(basePath, "malformed-default-repair");
		return {
			status: "complete",
			stage: null,
			reason:
				"Malformed project research decision was repaired to the deterministic skip default.",
		};
	}
	if (marker.decision === "skip") {
		return {
			status: "complete",
			stage: null,
			reason: "Project research was skipped.",
		};
	}
	if (!isExplicitResearchDecision(marker)) {
		writeDefaultResearchSkipDecision(
			basePath,
			"legacy-workflow-research-default",
			marker.source,
		);
		return {
			status: "complete",
			stage: null,
			reason:
				"Legacy workflow-defaulted project research was normalized to skip.",
		};
	}
	const researchStatus = getProjectResearchStatus(basePath);
	if (researchStatus.globalBlocker) {
		return {
			status: "blocked",
			stage: "project-research",
			reason:
				"Project research wrote PROJECT-RESEARCH-BLOCKER.md, so no verified research exists. Fix the blocker cause, delete the blocker, and rerun auto.",
		};
	}
	if (researchStatus.allDimensionBlockers) {
		return {
			status: "blocked",
			stage: "project-research",
			reason:
				"Project research produced only dimension blocker files, so no usable research exists. Fix the blocker cause, delete the dimension blocker files in `.sf/research/`, and rerun auto.",
		};
	}
	if (!researchStatus.complete) {
		return {
			status: "pending",
			stage: "project-research",
			reason:
				researchStatus.missingDimensions.length > 0
					? `Project research is missing dimensions: ${researchStatus.missingDimensions.join(", ")}.`
					: "Project research has not produced a verified research set.",
		};
	}
	return {
		status: "complete",
		stage: null,
		reason: "All deep project setup gates are complete.",
	};
}
