import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import type {
	ExtensionAPI,
	ExtensionCommandContext,
} from "@singularity-forge/pi-coding-agent";
import {
	isAutoActive,
	isAutoPaused,
	pauseAuto,
	startAuto,
	startAutoDetached,
	stopAuto,
	stopAutoRemote,
} from "../../auto.js";
import { handleRate } from "../../commands-rate.js";
import { enableDebug } from "../../debug-logger.js";
import { findMilestoneIds } from "../../milestone-id-utils.js";
import { guardRemoteSession, projectRoot } from "../context.js";

/**
 * Parse --yolo flag and optional file path from the autonomous command string.
 * Supports: `/sf autonomous --yolo path/to/file.md`, `/sf auto --yolo path/to/file.md`,
 * or `/sf auto -y path/to/file.md`.
 */
function parseYoloFlag(trimmed: string): {
	yoloSeedFile: string | null;
	rest: string;
} {
	const yoloRe = /(?:--yolo|-y)\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\S+)/;
	const match = trimmed.match(yoloRe);
	if (!match) return { yoloSeedFile: null, rest: trimmed };

	// Strip quotes if present
	let filePath = match[1];
	if (
		(filePath.startsWith('"') && filePath.endsWith('"')) ||
		(filePath.startsWith("'") && filePath.endsWith("'"))
	) {
		filePath = filePath.slice(1, -1);
	}

	const rest = trimmed.replace(match[0], "").replace(/\s+/g, " ").trim();
	return { yoloSeedFile: filePath, rest };
}

/**
 * Extract a milestone ID (e.g. M016 or M001-a3b4c5) from the command string.
 * Returns the matched ID and the remaining string with the ID removed.
 * The milestone ID pattern matches the format used by findMilestoneIds: M\d+ with
 * an optional -[a-z0-9]{6} suffix for unique milestone IDs.
 */
export function parseMilestoneTarget(input: string): {
	milestoneId: string | null;
	rest: string;
} {
	const match = input.match(/\b(M\d+(?:-[a-z0-9]{6})?)\b/);
	if (!match) return { milestoneId: null, rest: input };
	const rest = input.replace(match[0], "").replace(/\s+/g, " ").trim();
	return { milestoneId: match[1], rest };
}

/**
 * Dispatch entry point for the auto-mode command family.
 *
 * Handles `/sf auto`, `/sf autonomous`, `/sf next`, `/sf stop`, `/sf pause`, and
 * their flag variants. Returns `true` when the command was recognised and
 * routed (caller stops searching), `false` when the command isn't auto-related.
 *
 * Recognised flags on autonomous/auto:
 *   - `full` or `--full` — full-autonomy mode (auto-merge + chain milestones)
 *   - `--verbose`        — verbose execution output
 *   - `--debug`          — enable debug logging via SF_DEBUG
 *   - `M001` (positional) — milestone target lock (only run that milestone)
 *   - `--yolo=<file>`     — yolo seed; bootstraps a fresh milestone from a brief
 *
 * The handler validates milestone targets exist, gates remote sessions, then
 * dispatches via `launchAuto` (which routes between headless and detached
 * spawn paths).
 */
