/*
 * 文件定位：ToolabilityPool / TAP 高级工具系统 / Office 工具集合 / 演示文稿工具。
 * 核心目的：提供 办公文档基础工具 / 演示文稿工具 中的“编辑演示文稿”基础能力原语。
 * 能力要求1：需要定义该能力的输入、输出、错误、权限需求和可观测事件。
 * 能力要求2：这些 Office 能力由 TAP 高级工具系统承接，不再作为 baseTool 完成目标。
 * 能力要求3：TAP 负责 Office 能力的治理、审批、组合和专业能力库承接。
 * 边界：承接 Office 高级工具能力，不回写到 agentCore baseTools。
 * 对接：通过 TAP 复用转交与 runtime 官方模块桥接接通，并保留治理、审批和审计边界。
 * 实现提示：先补稳定类型契约、最小可测行为和清晰错误边界，再接入真实执行逻辑。
 */
import path from "node:path";
export const officeSlidesEditDescriptor = {
    tool: "office.slidesEdit",
    route: "toolabilityPool.officeBase.presentations",
    purpose: "plan a governed presentation edit while blocking real file writes in the first implementation",
    permissionsRequired: ["filesystem:read", "filesystem:write", "office:read", "office:write"],
    defaultDispatch: "dry-run",
    unsafeSideEffects: true,
    tapOwnsApproval: true,
};
function cleanList(values) {
    return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
}
function dryRunEnabled(context) {
    return context?.dryRun !== false;
}
function auditFor(context, presentationPath, outputPath, acceptedScopes = []) {
    return {
        tool: "office.slidesEdit",
        toolCallId: context?.toolCallId?.trim() || "office.slidesEdit:dry-run",
        presentationPath,
        outputPath,
        workspaceRoot: context?.workspaceRoot?.trim() || undefined,
        dryRun: dryRunEnabled(context),
        requestedScopes: cleanList(context?.requestedScopes),
        acceptedScopes,
        permissionsRequired: officeSlidesEditDescriptor.permissionsRequired,
        unsafeSideEffects: true,
        metadata: context?.auditMetadata ?? {},
    };
}
function failure(code, message, boundary, context, presentationPath, outputPath) {
    return {
        ok: false,
        error: { code, message, boundary, publicSafe: true },
        audit: auditFor(context, presentationPath, outputPath),
        events: ["office.slidesEdit.rejected"],
    };
}
function normalizeScopedPath(value, fieldName, context) {
    const rawPath = value?.trim() ?? "";
    if (rawPath.length === 0) {
        if (fieldName === "presentationPath") {
            return failure("MISSING_PRESENTATION_PATH", "office.slidesEdit requires presentationPath", "input", context, value);
        }
        return undefined;
    }
    if (rawPath.includes("\0")) {
        return failure("NUL_BYTE_IN_PATH", `office.slidesEdit ${fieldName} cannot contain NUL bytes`, "input", context);
    }
    const normalized = path.posix.normalize(rawPath.replaceAll("\\", "/"));
    if (path.posix.isAbsolute(normalized) || /^[A-Za-z]:\//.test(normalized)) {
        return failure("ABSOLUTE_PRESENTATION_PATH", `office.slidesEdit only accepts workspace-relative ${fieldName}`, "scope", context, normalized);
    }
    if (normalized === ".." || normalized.startsWith("../")) {
        return failure("PRESENTATION_PATH_OUTSIDE_SCOPE", `office.slidesEdit ${fieldName} must stay inside workspace scope`, "scope", context, normalized);
    }
    const allowedRoots = cleanList(context?.allowedPresentationRoots).map((root) => path.posix.normalize(root.replaceAll("\\", "/")));
    if (allowedRoots.length > 0) {
        const inScope = allowedRoots.some((root) => root === "." || normalized === root || normalized.startsWith(`${root}/`));
        if (!inScope) {
            return failure("PRESENTATION_PATH_OUTSIDE_SCOPE", `office.slidesEdit ${fieldName} is outside allowed presentation roots`, "scope", context, normalized);
        }
    }
    return normalized;
}
function normalizeOperations(operations, context) {
    if (operations === undefined || operations.length === 0) {
        return failure("MISSING_OPERATIONS", "office.slidesEdit requires at least one edit operation", "input", context);
    }
    const normalized = operations.map((operation) => ({
        ...operation,
        target: operation.target?.trim() || undefined,
        value: operation.value?.trim() || undefined,
    }));
    const invalid = normalized.some((operation) => {
        const knownKind = operation.kind === "set-text" ||
            operation.kind === "insert-slide" ||
            operation.kind === "delete-slide" ||
            operation.kind === "replace-image" ||
            operation.kind === "set-speaker-notes";
        const validSlide = operation.slideNumber === undefined || (Number.isInteger(operation.slideNumber) && operation.slideNumber >= 1);
        return !knownKind || !validSlide;
    });
    if (invalid) {
        return failure("INVALID_OPERATION", "office.slidesEdit operations must use known kinds and positive slide numbers", "input", context);
    }
    return normalized;
}
function resolveScopes(context) {
    const requested = cleanList(context?.requestedScopes);
    const allowed = cleanList(context?.allowedScopes);
    if (requested.length === 0) {
        return [];
    }
    const denied = requested.filter((scope) => !allowed.includes(scope));
    if (denied.length > 0) {
        return failure("SCOPE_DENIED", `office.slidesEdit scope ${denied[0]} is outside runtime governance`, "scope", context);
    }
    return requested;
}
function ensurePermissions(context, presentationPath, outputPath) {
    const granted = cleanList(context?.grantedPermissions);
    if (granted.length === 0) {
        return undefined;
    }
    const missing = officeSlidesEditDescriptor.permissionsRequired.filter((permission) => !granted.includes(permission));
    if (missing.length > 0) {
        return failure("PERMISSION_DENIED", `office.slidesEdit is missing permissions: ${missing.join(", ")}`, "permission", context, presentationPath, outputPath);
    }
    return undefined;
}
function buildCommandPreview(request) {
    return [
        "office-slides-edit",
        "--dry-run",
        "--operation-count",
        String(request.operationCount),
        "--input",
        request.presentationPath,
        ...(request.outputPath === undefined ? [] : ["--output", request.outputPath]),
    ];
}
export function planOfficeSlidesEdit(request = {}) {
    if (request.context?.governance?.accepted === false) {
        return failure("GOVERNANCE_REJECTED", request.context.governance.reason ?? "office.slidesEdit was rejected by runtime governance", "governance", request.context);
    }
    const presentationPath = normalizeScopedPath(request.presentationPath, "presentationPath", request.context);
    if (typeof presentationPath !== "string") {
        return (presentationPath ??
            failure("MISSING_PRESENTATION_PATH", "office.slidesEdit requires presentationPath", "input", request.context));
    }
    const outputPath = normalizeScopedPath(request.outputPath, "outputPath", request.context);
    if (outputPath !== undefined && typeof outputPath !== "string") {
        return outputPath;
    }
    const operations = normalizeOperations(request.operations, request.context);
    if ("ok" in operations) {
        return operations;
    }
    const acceptedScopes = resolveScopes(request.context);
    if ("ok" in acceptedScopes) {
        return acceptedScopes;
    }
    const permissionFailure = ensurePermissions(request.context, presentationPath, outputPath);
    if (permissionFailure !== undefined) {
        return permissionFailure;
    }
    if (!dryRunEnabled(request.context)) {
        return failure("REAL_EXECUTION_BLOCKED", "office.slidesEdit only returns a guarded dry-run edit plan in the first implementation", "contract", request.context, presentationPath, outputPath);
    }
    const plan = {
        kind: "agentCore.basicTool.office.slidesEdit.plan",
        operation: "presentation-edit",
        presentationPath,
        outputPath,
        operations,
        commandPreview: buildCommandPreview({ presentationPath, outputPath, operationCount: operations.length }),
        dryRun: true,
        executionBlocked: true,
        writesFile: true,
        tapOwnsApproval: true,
    };
    return {
        ok: true,
        plan,
        audit: auditFor(request.context, presentationPath, outputPath, acceptedScopes),
        events: ["office.slidesEdit.planned"],
    };
}
