import { unprocessable } from "../errors.js";
import { parseCron, validateCron } from "./cron.js";
export const OPEN_ISSUE_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked"];
export const LIVE_HEARTBEAT_RUN_STATUSES = ["queued", "running"];
export const TERMINAL_ISSUE_STATUSES = new Set(["done", "cancelled"]);
export const MAX_CATCH_UP_RUNS = 25;
const WEEKDAY_INDEX = {
    Sun: 0,
    Mon: 1,
    Tue: 2,
    Wed: 3,
    Thu: 4,
    Fri: 5,
    Sat: 6,
};
export function assertTimeZone(timeZone) {
    try {
        new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date());
    }
    catch {
        throw unprocessable(`Invalid timezone: ${timeZone}`);
    }
}
function floorToMinute(date) {
    const copy = new Date(date.getTime());
    copy.setUTCSeconds(0, 0);
    return copy;
}
function getZonedMinuteParts(date, timeZone) {
    const formatter = new Intl.DateTimeFormat("en-US", {
        timeZone,
        hour12: false,
        year: "numeric",
        month: "numeric",
        day: "numeric",
        hour: "numeric",
        minute: "numeric",
        weekday: "short",
    });
    const parts = formatter.formatToParts(date);
    const map = Object.fromEntries(parts.map((part) => [part.type, part.value]));
    const weekday = WEEKDAY_INDEX[map.weekday ?? ""];
    if (weekday == null) {
        throw new Error(`Unable to resolve weekday for timezone ${timeZone}`);
    }
    return {
        year: Number(map.year),
        month: Number(map.month),
        day: Number(map.day),
        hour: Number(map.hour),
        minute: Number(map.minute),
        weekday,
    };
}
function matchesCronMinute(expression, timeZone, date) {
    const cron = parseCron(expression);
    const parts = getZonedMinuteParts(date, timeZone);
    return (cron.minutes.includes(parts.minute) &&
        cron.hours.includes(parts.hour) &&
        cron.daysOfMonth.includes(parts.day) &&
        cron.months.includes(parts.month) &&
        cron.daysOfWeek.includes(parts.weekday));
}
export function nextCronTickInTimeZone(expression, timeZone, after) {
    const trimmed = expression.trim();
    assertTimeZone(timeZone);
    const error = validateCron(trimmed);
    if (error) {
        throw unprocessable(error);
    }
    const cursor = floorToMinute(after);
    cursor.setUTCMinutes(cursor.getUTCMinutes() + 1);
    const limit = 366 * 24 * 60 * 5;
    for (let i = 0; i < limit; i += 1) {
        if (matchesCronMinute(trimmed, timeZone, cursor)) {
            return new Date(cursor.getTime());
        }
        cursor.setUTCMinutes(cursor.getUTCMinutes() + 1);
    }
    return null;
}
export function nextResultText(status, issueId) {
    if (status === "issue_created" && issueId)
        return `Created execution issue ${issueId}`;
    if (status === "running")
        return "Started chat run";
    if (status === "coalesced")
        return "Coalesced into an existing live run";
    if (status === "skipped")
        return "Skipped because a live run already exists";
    if (status === "completed")
        return "Run completed";
    if (status === "failed")
        return "Execution failed";
    return status;
}
export function normalizeWebhookTimestampMs(rawTimestamp) {
    const parsed = Number(rawTimestamp);
    if (!Number.isFinite(parsed))
        return null;
    return parsed > 1e12 ? parsed : parsed * 1000;
}
//# sourceMappingURL=automations.scheduler.js.map