import dayjs from 'dayjs';

// JS 中 utcTime 与 localTime 及 Date.getTimezoneOffset() 的关系：
// Date.getTimezoneOffset() = utcTime - localTime;

/**
 * 获取当前时间的 Unix 时间戳（10 位，秒级）。
 * @returns 当前时间的 Unix 秒级时间戳
 */
export function currentUnixTimestamp() {
    return dayjs().unix();
}

/**
 * 获取当前时间的 JavaScript 时间戳（13 位，毫秒级）。
 * @returns 当前时间的毫秒级时间戳
 */
export function currentJsTimestamp() {
    return dayjs().valueOf();
}

/**
 * 获取当前本地时间的格式化字符串。
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 当前本地时间字符串
 */
export function currentDateTime(fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs().format(fmt);
}

/**
 * 获取当前 UTC 时间的格式化字符串。
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 当前 UTC 时间字符串
 */
export function currentUtcDateTime(fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs().utc().format(fmt);
}

/**
 * 基于当前本地时间，计算若干天前的本地时间字符串。
 * @param days 往前推的天数（正数表示过去）
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 对应日期的本地时间字符串
 */
export function getDateTimeString(days: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs().subtract(days, 'day').format(fmt);
}

/**
 * 基于当前时间，计算若干天前的 UTC 时间字符串。
 * @param days 往前推的天数（正数表示过去）
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 对应日期的 UTC 时间字符串
 */
export function getUtcDateTimeString(days: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs().subtract(days, 'day').utc().format(fmt);
}

/**
 * 将 Unix 时间戳（秒级）格式化为本地时间字符串。
 * @param unixTimestamp Unix 秒级时间戳
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 本地时间字符串
 */
export function unixTimeFormat(unixTimestamp: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs.unix(unixTimestamp).format(fmt);
}

/**
 * 将 Unix 时间戳（秒级）格式化为 UTC 时间字符串。
 * @param unixTimestamp Unix 秒级时间戳
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns UTC 时间字符串
 */
export function unixTimeFormatToUtc(unixTimestamp: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs.unix(unixTimestamp).utc().format(fmt);
}

/**
 * 将 JavaScript 时间戳（毫秒级）格式化为本地时间字符串。
 * @param jsTimestamp 毫秒级时间戳
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 本地时间字符串
 */
export function jsTimeFormat(jsTimestamp: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs(jsTimestamp).format(fmt);
}

/**
 * 将 JavaScript 时间戳（毫秒级）格式化为 UTC 时间字符串。
 * @param jsTimestamp 毫秒级时间戳
 * @param fmt dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns UTC 时间字符串
 */
export function jsTimeFormatToUtc(jsTimestamp: number, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs(jsTimestamp).utc().format(fmt);
}

/**
 * 按输入格式解析时间字符串，再格式化为目标格式的本地时间字符串。
 * @param dateTime 时间字符串
 * @param inFmt 输入字符串的 dayjs 格式模板
 * @param fmt 输出字符串的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 格式化后的本地时间字符串
 */
export function stringTimeFormat(dateTime: string, inFmt: string, fmt = 'YYYY-MM-DD HH:mm:ss') {
    return dayjs(dateTime, inFmt).format(fmt);
}

/**
 * 按输入格式解析时间字符串，再格式化为目标格式的 UTC 时间字符串。
 * @param dateTime 时间字符串
 * @param inFmt 输入字符串的 dayjs 格式模板
 * @param fmt 输出字符串的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 格式化后的 UTC 时间字符串
 */
export function stringTimeFormatToUtc(
    dateTime: string,
    inFmt: string,
    fmt = 'YYYY-MM-DD HH:mm:ss'
) {
    return dayjs(dateTime, inFmt).utc().format(fmt);
}

/**
 * 将总秒数按格式模板格式化为「天 时:分:秒」字符串；仅支持 D/d、H/h、m、s 占位符，数值不足两位时高位补 0。
 * @param seconds 总秒数
 * @param fmt 占位符模板，默认 'DD HH:mm:ss'（D/d 天，H/h 时，m 分，s 秒）
 * @returns 格式化后的时长字符串
 */
export function secondsFormat(seconds: number, fmt = 'DD HH:mm:ss') {
    let result = fmt;
    const sec = seconds % 86400;
    const o = {
        '[Dd]+': Math.trunc(seconds / 86400),
        '[Hh]+': Math.trunc(sec / 3600),
        'm+': Math.trunc((sec % 3600) / 60),
        's+': sec % 60,
    };
    for (const k in o) {
        if (Object.prototype.hasOwnProperty.call(o, k)) {
            const regex = new RegExp(`(${k})`);
            const match = regex.exec(fmt);
            if (match) {
                const val = o[k as keyof typeof o];
                const str = `${val}`;
                result = result.replace(
                    match[1],
                    match[1].length === 1 ? str : str.padStart(2, '0')
                );
            }
        }
    }
    return result;
}

