const formatByLocale: any = {};

export default function getDateTimeFormatPattern(locale: string, needTime?: boolean) {
    const cachedFormat = formatByLocale[`${locale}_${!!needTime}`];
    if (cachedFormat) {
        return cachedFormat as string;
    }

    const getPatternForPart = (part: Intl.DateTimeFormatPart) => {
        switch (part.type) {
            case "day":
                return "d".repeat(part.value.length);
            case "month":
                return "M".repeat(part.value.length);
            case "year":
                return "y".repeat(part.value.length);
            case "hour":
                return "H".repeat(part.value.length);
            case "minute":
                return "m".repeat(part.value.length);
            case "dayPeriod":
                return "a";
            case "literal":
                return part.value === ", " ? " " : part.value;
        }
    };

    const options = {
        year: "numeric",
        month: "numeric",
        day: "numeric",
        hour: needTime ? "numeric" : undefined,
        minute: needTime ? "numeric" : undefined,
        timeZone: "UTC",
    } as Intl.DateTimeFormatOptions;

    let dateTimeFormat: Intl.DateTimeFormat;
    try {
        dateTimeFormat = new Intl.DateTimeFormat(locale, options);
    } catch {
        dateTimeFormat = new Intl.DateTimeFormat("en-US", options);
    }

    const dateParts = dateTimeFormat.formatToParts(Date.UTC(2024, 1, 1, 1, 1));
    let format = dateParts.map(getPatternForPart).join("");
    if (dateParts.some(o => o.type === "dayPeriod")) {
        format = format.replace(/H/g, "h");
    }

    formatByLocale[`${locale}_${!!needTime}`] = format;

    return format;
}
