import { config } from "./config";

/**
 * Dates and times in the site's own format (Settings > General), the way
 * WordPress prints them elsewhere in the admin and in the emails. The
 * formats are PHP date() letters; the common ones are covered and any other
 * letter is printed as is.
 */

const pad = (n: number) => String(n).padStart(2, "0");

function names(locale: string, options: Intl.DateTimeFormatOptions, d: Date): string {
    try {
        return new Intl.DateTimeFormat(locale, options).format(d);
    } catch {
        return new Intl.DateTimeFormat("en-US", options).format(d);
    }
}

function suffix(day: number): string {
    if (day % 100 >= 11 && day % 100 <= 13) {
        return "th";
    }

    return ["th", "st", "nd", "rd"][day % 10] ?? "th";
}

/** Formats `d` with PHP date() letters. A backslash escapes the next letter. */
export function phpDate(format: string, d: Date, locale = "en-US"): string {
    let out = "";
    for (let i = 0; i < format.length; i++) {
        const c = format[i];
        if (c === "\\" && i + 1 < format.length) {
            out += format[++i];
            continue;
        }
        const h = d.getHours();
        switch (c) {
            case "d":
                out += pad(d.getDate());
                break;
            case "j":
                out += String(d.getDate());
                break;
            case "S":
                out += suffix(d.getDate());
                break;
            case "D":
                out += names(locale, { weekday: "short" }, d);
                break;
            case "l":
                out += names(locale, { weekday: "long" }, d);
                break;
            case "N":
                out += String(d.getDay() || 7);
                break;
            case "w":
                out += String(d.getDay());
                break;
            case "m":
                out += pad(d.getMonth() + 1);
                break;
            case "n":
                out += String(d.getMonth() + 1);
                break;
            case "M":
                out += names(locale, { month: "short" }, d);
                break;
            case "F":
                out += names(locale, { month: "long" }, d);
                break;
            case "Y":
                out += String(d.getFullYear());
                break;
            case "y":
                out += pad(d.getFullYear() % 100);
                break;
            case "a":
                out += h < 12 ? "am" : "pm";
                break;
            case "A":
                out += h < 12 ? "AM" : "PM";
                break;
            case "g":
                out += String(h % 12 || 12);
                break;
            case "h":
                out += pad(h % 12 || 12);
                break;
            case "G":
                out += String(h);
                break;
            case "H":
                out += pad(h);
                break;
            case "i":
                out += pad(d.getMinutes());
                break;
            case "s":
                out += pad(d.getSeconds());
                break;
            default:
                out += c;
        }
    }

    return out;
}

/** "YYYY-MM-DD" and an optional "HH:MM[:SS]" as a local Date, or null. */
function parse(date: string, time = ""): Date | null {
    const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date.trim());
    if (!m || m[1] === "0000") {
        return null;
    }
    const t = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/.exec(time.trim());

    return new Date(
        Number(m[1]),
        Number(m[2]) - 1,
        Number(m[3]),
        t ? Number(t[1]) : 0,
        t ? Number(t[2]) : 0,
        t && t[3] ? Number(t[3]) : 0,
    );
}

function formats() {
    const c = config();

    return {
        date: c.dateFormat || "F j, Y",
        time: c.timeFormat || "g:i a",
        locale: c.locale || "en-US",
    };
}

/** A "YYYY-MM-DD" date in the site's format; anything else comes back as is. */
export function formatDate(date: string): string {
    const d = parse(date);
    const f = formats();

    return d ? phpDate(f.date, d, f.locale) : date;
}

/** An "HH:MM[:SS]" time in the site's format; anything else comes back as is. */
export function formatTime(time: string): string {
    const d = parse("2000-01-01", time);
    const f = formats();

    return d && time.trim() !== "" ? phpDate(f.time, d, f.locale) : time;
}

/** A MySQL "YYYY-MM-DD HH:MM:SS" stamp as the site's date and time; the zero date is empty. */
export function formatStamp(stamp: string): string {
    const [date = "", time = ""] = stamp.trim().split(" ");
    if (date.startsWith("0000")) {
        return "";
    }
    const d = parse(date, time);
    const f = formats();

    return d ? `${phpDate(f.date, d, f.locale)} ${phpDate(f.time, d, f.locale)}` : stamp;
}
