"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseWorkflowDurationToMilliseconds = parseWorkflowDurationToMilliseconds;
exports.normalizeWorkflowRunAt = normalizeWorkflowRunAt;
exports.resolveWorkflowRunAfter = resolveWorkflowRunAfter;
exports.isDueAt = isDueAt;
exports.isWorkflowRunAtInputShape = isWorkflowRunAtInputShape;
exports.renderDateTimePartsInZone = renderDateTimePartsInZone;
const display_time_1 = require("../i18n/display-time");
const DURATION_PART_PATTERN = /(\d+)(ms|s|m|h|d|w)/giu;
const EXPLICIT_TIME_ZONE_PATTERN = /(?:z|[+-]\d{2}:\d{2})$/iu;
const LOCAL_DATE_TIME_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/u;
function parseWorkflowDurationToMilliseconds(value) {
    const text = value.trim();
    if (text.length === 0) {
        return null;
    }
    let total = 0;
    let matchedLength = 0;
    for (const match of text.matchAll(DURATION_PART_PATTERN)) {
        const whole = match[0];
        const amountText = match[1];
        const unit = match[2]?.toLowerCase();
        if (!whole || !amountText || !unit) {
            continue;
        }
        matchedLength += whole.length;
        total += Number(amountText) * resolveDurationUnitMilliseconds(unit);
    }
    return matchedLength === text.length && total > 0 ? total : null;
}
function normalizeWorkflowRunAt(value, displayTimeZone) {
    const text = value.trim();
    if (text.length === 0) {
        return null;
    }
    if (EXPLICIT_TIME_ZONE_PATTERN.test(text)) {
        const parsed = Date.parse(text);
        return Number.isFinite(parsed) ? new Date(parsed).toISOString() : null;
    }
    return normalizeLocalWorkflowRunAt(text, displayTimeZone);
}
function resolveWorkflowRunAfter(value, acceptedAt) {
    const durationMs = parseWorkflowDurationToMilliseconds(value);
    const acceptedMs = Date.parse(acceptedAt);
    if (durationMs === null || !Number.isFinite(acceptedMs)) {
        return null;
    }
    return new Date(acceptedMs + durationMs).toISOString();
}
function isDueAt(value, now = new Date()) {
    if (value === null || value === undefined) {
        return false;
    }
    return Date.parse(value) <= now.getTime();
}
function isWorkflowRunAtInputShape(value) {
    const text = value.trim();
    if (text.length === 0) {
        return false;
    }
    if (EXPLICIT_TIME_ZONE_PATTERN.test(text)) {
        return Number.isFinite(Date.parse(text));
    }
    return LOCAL_DATE_TIME_PATTERN.test(text);
}
function normalizeLocalWorkflowRunAt(value, displayTimeZone) {
    if (!(0, display_time_1.isSupportedTimeZone)(displayTimeZone)) {
        return null;
    }
    const match = value.match(LOCAL_DATE_TIME_PATTERN);
    if (match === null) {
        return null;
    }
    const parts = {
        year: Number(match[1]),
        month: Number(match[2]),
        day: Number(match[3]),
        hour: Number(match[4]),
        minute: Number(match[5]),
        second: Number(match[6] ?? "0"),
        millisecond: Number((match[7] ?? "0").padEnd(3, "0")),
    };
    if (!isLocalDateTimePartsValid(parts)) {
        return null;
    }
    const localMs = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, parts.millisecond);
    let utcMs = localMs;
    for (let index = 0; index < 4; index += 1) {
        utcMs = localMs - resolveTimeZoneOffsetMilliseconds(utcMs, displayTimeZone);
    }
    const rendered = renderDateTimePartsInZone(utcMs, displayTimeZone);
    if (rendered === null ||
        rendered.year !== parts.year ||
        rendered.month !== parts.month ||
        rendered.day !== parts.day ||
        rendered.hour !== parts.hour ||
        rendered.minute !== parts.minute ||
        rendered.second !== parts.second) {
        return null;
    }
    return new Date(utcMs).toISOString();
}
function isLocalDateTimePartsValid(parts) {
    if (!Number.isInteger(parts.year) ||
        !Number.isInteger(parts.month) ||
        !Number.isInteger(parts.day) ||
        !Number.isInteger(parts.hour) ||
        !Number.isInteger(parts.minute) ||
        !Number.isInteger(parts.second) ||
        !Number.isInteger(parts.millisecond) ||
        parts.month < 1 ||
        parts.month > 12 ||
        parts.hour < 0 ||
        parts.hour > 23 ||
        parts.minute < 0 ||
        parts.minute > 59 ||
        parts.second < 0 ||
        parts.second > 59 ||
        parts.millisecond < 0 ||
        parts.millisecond > 999) {
        return false;
    }
    const date = new Date(Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, parts.millisecond));
    return (date.getUTCFullYear() === parts.year &&
        date.getUTCMonth() === parts.month - 1 &&
        date.getUTCDate() === parts.day);
}
function resolveDurationUnitMilliseconds(unit) {
    switch (unit) {
        case "ms":
            return 1;
        case "s":
            return 1_000;
        case "m":
            return 60_000;
        case "h":
            return 3_600_000;
        case "d":
            return 86_400_000;
        case "w":
            return 604_800_000;
        default:
            return 0;
    }
}
function resolveTimeZoneOffsetMilliseconds(utcMs, timeZone) {
    const rendered = renderDateTimePartsInZone(utcMs, timeZone);
    if (rendered === null) {
        return 0;
    }
    return (Date.UTC(rendered.year, rendered.month - 1, rendered.day, rendered.hour, rendered.minute, rendered.second) - utcMs);
}
function renderDateTimePartsInZone(utcMs, timeZone) {
    let formatter;
    try {
        formatter = new Intl.DateTimeFormat("en-CA", {
            timeZone,
            year: "numeric",
            month: "2-digit",
            day: "2-digit",
            hour: "2-digit",
            minute: "2-digit",
            second: "2-digit",
            hour12: false,
            hourCycle: "h23",
        });
    }
    catch {
        return null;
    }
    const values = new Map(formatter
        .formatToParts(new Date(utcMs))
        .map((part) => [part.type, part.value]));
    const year = Number(values.get("year"));
    const month = Number(values.get("month"));
    const day = Number(values.get("day"));
    const hour = Number(values.get("hour"));
    const minute = Number(values.get("minute"));
    const second = Number(values.get("second"));
    if (![year, month, day, hour, minute, second].every(Number.isFinite)) {
        return null;
    }
    const dayOfWeek = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
    return { year, month, day, hour, minute, second, dayOfWeek };
}
