import { createRunWorkspace, getRunWorkspacePaths, getRunWorkspacePathsFromRef, readCheckpoint, readArtifactLedger, readManifest, recordArtifact, recordEvent, refreshArtifactLedger, writeCheckpoint, } from '../run-workspace.js';
import { isAbsolute, normalize, relative, resolve } from 'node:path';
import { checkReadPathAllowed, checkWritePathAllowed } from './fs-policy.js';
import { extractUserDeclaredExternalRoots } from '../task-state.js';
const ACTIONS = new Set([
    'create',
    'readManifest',
    'recordArtifact',
    'readLedger',
    'refreshLedger',
    'recordEvent',
    'writeCheckpoint',
    'readCheckpoint',
]);
const TASK_FAMILIES = new Set([
    'deck',
    'code',
    'image_asset',
    'research',
    'data_report',
    'release',
    'general',
]);
const DELIVERABLE_MODES = new Set([
    'read_only_review',
    'text_deliverable',
    'file_artifact_delivery',
    'code_change',
    'command_job',
    'mixed_unknown',
]);
export function createRunWorkspaceTool() {
    return {
        name: 'RunWorkspace',
        description: 'Manage OwlCoda durable run metadata under .owlcoda-run. Supports ' +
            'create/readManifest/recordArtifact/readLedger/refreshLedger/recordEvent/' +
            'writeCheckpoint/readCheckpoint. ' +
            'This tool does not execute shell commands or write user deliverables; it ' +
            'only creates and updates run manifest, artifact ledger, verification, checkpoint, and event metadata.',
        maturity: 'beta',
        async execute(input, context) {
            const action = typeof input?.action === 'string' ? input.action : '';
            if (!isRunWorkspaceAction(action)) {
                return error(`Error: action is required and must be one of: ${[...ACTIONS].join(', ')}.`, 'run-workspace:invalid-action');
            }
            try {
                if (action === 'create')
                    return await executeCreate(input, context);
                if (action === 'readManifest')
                    return await executeReadManifest(input, context);
                if (action === 'recordArtifact')
                    return await executeRecordArtifact(input, context);
                if (action === 'readLedger')
                    return await executeReadLedger(input, context);
                if (action === 'refreshLedger')
                    return await executeRefreshLedger(input, context);
                if (action === 'recordEvent')
                    return await executeRecordEvent(input, context);
                if (action === 'writeCheckpoint')
                    return await executeWriteCheckpoint(input, context);
                return await executeReadCheckpoint(input, context);
            }
            catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                return error(`Error: ${message}`, 'run-workspace:execution-error');
            }
        },
    };
}
async function executeCreate(input, context) {
    const outputRoot = requiredString(input.outputRoot, 'outputRoot');
    if (typeof outputRoot !== 'string')
        return outputRoot;
    const taskFamily = optionalEnum(input.taskFamily, TASK_FAMILIES, 'taskFamily');
    if (taskFamily instanceof Error)
        return validationError(taskFamily.message);
    const deliverableMode = optionalEnum(input.deliverableMode, DELIVERABLE_MODES, 'deliverableMode');
    if (deliverableMode instanceof Error)
        return validationError(deliverableMode.message);
    const paths = getRunWorkspacePaths(outputRoot, input.cwd ?? context?.taskState?.contract.cwd ?? process.cwd());
    const fsVerdict = allowMetadataWrite(paths.manifestPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const result = await createRunWorkspace({
        outputRoot,
        ...(typeof input.cwd === 'string' && input.cwd.trim() ? { cwd: input.cwd } : {}),
        ...(taskFamily ? { taskFamily } : {}),
        ...(deliverableMode ? { deliverableMode } : {}),
        ...(isRecord(input.skillRoute) ? { skillRoute: input.skillRoute } : {}),
        ...(isRecord(input.plan) ? { plan: input.plan } : {}),
        ...(isRecord(input.verification) ? { verification: input.verification } : {}),
        ...(isRecord(input.checkpoint) ? { checkpoint: input.checkpoint } : {}),
    });
    return jsonResult(result, { action: 'create', runDir: result.paths.runDir });
}
async function executeReadManifest(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const fsVerdict = allowMetadataRead(getRunWorkspacePathsFromRef(runRef, input.cwd).manifestPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const result = await readManifest(runRef, input.cwd);
    return jsonResult(result, { action: 'readManifest', runId: result.runId });
}
async function executeRecordArtifact(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const artifactPath = requiredString(input.path, 'path');
    if (typeof artifactPath !== 'string')
        return artifactPath;
    const origin = requiredString(input.origin, 'origin');
    if (typeof origin !== 'string')
        return origin;
    const paths = getRunWorkspacePathsFromRef(runRef, input.cwd);
    const fsVerdict = allowMetadataWrite(paths.artifactsPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const manifest = await readManifest(runRef, input.cwd);
    const artifactScope = validateArtifactPathWithinOutputRoot(artifactPath, manifest.outputRoot);
    if (artifactScope)
        return artifactScope;
    const result = await recordArtifact(runRef, {
        path: artifactPath,
        origin,
        ...(typeof input.stepId === 'string' && input.stepId.trim() ? { stepId: input.stepId } : {}),
        ...(typeof input.participatesInFinal === 'boolean' ? { participatesInFinal: input.participatesInFinal } : {}),
        ...(typeof input.sourcePath === 'string' && input.sourcePath.trim() ? { sourcePath: input.sourcePath } : {}),
    }, input.cwd);
    return jsonResult(result, { action: 'recordArtifact', artifactPath: result.path, status: result.status });
}
async function executeReadLedger(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const fsVerdict = allowMetadataRead(getRunWorkspacePathsFromRef(runRef, input.cwd).artifactsPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const result = await readArtifactLedger(runRef, {}, input.cwd);
    return jsonResult(result, { action: 'readLedger', artifactCount: result.artifacts.length });
}
async function executeRefreshLedger(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const paths = getRunWorkspacePathsFromRef(runRef, input.cwd);
    const fsVerdict = allowMetadataWrite(paths.artifactsPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const manifest = await readManifest(runRef, input.cwd);
    const ledger = await readArtifactLedger(runRef, {}, input.cwd);
    for (const artifact of ledger.artifacts) {
        const artifactScope = validateArtifactPathWithinOutputRoot(artifact.path, manifest.outputRoot);
        if (artifactScope)
            return artifactScope;
    }
    const result = await refreshArtifactLedger(runRef, input.cwd);
    return jsonResult(result, { action: 'refreshLedger', artifactCount: result.artifacts.length });
}
async function executeRecordEvent(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const fsVerdict = allowMetadataWrite(getRunWorkspacePathsFromRef(runRef, input.cwd).eventsPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const event = resolveEventInput(input);
    if (event instanceof Error)
        return validationError(event.message);
    const result = await recordEvent(runRef, event, input.cwd);
    return jsonResult(result, { action: 'recordEvent', eventType: result.type, runId: result.runId });
}
async function executeWriteCheckpoint(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const checkpoint = resolveCheckpointInput(input);
    if (checkpoint instanceof Error)
        return validationError(checkpoint.message);
    const fsVerdict = allowMetadataWrite(getRunWorkspacePathsFromRef(runRef, input.cwd).checkpointPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const result = await writeCheckpoint(runRef, checkpoint, input.cwd);
    return jsonResult(result, { action: 'writeCheckpoint' });
}
async function executeReadCheckpoint(input, context) {
    const runRef = requiredString(input.runRef, 'runRef');
    if (typeof runRef !== 'string')
        return runRef;
    const fsVerdict = allowMetadataRead(getRunWorkspacePathsFromRef(runRef, input.cwd).checkpointPath, input, context);
    if (fsVerdict)
        return fsVerdict;
    const result = await readCheckpoint(runRef, input.cwd);
    return jsonResult(result, { action: 'readCheckpoint' });
}
function resolveEventInput(input) {
    if (isRecord(input.event)) {
        if (typeof input.event.type !== 'string' || input.event.type.trim() === '') {
            return new Error('event.type is required for recordEvent.');
        }
        return input.event;
    }
    if (typeof input.type !== 'string' || input.type.trim() === '') {
        return new Error('event.type or top-level type is required for recordEvent.');
    }
    return {
        type: input.type,
        ...(typeof input.at === 'string' && input.at.trim() ? { at: input.at } : {}),
        ...(typeof input.stepId === 'string' && input.stepId.trim() ? { stepId: input.stepId } : {}),
        ...(typeof input.message === 'string' && input.message.trim() ? { message: input.message } : {}),
        ...(isRecord(input.data) ? { data: input.data } : {}),
    };
}
function resolveCheckpointInput(input) {
    if (!isRecord(input.checkpoint)) {
        return new Error('checkpoint object is required for writeCheckpoint.');
    }
    return input.checkpoint;
}
function isRunWorkspaceAction(value) {
    return ACTIONS.has(value);
}
function requiredString(value, name) {
    if (typeof value !== 'string' || value.trim() === '') {
        return {
            output: `Error: ${name} is required.`,
            isError: true,
            metadata: { failureCategory: `run-workspace:missing-${name}` },
        };
    }
    return value;
}
function optionalEnum(value, allowed, name) {
    if (value === undefined)
        return undefined;
    if (typeof value !== 'string' || !allowed.has(value)) {
        return new Error(`${name} must be one of: ${[...allowed].join(', ')}.`);
    }
    return value;
}
function validationError(message) {
    return error(`Error: ${message}`, 'run-workspace:invalid-input');
}
function allowMetadataWrite(targetPath, input, context) {
    const policy = checkWritePathAllowed(targetPath, {
        workspaceRoot: input.cwd ?? context?.taskState?.contract.cwd,
        externalScopes: extractUserDeclaredExternalRoots(context?.taskState),
    });
    if (policy.allowed)
        return null;
    return {
        output: `Error: ${policy.reason}`,
        isError: true,
        metadata: {
            failureCategory: 'run-workspace:fs-policy-denied',
            fsPolicyDenied: true,
            attemptedPath: policy.attemptedPath,
        },
    };
}
function allowMetadataRead(targetPath, input, context) {
    const policy = checkReadPathAllowed(targetPath, {
        workspaceRoot: input.cwd ?? context?.taskState?.contract.cwd,
        externalScopes: extractUserDeclaredExternalRoots(context?.taskState),
    });
    if (policy.allowed)
        return null;
    return {
        output: `Error: ${policy.reason}`,
        isError: true,
        metadata: {
            failureCategory: 'run-workspace:fs-policy-denied',
            fsPolicyDenied: true,
            attemptedPath: policy.attemptedPath,
        },
    };
}
function validateArtifactPathWithinOutputRoot(artifactPath, outputRoot) {
    const root = normalize(resolve(outputRoot));
    const resolved = normalize(isAbsolute(artifactPath) ? resolve(artifactPath) : resolve(root, artifactPath));
    const rel = relative(root, resolved);
    if (!rel || (!rel.startsWith('..') && !isAbsolute(rel)))
        return null;
    return error(`Error: artifact path resolves outside run workspace outputRoot. Resolved: ${resolved}. Output root: ${root}.`, 'run-workspace:artifact-outside-output-root');
}
function error(output, failureCategory) {
    return {
        output,
        isError: true,
        metadata: { failureCategory },
    };
}
function jsonResult(result, metadata) {
    return {
        output: JSON.stringify(result, null, 2),
        isError: false,
        metadata: { ...metadata, result },
    };
}
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
//# sourceMappingURL=run-workspace.js.map