/**
 * 将总秒数按格式模板格式化为时长字符串，值为 0 的档位会省略（秒档位始终保留）；seconds ≤ 0 时返回空字符串。
 * @param seconds 总秒数
 * @param fmt 占位符模板，默认 'DD天HH小时mm分钟ss秒'（DD/HH/mm/ss 对应天/时/分/秒及后缀）
 * @returns 精简后的时长字符串，如 61 秒对应「1分钟1秒」
 */
export function secondsFormatFit(seconds: number, fmt = 'DD天HH小时mm分钟ss秒') {
    if (seconds <= 0) {
        return '';
    }

    let result = fmt;

    const sec = seconds % 86400;
    const o = {
        DD: Math.trunc(seconds / 86400),
        HH: Math.trunc(sec / 3600),
        mm: Math.trunc((sec % 3600) / 60),
        ss: sec % 60,
    };

    for (const k in o) {
        if (Object.prototype.hasOwnProperty.call(o, k)) {
            const regex = new RegExp(`(${k})([^DHms]*)`);
            const match = regex.exec(fmt);
            if (match) {
                const val = o[k as keyof typeof o];
                const str = `${val}`;
                if (val > 0 || k === 'ss') {
                    result = result.replace(match[0], str + match[2]);
                } else {
                    result = result.replace(match[0], '');
                }
            }
        }
    }
    return result.trim();
}

/**
 * 将时间字符串解析为 JavaScript 时间戳（13 位，毫秒级）。若为 ISO 或 GMT 格式则自动识别，否则按 fmt 解析。
 * @param dateTimeString 时间字符串，如 '2013-11-11T15:30:00Z'、'2013-11-11T15:30:00+08:00'、'Thu Nov 14 2024 14:57:29 GMT+0800 (中国标准时间)' 或 'YYYY-MM-DD HH:mm:ss'
 * @param fmt 非 ISO/GMT 时的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 毫秒级时间戳
 */
export function getTimeFromDateTimeString(
    dateTimeString: string,
    fmt = 'YYYY-MM-DD HH:mm:ss'
): number {
    if (
        /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(dateTimeString) ||
        /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/.test(dateTimeString) ||
        /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{2} \d{4} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}( \(.+\))?$/.test(
            dateTimeString
        )
    ) {
        return dayjs(dateTimeString).valueOf();
    } else {
        return dayjs(dateTimeString, fmt).valueOf();
    }
}

/**
 * 将表示 UTC 的时间字符串按 UTC 解析为 JavaScript 时间戳（13 位，毫秒级）。若为 ISO 或 GMT 格式则自动识别，否则按 fmt 解析。
 * @param utcDateTimeString UTC 时间字符串，格式同 getTimeFromDateTimeString 的 dateTimeString
 * @param fmt 非 ISO/GMT 时的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns 毫秒级时间戳
 */
export function getTimeFromUtcDateTimeString(
    utcDateTimeString: string,
    fmt = 'YYYY-MM-DD HH:mm:ss'
): number {
    if (
        /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(utcDateTimeString) ||
        /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/.test(utcDateTimeString) ||
        /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{2} \d{4} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}( \(.+\))?$/.test(
            utcDateTimeString
        )
    ) {
        return dayjs.utc(utcDateTimeString).valueOf();
    } else {
        return dayjs.utc(utcDateTimeString, fmt).valueOf();
    }
}

/**
 * 将时间字符串解析为 Unix 时间戳（10 位，秒级）。解析规则同 getTimeFromDateTimeString，结果为毫秒戳向下取整除以 1000。
 * @param dateTimeString 时间字符串，格式同 getTimeFromDateTimeString
 * @param fmt 非 ISO/GMT 时的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns Unix 秒级时间戳
 */
export function getUnixFromDateTimeString(
    dateTimeString: string,
    fmt = 'YYYY-MM-DD HH:mm:ss'
): number {
    return Math.floor(getTimeFromDateTimeString(dateTimeString, fmt) / 1000);
}

/**
 * 将表示 UTC 的时间字符串解析为 Unix 时间戳（10 位，秒级）。解析规则同 getTimeFromUtcDateTimeString，结果为毫秒戳向下取整除以 1000。
 * @param utcDateTimeString UTC 时间字符串，格式同 getTimeFromUtcDateTimeString
 * @param fmt 非 ISO/GMT 时的 dayjs 格式模板，默认 'YYYY-MM-DD HH:mm:ss'
 * @returns Unix 秒级时间戳
 */
export function getUnixFromUtcDateTimeString(
    utcDateTimeString: string,
    fmt = 'YYYY-MM-DD HH:mm:ss'
): number {
    return Math.floor(getTimeFromUtcDateTimeString(utcDateTimeString, fmt) / 1000);
}