export async function handleAutoCommand(
	trimmed: string,
	ctx: ExtensionCommandContext,
	pi: ExtensionAPI,
): Promise<boolean> {
	const isAutonomousVerb =
		trimmed === "autonomous" || trimmed.startsWith("autonomous ");
	const isAutoVerb = trimmed === "auto" || trimmed.startsWith("auto ");
	const isAutonomousFamily = isAutonomousVerb || isAutoVerb;

	/**
	 * Route an auto-mode launch through either the headless (in-process) or
	 * detached (spawned subprocess) entry point depending on `SF_HEADLESS`.
	 *
	 * Headless mode runs the auto loop in the current process (used by CI,
	 * tests, and `sf headless`); detached mode forks a long-running child so
	 * the interactive shell stays responsive while auto-mode runs.
	 */
	const launchAuto = async (
		verboseMode: boolean,
		options?: {
			step?: boolean;
			milestoneLock?: string | null;
			fullAutonomy?: boolean;
			canAskUser?: boolean;
		},
	): Promise<void> => {
		if (process.env.SF_HEADLESS === "1") {
			await startAuto(ctx, pi, projectRoot(), verboseMode, options);
			return;
		}
		startAutoDetached(ctx, pi, projectRoot(), verboseMode, options);
	};

	if (trimmed === "next" || trimmed.startsWith("next ")) {
		if (trimmed.includes("--dry-run")) {
			const { handleDryRun } = await import("../../commands-maintenance.js");
			await handleDryRun(ctx, projectRoot());
			return true;
		}
		const { milestoneId, rest: afterMilestone } = parseMilestoneTarget(trimmed);
		const verboseMode = afterMilestone.includes("--verbose");
		const debugMode = afterMilestone.includes("--debug");
		if (debugMode) enableDebug(projectRoot());
		if (!(await guardRemoteSession(ctx, pi))) return true;

		// Validate the milestone target exists and is not already complete.
		if (milestoneId) {
			const allIds = findMilestoneIds(projectRoot());
			if (!allIds.includes(milestoneId)) {
				ctx.ui.notify(
					`Milestone ${milestoneId} does not exist. Available: ${allIds.join(", ") || "(none)"}`,
					"error",
				);
				return true;
			}
		}

		await launchAuto(verboseMode, {
			step: true,
			milestoneLock: milestoneId,
		});
		return true;
	}

	if (isAutonomousFamily) {
		const normalized = trimmed.replace(/^(?:auto|autonomous)\b/, "auto");
		const { yoloSeedFile, rest: afterYolo } = parseYoloFlag(normalized);
		const { milestoneId, rest: afterMilestone } =
			parseMilestoneTarget(afterYolo);
		const verboseMode = afterMilestone.includes("--verbose");
		const debugMode = afterMilestone.includes("--debug");
		// `/sf autonomous full` (or `--full`): full-autonomy mode — auto-merges
		// milestone branches and chains to the next milestone without pausing
		// for human review. Git revert is the safety net.
		const fullAutonomy =
			/\bfull\b/.test(afterMilestone) || afterMilestone.includes("--full");
		// `/sf auto` can ask the user when blocked; `/sf autonomous` cannot.
		const canAskUser = isAutoVerb;
		if (debugMode) enableDebug(projectRoot());
		if (!(await guardRemoteSession(ctx, pi))) return true;

		// Validate the milestone target exists and is not already complete.
		if (milestoneId) {
			const allIds = findMilestoneIds(projectRoot());
			if (!allIds.includes(milestoneId)) {
				ctx.ui.notify(
					`Milestone ${milestoneId} does not exist. Available: ${allIds.join(", ") || "(none)"}`,
					"error",
				);
				return true;
			}
		}

		if (yoloSeedFile) {
			const resolved = resolve(projectRoot(), yoloSeedFile);
			if (!existsSync(resolved)) {
				ctx.ui.notify(`Yolo seed file not found: ${resolved}`, "error");
				return true;
			}
			const seedContent = readFileSync(resolved, "utf-8").trim();
			if (!seedContent) {
				ctx.ui.notify(`Yolo seed file is empty: ${resolved}`, "error");
				return true;
			}
			// Headless path: bootstrap project, dispatch non-interactive discuss,
			// then auto-mode starts automatically via checkAutoStartAfterDiscuss
			// when the LLM says "Milestone X ready."
			const { showHeadlessMilestoneCreation } = await import(
				"../../guided-flow.js"
			);
			await showHeadlessMilestoneCreation(ctx, pi, projectRoot(), seedContent);
		} else if (milestoneId) {
			await launchAuto(verboseMode, {
				milestoneLock: milestoneId,
				fullAutonomy,
				canAskUser,
			});
		} else {
			await launchAuto(verboseMode, { fullAutonomy, canAskUser });
		}
		return true;
	}

	if (trimmed === "stop") {
		if (!isAutoActive() && !isAutoPaused()) {
			const result = stopAutoRemote(projectRoot());
			if (result.found) {
				ctx.ui.notify(
					`Sent stop signal to auto-mode session (PID ${result.pid}). It will shut down gracefully.`,
					"info",
				);
			} else if (result.error) {
				ctx.ui.notify(
					`Failed to stop remote auto-mode: ${result.error}`,
					"error",
				);
			} else {
				ctx.ui.notify("Auto-mode is not running.", "info");
			}
			return true;
		}
		await stopAuto(ctx, pi, "User requested stop");
		return true;
	}

	if (trimmed === "pause") {
		if (!isAutoActive()) {
			if (isAutoPaused()) {
				ctx.ui.notify(
					"Autonomous mode is already paused. /sf autonomous to resume.",
					"info",
				);
			} else {
				ctx.ui.notify("Auto-mode is not running.", "info");
			}
			return true;
		}
		await pauseAuto(ctx, pi);
		return true;
	}

	if (trimmed === "rate" || trimmed.startsWith("rate ")) {
		await handleRate(
			trimmed.replace(/^rate\s*/, "").trim(),
			ctx,
			projectRoot(),
		);
		return true;
	}

	if (trimmed === "") {
		if (!(await guardRemoteSession(ctx, pi))) return true;
		await launchAuto(false, { step: true });
		return true;
	}

	return false;
}
