import fs from "node:fs";
import path from "node:path";
const PLAN_SUBJECT = "plan";
const PLAN_SCOPE = "plan_mode";
const CURRENT_RUN_KEY = "currentRunId";
export class PlanModeService {
    projectPath;
    state;
    constructor(projectPath, state) {
        this.projectPath = projectPath;
        this.state = state;
    }
    enter(runId, goal = "") {
        const existing = this.read(runId);
        const content = existing.content || planTemplate(runId, goal);
        this.writePlan(runId, content);
        this.state?.setUiState(PLAN_SCOPE, CURRENT_RUN_KEY, runId);
        this.state?.appendEvent(runId, "plan_mode_entered", {
            plan_path: this.relativePath(runId),
            goal,
        });
        return this.read(runId);
    }
    save(runId, content) {
        const trimmed = content.trim();
        if (!trimmed)
            throw new Error("plan content is empty");
        this.writePlan(runId, `${trimmed}\n`);
        this.state?.setUiState(PLAN_SCOPE, CURRENT_RUN_KEY, runId);
        this.state?.appendEvent(runId, "plan_saved", {
            plan_path: this.relativePath(runId),
            chars: trimmed.length,
        });
        return this.read(runId);
    }
    exit(runId, content, summary = "") {
        const record = this.save(runId, content);
        const gate = this.ensureApprovalGate(runId, summary || summarizePlan(content));
        this.state?.updateRunStatus(runId, "paused", "plan awaiting approval");
        this.state?.appendEvent(runId, "plan_mode_exit_requested", {
            gate_id: gate.id,
            plan_path: record.relativePath,
        });
        return { ...record, gate };
    }
    read(runId) {
        const filePath = this.pathForRun(runId);
        const content = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
        return {
            runId,
            path: filePath,
            relativePath: this.relativePath(runId),
            content,
            gate: this.latestGate(runId),
        };
    }
    currentRunId() {
        return this.state?.getUiState(PLAN_SCOPE, CURRENT_RUN_KEY);
    }
    pathForRun(runId) {
        return path.join(this.projectPath, ".deepseekcode", "plans", `${safePlanName(runId)}.md`);
    }
    relativePath(runId) {
        return path.join(".deepseekcode", "plans", `${safePlanName(runId)}.md`).replace(/\\/g, "/");
    }
    writePlan(runId, content) {
        const filePath = this.pathForRun(runId);
        fs.mkdirSync(path.dirname(filePath), { recursive: true });
        fs.writeFileSync(filePath, content, "utf8");
    }
    ensureApprovalGate(runId, summary) {
        if (!this.state)
            throw new Error("plan approval requires a run state store");
        const existing = this.state.listApprovalGates({
            runId,
            subjectType: PLAN_SUBJECT,
            subjectId: runId,
        }, 20);
        const reusable = existing.find((gate) => gate.status === "pending" || gate.status === "approved");
        if (reusable)
            return reusable;
        const gateId = this.state.createApprovalGate({
            runId,
            subjectType: PLAN_SUBJECT,
            subjectId: runId,
            summary,
        });
        return this.state.listApprovalGates({ subjectType: PLAN_SUBJECT, subjectId: runId }, 1)[0]
            ?? {
                id: gateId,
                runId,
                subjectType: PLAN_SUBJECT,
                subjectId: runId,
                status: "pending",
                summary,
                rationale: "",
                createdAtMs: Date.now(),
                updatedAtMs: Date.now(),
            };
    }
    latestGate(runId) {
        return this.state?.listApprovalGates({
            runId,
            subjectType: PLAN_SUBJECT,
            subjectId: runId,
        }, 1)[0];
    }
}
export function formatPlanStatus(record) {
    const lines = [
        `plan: ${displayRunReference(record.runId)}`,
        `path: ${formatPlanReference(record.relativePath)}`,
        `chars: ${record.content.length}`,
    ];
    if (record.gate) {
        lines.push(`approval: ${record.gate.status}`);
    }
    else {
        lines.push("approval: none");
    }
    return lines.join("\n");
}
function planTemplate(runId, goal) {
    return [
        "# DeepSeekCode Plan",
        "",
        `Goal: ${goal.trim() || "(fill in the implementation goal)"}`,
        "",
        "## Understanding",
        "- ",
        "",
        "## Approach",
        "1. ",
        "",
        "## Verification",
        "- Run the focused tests/build required for this change.",
        "",
    ].join("\n");
}
export function formatPlanReference(relativePath) {
    return /^\.deepseekcode[\\/]+plans[\\/]+run_[^\\/]+\.md$/i.test(relativePath.trim())
        ? "draft plan"
        : relativePath;
}
function displayRunReference(runId) {
    return /^run_[0-9a-f-]{8,}$/i.test(runId) ? "current run" : runId;
}
function summarizePlan(content) {
    const firstMeaningful = content
        .split(/\r?\n/)
        .map((line) => line.replace(/^#+\s*/, "").trim())
        .find(Boolean);
    return compact(`Approve implementation plan: ${firstMeaningful ?? "DeepSeekCode plan"}`, 220);
}
function compact(value, max) {
    return value.length <= max ? value : `${value.slice(0, max - 3)}...`;
}
function safePlanName(value) {
    return value.trim().replace(/[^a-zA-Z0-9._-]/g, "-") || "current";
}
