import { randomUUID } from "node:crypto";
import { unlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { InputFile } from "grammy";
import { getAgentActivityLog } from "../../agents/shared/agent-activity.js";
import { capabilitiesOf, filterActivityEvents, formatLocalDateTime, formatLockOwner, labelOf, parseActivityOptions, renderActivityTimeline, renderAuditEvents, renderProgressHTML, renderProgressPlain, renderSessionLocks, } from "../shared/bot-rendering.js";
import { escapeHTML } from "../../core/format.js";
import { renderSessionInfoHTML, renderSessionInfoPlain } from "../shared/session-format.js";
import { canWriteWithLock } from "../../access/session-locks.js";
import { chatBucket, safeReply } from "./telegram-output.js";
import { telegramRateLimiter } from "./telegram-rate-limit.js";
export function registerTelegramOperationalCommands(options) {
    const { bot, config, promptStore, auditLog, lockStore, turnProgress, } = options;
    bot.command(["tasks", "progress"], async (ctx) => {
        const contextSession = await options.getContextSession(ctx, { deferThreadStart: true });
        if (!contextSession) {
            return;
        }
        const progress = turnProgress.get(contextSession.contextKey);
        const queue = promptStore.list(contextSession.contextKey);
        const externalActivity = options.getExternalActivity(contextSession.session);
        const busyState = {
            ...options.getBusyState(contextSession.contextKey),
            external: Boolean(externalActivity?.active),
        };
        const info = contextSession.session.getInfo();
        const plain = renderProgressPlain(progress, queue.length, busyState, info);
        const html = renderProgressHTML(progress, queue.length, busyState, info);
        await safeReply(ctx, html, { fallbackText: plain });
    });
    bot.command("activity", async (ctx) => {
        const contextSession = await options.getContextSession(ctx, { deferThreadStart: true });
        if (!contextSession) {
            return;
        }
        const info = contextSession.session.getInfo();
        if (!capabilitiesOf(info).activityLog) {
            const text = `${labelOf(info)} activity timelines are not available yet.`;
            await safeReply(ctx, escapeHTML(text), { fallbackText: text });
            return;
        }
        const threadId = contextSession.session.getActiveThreadId();
        if (!threadId) {
            await safeReply(ctx, escapeHTML("No active thread yet."), { fallbackText: "No active thread yet." });
            return;
        }
        const activityOptions = parseActivityOptions((ctx.message?.text ?? "").replace(/^\/activity(?:@\w+)?\s*/i, "").trim());
        const events = filterActivityEvents(getAgentActivityLog(contextSession.session, config, activityOptions.exportFile ? 200 : activityOptions.limit), activityOptions);
        const rendered = renderActivityTimeline(threadId, events, activityOptions);
        if (activityOptions.exportFile && ctx.chat) {
            const exportPath = path.join(tmpdir(), `nordrelay-activity-${threadId}-${randomUUID().slice(0, 8)}.txt`);
            await writeFile(exportPath, rendered.plain, "utf8");
            try {
                await telegramRateLimiter.run(chatBucket(ctx.chat.id), "sendDocument", () => ctx.api.sendDocument(ctx.chat.id, new InputFile(exportPath, path.basename(exportPath)), {
                    ...(ctx.message?.message_thread_id ? { message_thread_id: ctx.message.message_thread_id } : {}),
                }));
            }
            finally {
                await unlink(exportPath).catch(() => { });
            }
            return;
        }
        await safeReply(ctx, rendered.html, { fallbackText: rendered.plain });
    });
    bot.command("audit", async (ctx) => {
        const rawText = ctx.message?.text ?? "";
        const limitArg = rawText.replace(/^\/audit(?:@\w+)?\s*/i, "").trim();
        const limit = /^\d+$/.test(limitArg) ? Number(limitArg) : 20;
        const events = auditLog.list(limit);
        const rendered = renderAuditEvents(events);
        await safeReply(ctx, rendered.html, { fallbackText: rendered.plain });
    });
    bot.command("lock", async (ctx) => {
        const contextSession = await options.getContextSession(ctx, { deferThreadStart: true });
        if (!contextSession) {
            return;
        }
        const { contextKey, session } = contextSession;
        const owner = options.getLockOwner(ctx);
        if (!owner) {
            const text = "You must be authenticated before locking a session.";
            await safeReply(ctx, escapeHTML(text), { fallbackText: text });
            return;
        }
        const existing = lockStore.get(contextKey);
        if (existing && !canWriteWithLock(existing, owner.userId, options.isAdminUser(ctx))) {
            const text = `Session is already locked by ${formatLockOwner(existing)}.`;
            await safeReply(ctx, escapeHTML(text), { fallbackText: text });
            return;
        }
        const lock = lockStore.set(contextKey, owner, config.sessionLockTtlMs);
        options.auditContext(ctx, contextKey, session, {
            action: "lock_updated",
            status: "ok",
            detail: `locked by ${lock.ownerUserId}`,
        });
        const text = `Session locked by ${formatLockOwner(lock)}${lock.expiresAt ? ` until ${formatLocalDateTime(new Date(lock.expiresAt))}` : ""}.`;
        await safeReply(ctx, escapeHTML(text), { fallbackText: text });
    });
    bot.command("unlock", async (ctx) => {
        const contextSession = await options.getContextSession(ctx, { deferThreadStart: true });
        if (!contextSession) {
            return;
        }
        const { contextKey, session } = contextSession;
        const lock = lockStore.get(contextKey);
        const owner = options.getLockOwner(ctx);
        if (lock && !canWriteWithLock(lock, owner?.userId, options.isAdminUser(ctx))) {
            const text = `Only ${formatLockOwner(lock)} or an admin can unlock this session.`;
            await safeReply(ctx, escapeHTML(text), { fallbackText: text });
            return;
        }
        const removed = lockStore.clear(contextKey);
        options.auditContext(ctx, contextKey, session, {
            action: "lock_updated",
            status: "ok",
            detail: removed ? "unlocked" : "no lock",
        });
        const text = removed ? "Session lock released." : "No active lock for this session.";
        await safeReply(ctx, escapeHTML(text), { fallbackText: text });
    });
    bot.command("locks", async (ctx) => {
        const locks = lockStore.list();
        const rendered = renderSessionLocks(locks);
        await safeReply(ctx, rendered.html, { fallbackText: rendered.plain });
    });
    bot.command("sync", async (ctx) => {
        const contextSession = await options.getContextSession(ctx, { deferThreadStart: true });
        if (!contextSession) {
            return;
        }
        const sessionInfo = contextSession.session.getInfo();
        if (!capabilitiesOf(sessionInfo).externalActivity) {
            const plain = [`${labelOf(sessionInfo)} has no external CLI state watcher to sync.`, "", renderSessionInfoPlain(sessionInfo)].join("\n");
            const html = [`<b>${escapeHTML(labelOf(sessionInfo))} has no external CLI state watcher to sync.</b>`, "", renderSessionInfoHTML(sessionInfo)].join("\n");
            await safeReply(ctx, html, { fallbackText: plain });
            return;
        }
        const result = contextSession.session.syncFromAgentState({ reattach: true });
        if (result.changed) {
            options.updateSessionMetadata(contextSession.contextKey, contextSession.session);
        }
        const fields = result.changedFields.length > 0 ? result.changedFields.join(", ") : "none";
        const plain = [
            result.changed ? `Synced from ${labelOf(sessionInfo)} state.` : "Already in sync.",
            `Changed: ${fields}`,
            `Reattached: ${result.reattached ? "yes" : "no"}`,
            "",
            renderSessionInfoPlain(result.info),
        ].join("\n");
        const html = [
            result.changed ? `<b>Synced from ${escapeHTML(labelOf(sessionInfo))} state.</b>` : "<b>Already in sync.</b>",
            `<b>Changed:</b> <code>${escapeHTML(fields)}</code>`,
            `<b>Reattached:</b> <code>${result.reattached ? "yes" : "no"}</code>`,
            "",
            renderSessionInfoHTML(result.info),
        ].join("\n");
        await safeReply(ctx, html, { fallbackText: plain });
    });
}
