import dayjs from 'dayjs';

// Format pairs: [24h base, 12h variant]. Only formats with both variants are listed.
// Formats without a counterpart (ISO, date-only, week, etc.) are intentionally omitted —
// iso_datetime_formatter passes those through unchanged regardless of use_12h.
const FORMAT_PAIRS: [string, string][] = [
    ['datetime_iso_no_seconds', 'datetime_iso_12_no_seconds'],
    ['datetime_short',          'datetime_12_short'],
    ['datetime_medium',         'datetime_12_medium'],
    ['datetime_long',           'datetime_12_long'],
    ['datetime_medium_sec',     'datetime_12_medium_sec'],
    ['time_long',               'time_12_long'],
    ['time_short',              'time_12_short'],
    ['time_short_no_leading',   'time_12_short_no_leading'],
];

// Build lookup maps from the pairs above. Both directions are derived from the same source.
const TO_12H: Record<string, string> = Object.fromEntries(
    FORMAT_PAIRS.map(([h24, h12]) => [h24, h12])
);
const TO_24H: Record<string, string> = Object.fromEntries(
    FORMAT_PAIRS.map(([h24, h12]) => [h12, h24])
);

export const iso_datetime_formatter = function iso_datetime_formatter(
    raw_datetime: null | string | Date = null,
    named_format: string = 'datetime_iso_no_seconds', // date_iso, datetime_iso_no_seconds
    // Pass true/false to resolve to the correct 12h or 24h variant automatically.
    // null (default) leaves named_format unchanged — all existing call sites unaffected.
    use_12h: boolean | null = null,
    // When true, treats a naive datetime string (no Z / offset) as UTC so dayjs
    // converts it to local browser time on display. Use for timestamps stored as
    // UTC in the DB but returned without a timezone indicator.
    treat_as_utc: boolean = false
) {
    // console.log('*** iso_datetime_formatter() ***');

    // https://en.wikipedia.org/wiki/ISO_8601
    // https://day.js.org/docs/en/display/format
    // ISO 8601-1:2019 includes the T before the time portion
    // ISO 8601-1:2019 midnight may only be referred to as "00:00", corresponding to the beginning of a calendar day
    // and "24:00" is no longer allowed corresponding to the end of a day
    // 60 is only used to denote an added leap second

    // ISO 8601 UTC: 2021-03-04T19:04:44+00:00
    // ISO 8601 UTC: 2021-03-04T19:04:44Z
    // ISO 8601 UTC: 20210304T190444Z

    // datetime_iso 'YYYY-MM-DD HH:mm:ss'
    // datetime_iso_12 'YYYY-MM-DD hh:mm:ss A'
    // datetime_iso_12_short 'YY-MM-DD hh:mm A'
    // datetime_iso_tz 'YYYY-MM-DD HH:mm:ss Z'

    // datetime_12_no_seconds 'YYYY-MM-DD hh:mm A'

    // datetime_long 'dddd, MMMM D, YYYY hh:mm:ss A'
    // datetime_medium 'ddd, MMM D, YYYY hh:mm:ss A'
    // datetime_short 'MMM D, YY hh:mm A'

    // date_iso 'YYYY-MM-DD'

    // date_long 'dddd, MMMM D, YYYY'
    // date_medium 'ddd, MMM D, YYYY'
    // date_short 'MMM D, YY'

    // time_iso 'HH:mm:ss'
    // time_iso_12 'hh:mm:ss A'
    // time_iso_tz 'HH:mm:ss Z'
    // time_iso_12_tz 'hh:mm:ss A Z'

    // time_long 'hh:mm:ss A'
    // time_medium 'h:m:s A'
    // time_short 'hh:mm A'

    // dayjs(raw_datetime).format('dddd, MMMM D, YYYY hh:mm:ss A');

    if (!raw_datetime) {
        raw_datetime = new Date(); // Get the current datetime if one was not passed.
    }

    // Append 'Z' to naive UTC strings so dayjs converts to local browser time.
    // Guards against double-appending if the backend ever adds timezone info.
    if (treat_as_utc && typeof raw_datetime === 'string' && !raw_datetime.match(/Z$|[+-]\d{2}:?\d{2}$/)) {
        raw_datetime = raw_datetime + 'Z';
    }

    if (use_12h !== null) {
        named_format = use_12h
            ? (TO_12H[named_format] ?? named_format)
            : (TO_24H[named_format] ?? named_format);
    }

    let datetime_string = null;

    switch (named_format) {
        case 'datetime_iso':
            datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD HH:mm:ss');
            break;
        case 'datetime_iso_no_seconds':
            datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD HH:mm');
            break;
        case 'datetime_iso_12_short':
            datetime_string = dayjs(raw_datetime).format('YY-MM-DD hh:mm A');
            break;
        case 'datetime_iso_12_short_month':
            datetime_string = dayjs(raw_datetime).format('MM-DD hh:mm A');
            break;
        case 'datetime_iso_tz':
            datetime_string = dayjs(raw_datetime).format(
                'YYYY-MM-DD HH:mm:ss Z'
            );
            break;
        case 'datetime_iso_12_no_seconds':
            datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
            break;
        // case 'datetime_12_no_seconds':
        //     datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
        //     break;
        case 'datetime_us':
            datetime_string = dayjs(raw_datetime).format(
                'MM/DD/YYYY hh:mm:ss A'
            );
            break;
        case 'datetime_short':
            datetime_string = dayjs(raw_datetime).format('MMM D, YY HH:mm');
            break;
        case 'datetime_12_short':
            datetime_string = dayjs(raw_datetime).format('MMM D, YY hh:mm A');
            break;
        case 'datetime_medium':
            datetime_string = dayjs(raw_datetime).format('MMM D, YYYY H:mm');
            break;
        case 'datetime_12_medium':
            datetime_string = dayjs(raw_datetime).format('MMM D, YYYY h:mm A');
            break;
        case 'datetime_long':
            datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY HH:mm');
            break;
        case 'datetime_12_long':
            datetime_string = dayjs(raw_datetime).format(
                'MMMM D, YYYY hh:mm A'
            );
            break;
        case 'datetime_medium_sec':
            datetime_string = dayjs(raw_datetime).format('MMM D, YYYY H:mm:ss');
            break;
        case 'datetime_12_medium_sec':
            datetime_string = dayjs(raw_datetime).format(
                'MMM D, YYYY h:mm:ss A'
            );
            break;
        case 'datetime_short_month':
            datetime_string = dayjs(raw_datetime).format('MMM D hh:mm A');
            break;
        case 'datetime_long_month':
            datetime_string = dayjs(raw_datetime).format('MMMM D hh:mm A');
            break;
        case 'datetime_short_day':
            datetime_string = dayjs(raw_datetime).format('D hh:mm A');
            break;
        case 'date_iso':
            datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD');
            break;
        case 'date_us':
            datetime_string = dayjs(raw_datetime).format('MM/DD/YYYY');
            break;
        case 'date_long_month_day':
            datetime_string = dayjs(raw_datetime).format('MMMM D');
            break;
        case 'date_short':
            datetime_string = dayjs(raw_datetime).format('MMM D, YY');
            break;
        case 'date_short_no_year':
            datetime_string = dayjs(raw_datetime).format('MMM D');
            break;
        case 'date_long':
            datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY');
            break;
        case 'date_full':
            datetime_string = dayjs(raw_datetime).format('dddd, MMMM D, YYYY');
            break;
        case 'date_full_no_year':
            datetime_string = dayjs(raw_datetime).format('dddd, MMMM D');
            break;
        case 'time_iso':
            datetime_string = dayjs(raw_datetime).format('HH:mm:ss');
            break;
        case 'time_iso_12_tz':
            datetime_string = dayjs(raw_datetime).format('hh:mm:ss A Z');
            break;
        case 'time_long':
            datetime_string = dayjs(raw_datetime).format('HH:mm:ss');
            break;
        case 'time_12_long':
            datetime_string = dayjs(raw_datetime).format('hh:mm:ss A');
            break;
        case 'time_short':
            datetime_string = dayjs(raw_datetime).format('HH:mm');
            break;
        case 'time_short_no_leading':
            datetime_string = dayjs(raw_datetime).format('H:mm');
            break;
        case 'time_12_short':
            datetime_string = dayjs(raw_datetime).format('hh:mm A');
            break;
        case 'time_12_short_no_leading':
            datetime_string = dayjs(raw_datetime).format('h:mm A');
            break;
        case 'week_long':
            datetime_string = dayjs(raw_datetime).format('dddd');
            break;
        case 'week_medium':
            datetime_string = dayjs(raw_datetime).format('ddd');
            break;
        case 'week_short':
            datetime_string = dayjs(raw_datetime).format('dd');
            break;
        default:
            // console.log(`The named format passed (${named_format}) did not match a common name. Trying to format with the named format value.`);
            datetime_string = dayjs(raw_datetime).format(named_format);
        // datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD HH:mm:ss');
    }
    return datetime_string;
};
