import { issues, projects, projectWorkspaces } from "@rudderhq/db";
import { updateRunWorkspaceSchema } from "@rudderhq/shared";
import { and, eq } from "drizzle-orm";
import { Router } from "express";
import { validate } from "../middleware/validate.js";
import { parseProjectExecutionWorkspacePolicy } from "../services/execution-workspace-policy.js";
import { logActivity, runWorkspaceService, workspaceOperationService } from "../services/index.js";
import { cleanupExecutionWorkspaceArtifacts, stopRuntimeServicesForExecutionWorkspace, } from "../services/workspace-runtime.js";
import { assertCompanyAccess, getActorInfo } from "./authz.js";
const TERMINAL_ISSUE_STATUSES = new Set(["done", "cancelled"]);
export function runWorkspaceRoutes(db) {
    const router = Router();
    const svc = runWorkspaceService(db);
    const workspaceOperationsSvc = workspaceOperationService(db);
    router.get(["/orgs/:orgId/run-workspaces", "/orgs/:orgId/execution-workspaces"], async (req, res) => {
        const orgId = req.params.orgId;
        assertCompanyAccess(req, orgId);
        const workspaces = await svc.list(orgId, {
            projectId: req.query.projectId,
            projectWorkspaceId: req.query.projectWorkspaceId,
            issueId: req.query.issueId,
            status: req.query.status,
            reuseEligible: req.query.reuseEligible === "true",
        });
        res.json(workspaces);
    });
    router.get(["/run-workspaces/:id", "/execution-workspaces/:id"], async (req, res) => {
        const id = req.params.id;
        const workspace = await svc.getById(id);
        if (!workspace) {
            res.status(404).json({ error: "Run workspace not found" });
            return;
        }
        assertCompanyAccess(req, workspace.orgId);
        res.json(workspace);
    });
    router.patch(["/run-workspaces/:id", "/execution-workspaces/:id"], validate(updateRunWorkspaceSchema), async (req, res) => {
        const id = req.params.id;
        const existing = await svc.getById(id);
        if (!existing) {
            res.status(404).json({ error: "Run workspace not found" });
            return;
        }
        assertCompanyAccess(req, existing.orgId);
        const patch = {
            ...req.body,
            ...(req.body.cleanupEligibleAt ? { cleanupEligibleAt: new Date(req.body.cleanupEligibleAt) } : {}),
        };
        let workspace = existing;
        let cleanupWarnings = [];
        if (req.body.status === "archived" && existing.status !== "archived") {
            const linkedIssues = await db
                .select({
                id: issues.id,
                status: issues.status,
            })
                .from(issues)
                .where(and(eq(issues.orgId, existing.orgId), eq(issues.executionWorkspaceId, existing.id)));
            const activeLinkedIssues = linkedIssues.filter((issue) => !TERMINAL_ISSUE_STATUSES.has(issue.status));
            if (activeLinkedIssues.length > 0) {
                res.status(409).json({
                    error: `Cannot archive run workspace while ${activeLinkedIssues.length} linked issue(s) are still open`,
                });
                return;
            }
            const closedAt = new Date();
            const archivedWorkspace = await svc.update(id, {
                ...patch,
                status: "archived",
                closedAt,
                cleanupReason: null,
            });
            if (!archivedWorkspace) {
                res.status(404).json({ error: "Run workspace not found" });
                return;
            }
            workspace = archivedWorkspace;
            try {
                await stopRuntimeServicesForExecutionWorkspace({
                    db,
                    executionWorkspaceId: existing.id,
                    workspaceCwd: existing.cwd,
                });
                const projectWorkspace = existing.projectWorkspaceId
                    ? await db
                        .select({
                        cwd: projectWorkspaces.cwd,
                        cleanupCommand: projectWorkspaces.cleanupCommand,
                    })
                        .from(projectWorkspaces)
                        .where(and(eq(projectWorkspaces.id, existing.projectWorkspaceId), eq(projectWorkspaces.orgId, existing.orgId)))
                        .then((rows) => rows[0] ?? null)
                    : null;
                const projectPolicy = existing.projectId
                    ? await db
                        .select({
                        executionWorkspacePolicy: projects.executionWorkspacePolicy,
                    })
                        .from(projects)
                        .where(and(eq(projects.id, existing.projectId), eq(projects.orgId, existing.orgId)))
                        .then((rows) => parseProjectExecutionWorkspacePolicy(rows[0]?.executionWorkspacePolicy))
                    : null;
                const cleanupResult = await cleanupExecutionWorkspaceArtifacts({
                    workspace: existing,
                    projectWorkspace,
                    teardownCommand: projectPolicy?.workspaceStrategy?.teardownCommand ?? null,
                    recorder: workspaceOperationsSvc.createRecorder({
                        orgId: existing.orgId,
                        executionWorkspaceId: existing.id,
                    }),
                });
                cleanupWarnings = cleanupResult.warnings;
                const cleanupPatch = {
                    closedAt,
                    cleanupReason: cleanupWarnings.length > 0 ? cleanupWarnings.join(" | ") : null,
                };
                if (!cleanupResult.cleaned) {
                    cleanupPatch.status = "cleanup_failed";
                }
                if (cleanupResult.warnings.length > 0 || !cleanupResult.cleaned) {
                    workspace = (await svc.update(id, cleanupPatch)) ?? workspace;
                }
            }
            catch (error) {
                const failureReason = error instanceof Error ? error.message : String(error);
                workspace =
                    (await svc.update(id, {
                        status: "cleanup_failed",
                        closedAt,
                        cleanupReason: failureReason,
                    })) ?? workspace;
                res.status(500).json({
                    error: `Failed to archive run workspace: ${failureReason}`,
                });
                return;
            }
        }
        else {
            const updatedWorkspace = await svc.update(id, patch);
            if (!updatedWorkspace) {
                res.status(404).json({ error: "Run workspace not found" });
                return;
            }
            workspace = updatedWorkspace;
        }
        const actor = getActorInfo(req);
        await logActivity(db, {
            orgId: existing.orgId,
            actorType: actor.actorType,
            actorId: actor.actorId,
            agentId: actor.agentId,
            runId: actor.runId,
            action: "run_workspace.updated",
            entityType: "run_workspace",
            entityId: workspace.id,
            details: {
                changedKeys: Object.keys(req.body).sort(),
                ...(cleanupWarnings.length > 0 ? { cleanupWarnings } : {}),
            },
        });
        res.json(workspace);
    });
    return router;
}
/** @deprecated Use runWorkspaceRoutes. */
export const executionWorkspaceRoutes = runWorkspaceRoutes;
//# sourceMappingURL=execution-workspaces.js.map