/*
 * 文件定位：ToolabilityPool / TAP 高级工具系统 / Office 工具集合 / 演示文稿工具。
 * 核心目的：提供 办公文档基础工具 / 演示文稿工具 中的“视觉检查演示文稿”基础能力原语。
 * 能力要求1：需要定义该能力的输入、输出、错误、权限需求和可观测事件。
 * 能力要求2：这些 Office 能力由 TAP 高级工具系统承接，不再作为 baseTool 完成目标。
 * 能力要求3：TAP 负责 Office 能力的治理、审批、组合和专业能力库承接。
 * 边界：承接 Office 高级工具能力，不回写到 agentCore baseTools。
 * 对接：通过 TAP 复用转交与 runtime 官方模块桥接接通，并保留治理、审批和审计边界。
 * 实现提示：先补稳定类型契约、最小可测行为和清晰错误边界，再接入真实执行逻辑。
 */
export const officeSlidesVisionCheckDescriptor = {
    toolId: "office.slidesVisionCheck",
    capability: "check-presentation-vision",
    route: "toolabilityPool.officeBase.presentations",
    defaultDryRun: true,
    tapOwnsApproval: true,
    unsafeSideEffects: false,
    permissionsRequired: ["filesystem:read", "office:slides:read", "vision:read"],
};
const supportedChecks = [
    "renderability",
    "layout-overlap",
    "text-legibility",
    "asset-presence",
];
function cleanList(values) {
    return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
}
function dryRunEnabled(context) {
    return context?.dryRun !== false;
}
function invocationId(context) {
    return context?.invocationId?.trim() || "office.slidesVisionCheck:dry-run";
}
function normalizeRoot(root) {
    const trimmed = root.trim();
    return trimmed === "/" ? trimmed : trimmed.replace(/\/+$/, "");
}
function hasPathTraversal(presentationPath) {
    return presentationPath.split(/[\\/]+/).some((segment) => segment === "..");
}
function auditEvent(type, context, presentationPath, metadata) {
    return {
        type,
        toolId: officeSlidesVisionCheckDescriptor.toolId,
        invocationId: invocationId(context),
        dryRun: dryRunEnabled(context),
        presentationPath,
        metadata: {
            ...(context?.auditMetadata ?? {}),
            ...(metadata ?? {}),
        },
    };
}
function failure(code, message, boundary, context, presentationPath) {
    return {
        ok: false,
        toolId: officeSlidesVisionCheckDescriptor.toolId,
        error: { code, message, boundary, publicSafe: true },
        audit: [auditEvent("agentCore.basicTool.office.slidesVisionCheck.rejected", context, presentationPath, { code })],
        events: ["basicTool.office.slidesVisionCheck.rejected"],
    };
}
function normalizePresentationPath(presentationPath, context) {
    const normalized = presentationPath?.trim() ?? "";
    if (normalized.length === 0) {
        return failure("MISSING_PRESENTATION_PATH", "office.slidesVisionCheck requires target.presentationPath", "input", context, presentationPath);
    }
    if (hasPathTraversal(normalized)) {
        return failure("SCOPE_REJECTED", "office.slidesVisionCheck target.presentationPath must not escape its presentation scope", "scope", context, normalized);
    }
    return normalized;
}
function normalizeChecks(checks, context, presentationPath) {
    if (checks === undefined || checks.length === 0) {
        return ["renderability", "layout-overlap", "text-legibility"];
    }
    const normalized = cleanList(checks);
    const invalid = normalized.find((check) => !supportedChecks.includes(check));
    if (invalid !== undefined) {
        return failure("INVALID_CHECK", "office.slidesVisionCheck target.checks contains an unsupported check", "input", context, presentationPath);
    }
    return normalized;
}
function normalizeSlideNumbers(slideNumbers, context, presentationPath) {
    if (slideNumbers === undefined || slideNumbers.length === 0) {
        return [];
    }
    const invalid = slideNumbers.find((slideNumber) => !Number.isInteger(slideNumber) || slideNumber <= 0);
    if (invalid !== undefined) {
        return failure("INVALID_SLIDE_NUMBER", "office.slidesVisionCheck target.slideNumbers must contain positive integers", "input", context, presentationPath);
    }
    return [...new Set(slideNumbers)];
}
function normalizeRenderScale(renderScale, context, presentationPath) {
    if (renderScale === undefined) {
        return 1;
    }
    if (Number.isFinite(renderScale) && renderScale > 0 && renderScale <= 4) {
        return renderScale;
    }
    return failure("INVALID_RENDER_SCALE", "office.slidesVisionCheck target.renderScale must be greater than 0 and no more than 4", "input", context, presentationPath);
}
function normalizeTarget(target, context) {
    const presentationPath = normalizePresentationPath(target?.presentationPath, context);
    if (typeof presentationPath !== "string") {
        return presentationPath;
    }
    const checks = normalizeChecks(target?.checks, context, presentationPath);
    if ("ok" in checks) {
        return checks;
    }
    const slideNumbers = normalizeSlideNumbers(target?.slideNumbers, context, presentationPath);
    if ("ok" in slideNumbers) {
        return slideNumbers;
    }
    const renderScale = normalizeRenderScale(target?.renderScale, context, presentationPath);
    if (typeof renderScale !== "number") {
        return renderScale;
    }
    return {
        presentationPath,
        checks,
        slideNumbers,
        renderScale,
    };
}
function ensureScope(target, context) {
    const allowedRoots = cleanList(context?.allowedPresentationRoots).map(normalizeRoot);
    if (allowedRoots.length === 0) {
        return undefined;
    }
    const allowed = allowedRoots.some((root) => target.presentationPath === root || target.presentationPath.startsWith(`${root}/`));
    if (allowed) {
        return undefined;
    }
    return failure("SCOPE_REJECTED", "office.slidesVisionCheck target presentation is outside the allowed presentation roots", "scope", context, target.presentationPath);
}
function ensurePermissions(target, context) {
    const granted = cleanList(context?.grantedPermissions);
    if (granted.length === 0) {
        return undefined;
    }
    const missing = officeSlidesVisionCheckDescriptor.permissionsRequired.filter((permission) => !granted.includes(permission));
    if (missing.length === 0) {
        return undefined;
    }
    return failure("PERMISSION_DENIED", `office.slidesVisionCheck is missing permissions: ${missing.join(", ")}`, "permission", context, target.presentationPath);
}
function blockRealExecution(target, context) {
    if (dryRunEnabled(context)) {
        return undefined;
    }
    return failure("REAL_EXECUTION_BLOCKED", "office.slidesVisionCheck only returns a guarded dry-run plan in the first implementation", "contract", context, target.presentationPath);
}
function actionPreview(target) {
    return [
        "office.slidesVisionCheck",
        "--input",
        target.presentationPath,
        "--checks",
        target.checks.join(","),
        "--slides",
        target.slideNumbers.length === 0 ? "all" : target.slideNumbers.join(","),
        "--render-scale",
        String(target.renderScale),
    ];
}
export function planOfficeSlidesVisionCheck(request = {}) {
    const target = normalizeTarget(request.target, request.context);
    if ("ok" in target) {
        return target;
    }
    const scopeFailure = ensureScope(target, request.context);
    if (scopeFailure !== undefined) {
        return scopeFailure;
    }
    const permissionFailure = ensurePermissions(target, request.context);
    if (permissionFailure !== undefined) {
        return permissionFailure;
    }
    const realExecutionFailure = blockRealExecution(target, request.context);
    if (realExecutionFailure !== undefined) {
        return realExecutionFailure;
    }
    return {
        ok: true,
        toolId: officeSlidesVisionCheckDescriptor.toolId,
        output: {
            kind: "agentCore.basicTool.office.slidesVisionCheck",
            target,
            actionPreview: actionPreview(target),
            dryRun: true,
            executionBlocked: true,
            permissionsRequired: officeSlidesVisionCheckDescriptor.permissionsRequired,
            unsafeSideEffects: false,
            resultEnvelope: {
                findings: [],
                metadata: {
                    vision: "not-executed",
                    formatFamily: "presentation",
                },
            },
        },
        audit: [
            auditEvent("agentCore.basicTool.office.slidesVisionCheck.dryRun", request.context, target.presentationPath, {
                checks: target.checks,
                slideNumbers: target.slideNumbers,
                renderScale: target.renderScale,
            }),
        ],
        events: ["basicTool.office.slidesVisionCheck.dryRun"],
    };
}
