import fs from "node:fs/promises";
import path from "node:path";
import { asString, parseObject } from "../agent-runtimes/utils.js";
import { buildExecutionWorkspaceCleanupEnv, directoryExists, gitErrorIncludes, provisionExecutionWorktree, recordGitOperation, recordWorkspaceCommandOperation, renderWorkspaceTemplate, resolveConfiguredPath, resolveGitRepoRootForWorkspaceCleanup, runGit, sanitizeBranchName } from "./workspace-runtime.helpers.js";
export async function realizeExecutionWorkspace(input) {
    const rawStrategy = parseObject(input.config.workspaceStrategy);
    const strategyType = asString(rawStrategy.type, "project_primary");
    if (strategyType !== "git_worktree") {
        return {
            ...input.base,
            strategy: "project_primary",
            cwd: input.base.baseCwd,
            branchName: null,
            worktreePath: null,
            warnings: [],
            created: false,
        };
    }
    const repoRoot = await runGit(["rev-parse", "--show-toplevel"], input.base.baseCwd);
    const branchTemplate = asString(rawStrategy.branchTemplate, "{{issue.identifier}}-{{slug}}");
    const renderedBranch = renderWorkspaceTemplate(branchTemplate, {
        issue: input.issue,
        agent: input.agent,
        projectId: input.base.projectId,
        repoRef: input.base.repoRef,
    });
    const branchName = sanitizeBranchName(renderedBranch);
    const configuredParentDir = asString(rawStrategy.worktreeParentDir, "");
    const worktreeParentDir = configuredParentDir
        ? resolveConfiguredPath(configuredParentDir, repoRoot)
        : path.join(repoRoot, ".rudder", "worktrees");
    const worktreePath = path.join(worktreeParentDir, branchName);
    const baseRef = asString(rawStrategy.baseRef, input.base.repoRef ?? "HEAD");
    await fs.mkdir(worktreeParentDir, { recursive: true });
    const existingWorktree = await directoryExists(worktreePath);
    if (existingWorktree) {
        const existingGitDir = await runGit(["rev-parse", "--git-dir"], worktreePath).catch(() => null);
        if (existingGitDir) {
            if (input.recorder) {
                await input.recorder.recordOperation({
                    phase: "worktree_prepare",
                    cwd: repoRoot,
                    metadata: {
                        repoRoot,
                        worktreePath,
                        branchName,
                        baseRef,
                        created: false,
                        reused: true,
                    },
                    run: async () => ({
                        status: "succeeded",
                        exitCode: 0,
                        system: `Reused existing git worktree at ${worktreePath}\n`,
                    }),
                });
            }
            await provisionExecutionWorktree({
                strategy: rawStrategy,
                base: input.base,
                repoRoot,
                worktreePath,
                branchName,
                issue: input.issue,
                agent: input.agent,
                created: false,
                recorder: input.recorder ?? null,
            });
            return {
                ...input.base,
                strategy: "git_worktree",
                cwd: worktreePath,
                branchName,
                worktreePath,
                warnings: [],
                created: false,
            };
        }
        throw new Error(`Configured worktree path "${worktreePath}" already exists and is not a git worktree.`);
    }
    try {
        await recordGitOperation(input.recorder, {
            phase: "worktree_prepare",
            args: ["worktree", "add", "-b", branchName, worktreePath, baseRef],
            cwd: repoRoot,
            metadata: {
                repoRoot,
                worktreePath,
                branchName,
                baseRef,
                created: true,
            },
            successMessage: `Created git worktree at ${worktreePath}\n`,
            failureLabel: `git worktree add ${worktreePath}`,
        });
    }
    catch (error) {
        if (!gitErrorIncludes(error, "already exists")) {
            throw error;
        }
        await recordGitOperation(input.recorder, {
            phase: "worktree_prepare",
            args: ["worktree", "add", worktreePath, branchName],
            cwd: repoRoot,
            metadata: {
                repoRoot,
                worktreePath,
                branchName,
                baseRef,
                created: false,
                reusedExistingBranch: true,
            },
            successMessage: `Attached existing branch ${branchName} at ${worktreePath}\n`,
            failureLabel: `git worktree add ${worktreePath}`,
        });
    }
    await provisionExecutionWorktree({
        strategy: rawStrategy,
        base: input.base,
        repoRoot,
        worktreePath,
        branchName,
        issue: input.issue,
        agent: input.agent,
        created: true,
        recorder: input.recorder ?? null,
    });
    return {
        ...input.base,
        strategy: "git_worktree",
        cwd: worktreePath,
        branchName,
        worktreePath,
        warnings: [],
        created: true,
    };
}
export async function cleanupExecutionWorkspaceArtifacts(input) {
    const warnings = [];
    const workspacePath = input.workspace.providerRef ?? input.workspace.cwd;
    const cleanupEnv = buildExecutionWorkspaceCleanupEnv({
        workspace: input.workspace,
        projectWorkspaceCwd: input.projectWorkspace?.cwd ?? null,
    });
    const createdByRuntime = input.workspace.metadata?.createdByRuntime === true;
    const cleanupCommands = [
        input.projectWorkspace?.cleanupCommand ?? null,
        input.teardownCommand ?? null,
    ]
        .map((value) => asString(value, "").trim())
        .filter(Boolean);
    for (const command of cleanupCommands) {
        try {
            await recordWorkspaceCommandOperation(input.recorder, {
                phase: "workspace_teardown",
                command,
                cwd: workspacePath ?? input.projectWorkspace?.cwd ?? process.cwd(),
                env: cleanupEnv,
                label: `Run workspace cleanup command "${command}"`,
                metadata: {
                    workspaceId: input.workspace.id,
                    workspacePath,
                    branchName: input.workspace.branchName,
                    providerType: input.workspace.providerType,
                },
                successMessage: `Completed cleanup command "${command}"\n`,
            });
        }
        catch (err) {
            warnings.push(err instanceof Error ? err.message : String(err));
        }
    }
    if (input.workspace.providerType === "git_worktree" && workspacePath) {
        const repoRoot = await resolveGitRepoRootForWorkspaceCleanup(workspacePath, input.projectWorkspace?.cwd ?? null);
        const worktreeExists = await directoryExists(workspacePath);
        if (worktreeExists) {
            if (!repoRoot) {
                warnings.push(`Could not resolve git repo root for "${workspacePath}".`);
            }
            else {
                try {
                    await recordGitOperation(input.recorder, {
                        phase: "worktree_cleanup",
                        args: ["worktree", "remove", "--force", workspacePath],
                        cwd: repoRoot,
                        metadata: {
                            workspaceId: input.workspace.id,
                            workspacePath,
                            branchName: input.workspace.branchName,
                            cleanupAction: "worktree_remove",
                        },
                        successMessage: `Removed git worktree ${workspacePath}\n`,
                        failureLabel: `git worktree remove ${workspacePath}`,
                    });
                }
                catch (err) {
                    warnings.push(err instanceof Error ? err.message : String(err));
                }
            }
        }
        if (createdByRuntime && input.workspace.branchName) {
            if (!repoRoot) {
                warnings.push(`Could not resolve git repo root to delete branch "${input.workspace.branchName}".`);
            }
            else {
                try {
                    await recordGitOperation(input.recorder, {
                        phase: "worktree_cleanup",
                        args: ["branch", "-d", input.workspace.branchName],
                        cwd: repoRoot,
                        metadata: {
                            workspaceId: input.workspace.id,
                            workspacePath,
                            branchName: input.workspace.branchName,
                            cleanupAction: "branch_delete",
                        },
                        successMessage: `Deleted branch ${input.workspace.branchName}\n`,
                        failureLabel: `git branch -d ${input.workspace.branchName}`,
                    });
                }
                catch (err) {
                    const message = err instanceof Error ? err.message : String(err);
                    warnings.push(`Skipped deleting branch "${input.workspace.branchName}": ${message}`);
                }
            }
        }
    }
    else if (input.workspace.providerType === "local_fs" && createdByRuntime && workspacePath) {
        const projectWorkspaceCwd = input.projectWorkspace?.cwd ? path.resolve(input.projectWorkspace.cwd) : null;
        const resolvedWorkspacePath = path.resolve(workspacePath);
        const containsProjectWorkspace = projectWorkspaceCwd
            ? (resolvedWorkspacePath === projectWorkspaceCwd ||
                projectWorkspaceCwd.startsWith(`${resolvedWorkspacePath}${path.sep}`))
            : false;
        if (containsProjectWorkspace) {
            warnings.push(`Refusing to remove path "${workspacePath}" because it contains the project workspace.`);
        }
        else {
            await fs.rm(resolvedWorkspacePath, { recursive: true, force: true });
            if (input.recorder) {
                await input.recorder.recordOperation({
                    phase: "workspace_teardown",
                    cwd: projectWorkspaceCwd ?? process.cwd(),
                    metadata: {
                        workspaceId: input.workspace.id,
                        workspacePath: resolvedWorkspacePath,
                        cleanupAction: "remove_local_fs",
                    },
                    run: async () => ({
                        status: "succeeded",
                        exitCode: 0,
                        system: `Removed local workspace directory ${resolvedWorkspacePath}\n`,
                    }),
                });
            }
        }
    }
    const cleaned = !workspacePath ||
        !(await directoryExists(workspacePath));
    return {
        cleanedPath: workspacePath,
        cleaned,
        warnings,
    };
}
//# sourceMappingURL=workspace-runtime.lifecycle.js.map