import { danger } from "../../globals.js";
import { sanitizeAgentId } from "../../routing/session-key.js";
import { defaultRuntime } from "../../runtime.js";
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
import { parsePositiveIntOrUndefined } from "../program/helpers.js";
import { getCronChannelOptions, parseAt, parseDurationMs, printCronList, warnIfCronSchedulerDisabled, } from "./shared.js";
export function registerCronStatusCommand(cron) {
    addGatewayClientOptions(cron
        .command("status")
        .description("Show cron scheduler status")
        .option("--json", "Output JSON", false)
        .action(async (opts) => {
        try {
            const res = await callGatewayFromCli("cron.status", opts, {});
            defaultRuntime.log(JSON.stringify(res, null, 2));
        }
        catch (err) {
            defaultRuntime.error(danger(String(err)));
            defaultRuntime.exit(1);
        }
    }));
}
export function registerCronListCommand(cron) {
    addGatewayClientOptions(cron
        .command("list")
        .description("List cron jobs")
        .option("--all", "Include disabled jobs", false)
        .option("--json", "Output JSON", false)
        .action(async (opts) => {
        try {
            const res = await callGatewayFromCli("cron.list", opts, {
                includeDisabled: Boolean(opts.all),
            });
            if (opts.json) {
                defaultRuntime.log(JSON.stringify(res, null, 2));
                return;
            }
            const jobs = res?.jobs ?? [];
            printCronList(jobs, defaultRuntime);
        }
        catch (err) {
            defaultRuntime.error(danger(String(err)));
            defaultRuntime.exit(1);
        }
    }));
}
export function registerCronAddCommand(cron) {
    addGatewayClientOptions(cron
        .command("add")
        .alias("create")
        .description("Add a cron job")
        .requiredOption("--name <name>", "Job name")
        .option("--description <text>", "Optional description")
        .option("--disabled", "Create job disabled", false)
        .option("--delete-after-run", "Delete one-shot job after it succeeds", false)
        .option("--keep-after-run", "Keep one-shot job after it succeeds", false)
        .option("--agent <id>", "Agent id for this job")
        .option("--session <target>", "Session target (main|isolated)")
        .option("--wake <mode>", "Wake mode (now|next-heartbeat)", "next-heartbeat")
        .option("--at <when>", "Run once at time (ISO) or +duration (e.g. 20m)")
        .option("--every <duration>", "Run every duration (e.g. 10m, 1h)")
        .option("--cron <expr>", "Cron expression (5-field)")
        .option("--tz <iana>", "Timezone for cron expressions (IANA)", "")
        .option("--system-event <text>", "System event payload (main session)")
        .option("--message <text>", "Agent message payload")
        .option("--thinking <level>", "Thinking level for agent jobs (off|minimal|low|medium|high)")
        .option("--model <model>", "Model override for agent jobs (provider/model or alias)")
        .option("--timeout-seconds <n>", "Timeout seconds for agent jobs")
        .option("--announce", "Announce summary to a chat (subagent-style)", false)
        .option("--deliver", "Deliver full output to a chat (required when using last-route delivery without --to)")
        .option("--no-deliver", "Disable delivery and skip main-session summary")
        .option("--channel <channel>", `Delivery channel (${getCronChannelOptions()})`, "last")
        .option("--to <dest>", "Delivery destination (E.164, Telegram chatId, or Discord channel/user)")
        .option("--best-effort-deliver", "Do not fail the job if delivery fails", false)
        .option("--post-prefix <prefix>", "Prefix for main-session post", "Cron")
        .option("--post-mode <mode>", "What to post back to main for isolated jobs (summary|full)", "summary")
        .option("--post-max-chars <n>", "Max chars when --post-mode=full (default 8000)", "8000")
        .option("--json", "Output JSON", false)
        .action(async (opts, cmd) => {
        try {
            const schedule = (() => {
                const at = typeof opts.at === "string" ? opts.at : "";
                const every = typeof opts.every === "string" ? opts.every : "";
                const cronExpr = typeof opts.cron === "string" ? opts.cron : "";
                const chosen = [Boolean(at), Boolean(every), Boolean(cronExpr)].filter(Boolean).length;
                if (chosen !== 1) {
                    throw new Error("Choose exactly one schedule: --at, --every, or --cron");
                }
                if (at) {
                    const atMs = parseAt(at);
                    if (!atMs) {
                        throw new Error("Invalid --at; use ISO time or duration like 20m");
                    }
                    return { kind: "at", atMs };
                }
                if (every) {
                    const everyMs = parseDurationMs(every);
                    if (!everyMs) {
                        throw new Error("Invalid --every; use e.g. 10m, 1h, 1d");
                    }
                    return { kind: "every", everyMs };
                }
                return {
                    kind: "cron",
                    expr: cronExpr,
                    tz: typeof opts.tz === "string" && opts.tz.trim() ? opts.tz.trim() : undefined,
                };
            })();
            const wakeModeRaw = typeof opts.wake === "string" ? opts.wake : "next-heartbeat";
            const wakeMode = wakeModeRaw.trim() || "next-heartbeat";
            if (wakeMode !== "now" && wakeMode !== "next-heartbeat") {
                throw new Error("--wake must be now or next-heartbeat");
            }
            const agentId = typeof opts.agent === "string" && opts.agent.trim()
                ? sanitizeAgentId(opts.agent.trim())
                : undefined;
            const hasAnnounce = Boolean(opts.announce);
            const hasDeliver = opts.deliver === true;
            const hasNoDeliver = opts.deliver === false;
            const deliveryFlagCount = [hasAnnounce, hasDeliver, hasNoDeliver].filter(Boolean).length;
            if (deliveryFlagCount > 1) {
                throw new Error("Choose at most one of --announce, --deliver, or --no-deliver");
            }
            const payload = (() => {
                const systemEvent = typeof opts.systemEvent === "string" ? opts.systemEvent.trim() : "";
                const message = typeof opts.message === "string" ? opts.message.trim() : "";
                const chosen = [Boolean(systemEvent), Boolean(message)].filter(Boolean).length;
                if (chosen !== 1) {
                    throw new Error("Choose exactly one payload: --system-event or --message");
                }
                if (systemEvent) {
                    return { kind: "systemEvent", text: systemEvent };
                }
                const timeoutSeconds = parsePositiveIntOrUndefined(opts.timeoutSeconds);
                return {
                    kind: "agentTurn",
                    message,
                    model: typeof opts.model === "string" && opts.model.trim() ? opts.model.trim() : undefined,
                    thinking: typeof opts.thinking === "string" && opts.thinking.trim()
                        ? opts.thinking.trim()
                        : undefined,
                    timeoutSeconds: timeoutSeconds && Number.isFinite(timeoutSeconds) ? timeoutSeconds : undefined,
                };
            })();
            const optionSource = typeof cmd?.getOptionValueSource === "function"
                ? (name) => cmd.getOptionValueSource(name)
                : () => undefined;
            const sessionSource = optionSource("session");
            const sessionTargetRaw = typeof opts.session === "string" ? opts.session.trim() : "";
            const inferredSessionTarget = payload.kind === "agentTurn" ? "isolated" : "main";
            const sessionTarget = sessionSource === "cli" ? sessionTargetRaw || "" : inferredSessionTarget;
            if (sessionTarget !== "main" && sessionTarget !== "isolated") {
                throw new Error("--session must be main or isolated");
            }
            if (opts.deleteAfterRun && opts.keepAfterRun) {
                throw new Error("Choose --delete-after-run or --keep-after-run, not both");
            }
            if (sessionTarget === "main" && payload.kind !== "systemEvent") {
                throw new Error("Main jobs require --system-event (systemEvent).");
            }
            if (sessionTarget === "isolated" && payload.kind !== "agentTurn") {
                throw new Error("Isolated jobs require --message (agentTurn).");
            }
            if ((opts.announce || typeof opts.deliver === "boolean") &&
                (sessionTarget !== "isolated" || payload.kind !== "agentTurn")) {
                throw new Error("--announce/--deliver/--no-deliver require --session isolated.");
            }
            const hasLegacyPostConfig = optionSource("postPrefix") === "cli" ||
                optionSource("postMode") === "cli" ||
                optionSource("postMaxChars") === "cli";
            if (hasLegacyPostConfig &&
                (sessionTarget !== "isolated" || payload.kind !== "agentTurn")) {
                throw new Error("--post-prefix/--post-mode/--post-max-chars require --session isolated.");
            }
            if (hasLegacyPostConfig && (hasAnnounce || hasDeliver || hasNoDeliver)) {
                throw new Error("Choose legacy main-summary options or a delivery mode (not both).");
            }
            const isolation = sessionTarget === "isolated" && hasLegacyPostConfig
                ? {
                    postToMainPrefix: typeof opts.postPrefix === "string" && opts.postPrefix.trim()
                        ? opts.postPrefix.trim()
                        : "Cron",
                    postToMainMode: opts.postMode === "full" || opts.postMode === "summary"
                        ? opts.postMode
                        : undefined,
                    postToMainMaxChars: opts.postMode === "full" &&
                        typeof opts.postMaxChars === "string" &&
                        /^\d+$/.test(opts.postMaxChars)
                        ? Number.parseInt(opts.postMaxChars, 10)
                        : undefined,
                }
                : undefined;
            const deliveryMode = sessionTarget === "isolated" && payload.kind === "agentTurn" && !hasLegacyPostConfig
                ? hasAnnounce
                    ? "announce"
                    : hasDeliver
                        ? "deliver"
                        : hasNoDeliver
                            ? "none"
                            : "announce"
                : undefined;
            const nameRaw = typeof opts.name === "string" ? opts.name : "";
            const name = nameRaw.trim();
            if (!name) {
                throw new Error("--name is required");
            }
            const description = typeof opts.description === "string" && opts.description.trim()
                ? opts.description.trim()
                : undefined;
            const params = {
                name,
                description,
                enabled: !opts.disabled,
                deleteAfterRun: opts.deleteAfterRun ? true : opts.keepAfterRun ? false : undefined,
                agentId,
                schedule,
                sessionTarget,
                wakeMode,
                payload,
                delivery: deliveryMode
                    ? {
                        mode: deliveryMode,
                        channel: typeof opts.channel === "string" && opts.channel.trim()
                            ? opts.channel.trim()
                            : undefined,
                        to: typeof opts.to === "string" && opts.to.trim() ? opts.to.trim() : undefined,
                        bestEffort: deliveryMode === "deliver" && opts.bestEffortDeliver ? true : undefined,
                    }
                    : undefined,
                isolation,
            };
            const res = await callGatewayFromCli("cron.add", opts, params);
            defaultRuntime.log(JSON.stringify(res, null, 2));
            await warnIfCronSchedulerDisabled(opts);
        }
        catch (err) {
            defaultRuntime.error(danger(String(err)));
            defaultRuntime.exit(1);
        }
    }));
}
