export const PLATFORM_TIME_ZONE = 'Asia/Kuala_Lumpur';
export const PLATFORM_TIME_ZONE_OFFSET_MINUTES = 8 * 60;
export const PLATFORM_TIME_ZONE_OFFSET_LABEL = 'UTC+8';
export const MATCH_TODAY_NEXT_DAY_CUTOFF_HOUR = 12;
const PICKER_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/;
function pad2(value) {
    return String(value).padStart(2, '0');
}
function validDate(value) {
    if (value == null)
        return null;
    const date = value instanceof Date ? value : new Date(value);
    return Number.isNaN(date.getTime()) ? null : date;
}
function formatDateTime(date, locale, options) {
    try {
        return new Intl.DateTimeFormat(locale || 'en-US', options).format(date);
    }
    catch {
        return new Intl.DateTimeFormat('en-US', options).format(date);
    }
}
function timeZoneParts(date, timeZone) {
    const parts = new Intl.DateTimeFormat('en-US', {
        timeZone,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hourCycle: 'h23',
    }).formatToParts(date);
    return new Map(parts.map((part) => [part.type, part.value]));
}
function dayKeyInTimeZone(date, timeZone) {
    const parts = timeZoneParts(date, timeZone);
    return `${parts.get('year')}-${parts.get('month')}-${parts.get('day')}`;
}
function offsetMinutesInTimeZone(date, timeZone) {
    const parts = timeZoneParts(date, timeZone);
    const asUtc = Date.UTC(Number(parts.get('year')), Number(parts.get('month')) - 1, Number(parts.get('day')), Number(parts.get('hour')), Number(parts.get('minute')), Number(parts.get('second')));
    return Math.round((asUtc - date.getTime()) / 60000);
}
function zonedWallTimeToUtc(parts, timeZone) {
    const wallMs = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute ?? 0, parts.second ?? 0);
    let utcMs = wallMs;
    for (let i = 0; i < 3; i += 1) {
        utcMs = wallMs - offsetMinutesInTimeZone(new Date(utcMs), timeZone) * 60 * 1000;
    }
    return new Date(utcMs);
}
function localDayParts(date, timeZone) {
    const parts = timeZoneParts(date, timeZone);
    return {
        year: Number(parts.get('year')),
        month: Number(parts.get('month')),
        day: Number(parts.get('day')),
    };
}
function addDaysToParts(parts, days) {
    const d = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + days));
    return {
        year: d.getUTCFullYear(),
        month: d.getUTCMonth() + 1,
        day: d.getUTCDate(),
    };
}
function localTodayMatchWindow(now = new Date(), timeZone) {
    if (timeZone) {
        const today = localDayParts(now, timeZone);
        const tomorrow = addDaysToParts(today, 1);
        return {
            start: zonedWallTimeToUtc({ ...today, hour: 0 }, timeZone),
            end: zonedWallTimeToUtc({ ...tomorrow, hour: MATCH_TODAY_NEXT_DAY_CUTOFF_HOUR }, timeZone),
        };
    }
    const start = new Date(now);
    start.setHours(0, 0, 0, 0);
    const end = new Date(start);
    end.setDate(end.getDate() + 1);
    end.setHours(MATCH_TODAY_NEXT_DAY_CUTOFF_HOUR, 0, 0, 0);
    return { start, end };
}
function parsePickerParts(value) {
    const match = PICKER_DATETIME_RE.exec(value.trim());
    if (!match)
        return null;
    const [, y, mo, d, h, mi, s = '00'] = match;
    const parts = {
        year: Number(y),
        month: Number(mo),
        day: Number(d),
        hour: Number(h),
        minute: Number(mi),
        second: Number(s),
    };
    const wallAsUtc = new Date(Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second));
    if (wallAsUtc.getUTCFullYear() !== parts.year ||
        wallAsUtc.getUTCMonth() !== parts.month - 1 ||
        wallAsUtc.getUTCDate() !== parts.day ||
        wallAsUtc.getUTCHours() !== parts.hour ||
        wallAsUtc.getUTCMinutes() !== parts.minute ||
        wallAsUtc.getUTCSeconds() !== parts.second) {
        return null;
    }
    return parts;
}
export function platformPickerDateTimeToIso(value) {
    const trimmed = value.trim();
    if (!trimmed)
        return '';
    const parts = parsePickerParts(trimmed);
    if (!parts)
        return trimmed;
    const platformWallMs = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second);
    return new Date(platformWallMs - PLATFORM_TIME_ZONE_OFFSET_MINUTES * 60 * 1000).toISOString();
}
export function isoToPlatformPickerDateTime(value) {
    if (!value?.trim())
        return '';
    const date = validDate(value);
    if (!date)
        return value.slice(0, 19);
    const platform = new Date(date.getTime() + PLATFORM_TIME_ZONE_OFFSET_MINUTES * 60 * 1000);
    return [
        platform.getUTCFullYear(),
        pad2(platform.getUTCMonth() + 1),
        pad2(platform.getUTCDate()),
    ].join('-') + `T${pad2(platform.getUTCHours())}:${pad2(platform.getUTCMinutes())}:${pad2(platform.getUTCSeconds())}`;
}
export function isValidIsoDateTime(value) {
    return validDate(value) !== null;
}
export function formatPlatformMatchDateTime(value, locale = 'en-US') {
    const date = validDate(value);
    if (!date)
        return '';
    const formatted = formatDateTime(date, locale, {
        timeZone: PLATFORM_TIME_ZONE,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
    });
    return `${formatted} ${PLATFORM_TIME_ZONE_OFFSET_LABEL}`;
}
export function getLocalGmtOffsetLabel(value = new Date(), timeZone) {
    const date = validDate(value) ?? new Date();
    const offsetMinutes = timeZone
        ? offsetMinutesInTimeZone(date, timeZone)
        : -date.getTimezoneOffset();
    const sign = offsetMinutes >= 0 ? '+' : '-';
    const abs = Math.abs(offsetMinutes);
    const hours = Math.floor(abs / 60);
    const minutes = abs % 60;
    return minutes === 0 ? `GMT${sign}${hours}` : `GMT${sign}${hours}:${pad2(minutes)}`;
}
export function isSameLocalCalendarDay(value, now = new Date(), timeZone) {
    const date = validDate(value);
    if (!date)
        return false;
    if (timeZone) {
        return dayKeyInTimeZone(date, timeZone) === dayKeyInTimeZone(now, timeZone);
    }
    return (date.getFullYear() === now.getFullYear() &&
        date.getMonth() === now.getMonth() &&
        date.getDate() === now.getDate());
}
export function isInLocalToday(value, now = new Date(), timeZone) {
    const date = validDate(value);
    if (!date)
        return false;
    if (timeZone) {
        return dayKeyInTimeZone(date, timeZone) === dayKeyInTimeZone(now, timeZone);
    }
    const start = new Date(now);
    start.setHours(0, 0, 0, 0);
    const end = new Date(start);
    end.setDate(end.getDate() + 1);
    return date >= start && date < end;
}
export function isAfterLocalToday(value, now = new Date(), timeZone) {
    const date = validDate(value);
    if (!date)
        return false;
    if (timeZone) {
        return dayKeyInTimeZone(date, timeZone) > dayKeyInTimeZone(now, timeZone);
    }
    const end = new Date(now);
    end.setHours(0, 0, 0, 0);
    end.setDate(end.getDate() + 1);
    return date >= end;
}
export function isInLocalTodayMatchWindow(value, now = new Date(), timeZone) {
    const date = validDate(value);
    if (!date)
        return false;
    const { start, end } = localTodayMatchWindow(now, timeZone);
    return date >= start && date < end;
}
export function isAfterLocalTodayMatchWindow(value, now = new Date(), timeZone) {
    const date = validDate(value);
    if (!date)
        return false;
    const { end } = localTodayMatchWindow(now, timeZone);
    return date >= end;
}
export function formatLocalMatchDateTime(value, locale = 'en-US', options = {}) {
    const date = validDate(value);
    if (!date)
        return '';
    const variant = options.variant ?? 'compact';
    const time = formatDateTime(date, locale, {
        ...(options.timeZone ? { timeZone: options.timeZone } : {}),
        hour: '2-digit',
        minute: '2-digit',
        ...(options.includeSeconds ? { second: '2-digit' } : {}),
    });
    let text;
    if (variant === 'compact') {
        if (options.todayLabel && isSameLocalCalendarDay(date, new Date(), options.timeZone)) {
            text = `${options.todayLabel} ${time}`;
        }
        else {
            const day = formatDateTime(date, locale, {
                ...(options.timeZone ? { timeZone: options.timeZone } : {}),
                month: 'numeric',
                day: 'numeric',
            });
            text = `${day} ${time}`;
        }
    }
    else {
        const day = formatDateTime(date, locale, {
            ...(options.timeZone ? { timeZone: options.timeZone } : {}),
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
        });
        text = `${day} ${time}`;
    }
    if (options.includeTimeZone === false)
        return text;
    return `${text} ${getLocalGmtOffsetLabel(date, options.timeZone)}`;
}
