import { resolveAgentConfig } from "../../agents/agent-scope.js";
import { getChannelDock } from "../../channels/dock.js";
import { normalizeChannelId } from "../../channels/plugins/index.js";
import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js";
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
import { formatCliCommand } from "../../cli/command-format.js";
function normalizeAllowToken(value) {
    if (!value)
        return "";
    return value.trim().toLowerCase();
}
function slugAllowToken(value) {
    if (!value)
        return "";
    let text = value.trim().toLowerCase();
    if (!text)
        return "";
    text = text.replace(/^[@#]+/, "");
    text = text.replace(/[\s_]+/g, "-");
    text = text.replace(/[^a-z0-9-]+/g, "-");
    return text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
}
const SENDER_PREFIXES = [
    ...CHAT_CHANNEL_ORDER,
    INTERNAL_MESSAGE_CHANNEL,
    "user",
    "group",
    "channel",
];
const SENDER_PREFIX_RE = new RegExp(`^(${SENDER_PREFIXES.join("|")}):`, "i");
function stripSenderPrefix(value) {
    if (!value)
        return "";
    const trimmed = value.trim();
    return trimmed.replace(SENDER_PREFIX_RE, "");
}
function resolveElevatedAllowList(allowFrom, provider, fallbackAllowFrom) {
    if (!allowFrom)
        return fallbackAllowFrom;
    const value = allowFrom[provider];
    return Array.isArray(value) ? value : fallbackAllowFrom;
}
function isApprovedElevatedSender(params) {
    const rawAllow = resolveElevatedAllowList(params.allowFrom, params.provider, params.fallbackAllowFrom);
    if (!rawAllow || rawAllow.length === 0)
        return false;
    const allowTokens = rawAllow.map((entry) => String(entry).trim()).filter(Boolean);
    if (allowTokens.length === 0)
        return false;
    if (allowTokens.some((entry) => entry === "*"))
        return true;
    const tokens = new Set();
    const addToken = (value) => {
        if (!value)
            return;
        const trimmed = value.trim();
        if (!trimmed)
            return;
        tokens.add(trimmed);
        const normalized = normalizeAllowToken(trimmed);
        if (normalized)
            tokens.add(normalized);
        const slugged = slugAllowToken(trimmed);
        if (slugged)
            tokens.add(slugged);
    };
    addToken(params.ctx.SenderName);
    addToken(params.ctx.SenderUsername);
    addToken(params.ctx.SenderTag);
    addToken(params.ctx.SenderE164);
    addToken(params.ctx.From);
    addToken(stripSenderPrefix(params.ctx.From));
    addToken(params.ctx.To);
    addToken(stripSenderPrefix(params.ctx.To));
    for (const rawEntry of allowTokens) {
        const entry = rawEntry.trim();
        if (!entry)
            continue;
        const stripped = stripSenderPrefix(entry);
        if (tokens.has(entry) || tokens.has(stripped))
            return true;
        const normalized = normalizeAllowToken(stripped);
        if (normalized && tokens.has(normalized))
            return true;
        const slugged = slugAllowToken(stripped);
        if (slugged && tokens.has(slugged))
            return true;
    }
    return false;
}
export function resolveElevatedPermissions(params) {
    const globalConfig = params.cfg.tools?.elevated;
    const agentConfig = resolveAgentConfig(params.cfg, params.agentId)?.tools?.elevated;
    const globalEnabled = globalConfig?.enabled !== false;
    const agentEnabled = agentConfig?.enabled !== false;
    const enabled = globalEnabled && agentEnabled;
    const failures = [];
    if (!globalEnabled)
        failures.push({ gate: "enabled", key: "tools.elevated.enabled" });
    if (!agentEnabled)
        failures.push({
            gate: "enabled",
            key: "agents.list[].tools.elevated.enabled",
        });
    if (!enabled)
        return { enabled, allowed: false, failures };
    if (!params.provider) {
        failures.push({ gate: "provider", key: "ctx.Provider" });
        return { enabled, allowed: false, failures };
    }
    const normalizedProvider = normalizeChannelId(params.provider);
    const dockFallbackAllowFrom = normalizedProvider
        ? getChannelDock(normalizedProvider)?.elevated?.allowFromFallback?.({
            cfg: params.cfg,
            accountId: params.ctx.AccountId,
        })
        : undefined;
    const fallbackAllowFrom = dockFallbackAllowFrom;
    const globalAllowed = isApprovedElevatedSender({
        provider: params.provider,
        ctx: params.ctx,
        allowFrom: globalConfig?.allowFrom,
        fallbackAllowFrom,
    });
    if (!globalAllowed) {
        failures.push({
            gate: "allowFrom",
            key: `tools.elevated.allowFrom.${params.provider}`,
        });
        return { enabled, allowed: false, failures };
    }
    const agentAllowed = agentConfig?.allowFrom
        ? isApprovedElevatedSender({
            provider: params.provider,
            ctx: params.ctx,
            allowFrom: agentConfig.allowFrom,
            fallbackAllowFrom,
        })
        : true;
    if (!agentAllowed) {
        failures.push({
            gate: "allowFrom",
            key: `agents.list[].tools.elevated.allowFrom.${params.provider}`,
        });
    }
    return { enabled, allowed: globalAllowed && agentAllowed, failures };
}
export function formatElevatedUnavailableMessage(params) {
    const lines = [];
    lines.push(`elevated is not available right now (runtime=${params.runtimeSandboxed ? "sandboxed" : "direct"}).`);
    if (params.failures.length > 0) {
        lines.push(`Failing gates: ${params.failures.map((f) => `${f.gate} (${f.key})`).join(", ")}`);
    }
    else {
        lines.push("Failing gates: enabled (tools.elevated.enabled / agents.list[].tools.elevated.enabled), allowFrom (tools.elevated.allowFrom.<provider>).");
    }
    lines.push("Fix-it keys:");
    lines.push("- tools.elevated.enabled");
    lines.push("- tools.elevated.allowFrom.<provider>");
    lines.push("- agents.list[].tools.elevated.enabled");
    lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
    if (params.sessionKey) {
        lines.push(`See: ${formatCliCommand(`openclaw-cn sandbox explain --session ${params.sessionKey}`)}`);
    }
    return lines.join("\n");
}
