const STANDARD = Math.pow(10, 12);
const ONE_DAY_TICKS = 86400000;

const ONE_MINUTE_TICKS = 60 * 1000;
const ONE_HOUR_TICKS = 60 * 60 * 1000;

export const enum TimeFormatRule {
    /**
     * 12小时制
     */
    Hour12,
    /**
     * 24小时制
     */
    Hour24,
}

function formatHour2Friendly(hour: number) {
    if (hour <= 1) {
        return "凌晨";
    }
    if (hour < 12) {
        return "上午";
    }
    if (hour === 12) {
        return "中午";
    }
    if (hour < 20) {
        return "下午";
    }
    return "晚上";
}

function formatHour12Unit(hour: number, rule: TimeFormatRule) {
    if (rule === TimeFormatRule.Hour12) {
        return hour > 12 ? hour - 12 : hour;
    }
    return hour;
}

function format2DetailTime(hour: number, time: Date, rule: TimeFormatRule) {
    let hourString = "";
    const h = formatHour12Unit(hour, rule);
    if (h < 10) {
        hourString = "0" + h;
    } else {
        hourString = h + "";
    }
    let minuteString = "";
    const m = time.getMinutes();
    if (m < 10) {
        minuteString = "0" + m;
    } else {
        minuteString = "" + m;
    }
    return `${formatHour2Friendly(hour)} ${hourString}:${minuteString}`;
}

function isSameDay(d1: Date, d2: Date) {
    return (
        d1.getFullYear() === d2.getFullYear() &&
        d1.getMonth() === d2.getMonth() &&
        d1.getDate() === d2.getDate()
    );
}

function isInMinute(d1: Date, d2: Date) {
    return Math.abs(d1.valueOf() - d2.valueOf()) <= ONE_MINUTE_TICKS;
}

function isInHour(d1: Date, d2: Date) {
    return Math.abs(d1.valueOf() - d2.valueOf()) < ONE_HOUR_TICKS;
}

function isYesterday(date: Date) {
    const now = new Date();
    now.setHours(0);
    now.setMinutes(0);
    now.setSeconds(0);
    now.setMilliseconds(0);
    const ticks = now.valueOf();
    const yesterday = ticks - ONE_DAY_TICKS;
    const v = date.valueOf();
    return v >= yesterday && v < ticks;
}

function isIn6Days(date: Date) {
    const now = new Date();
    now.setHours(0);
    now.setMinutes(0);
    now.setSeconds(0);
    now.setMilliseconds(0);
    const ticks = now.valueOf();
    const sixDaysAgo = ticks - ONE_DAY_TICKS * 6;
    const v = date.valueOf();
    return v >= sixDaysAgo && v < ticks;
}

function isSameYear(d1: Date, d2: Date) {
    return d1.getFullYear() === d2.getFullYear();
}

const DAY_MAPPING: { [key: number]: string } = {
    0: "日",
    1: "一",
    2: "二",
    3: "三",
    4: "四",
    5: "五",
    6: "六",
};

function getDayInWeek(time: Date) {
    const d = time.getDay();
    return DAY_MAPPING[d];
}

function formatWithTwoNumber(number: number) {
    return number < 10 ? `0${number}` : `${number}`;
}

function formatTime2MonthDate(time: Date) {
    return `${formatWithTwoNumber(time.getMonth() + 1)}月${formatWithTwoNumber(
        time.getDate()
    )}日`;
}

function formatTime2ShortMonthDate(time: Date) {
    return `${formatWithTwoNumber(time.getMonth() + 1)}/${formatWithTwoNumber(
        time.getDate()
    )}`;
}

function formatTime2YearMonthDate(time: Date) {
    return `${time.getFullYear()}年${formatWithTwoNumber(
        time.getMonth() + 1
    )}月${formatWithTwoNumber(time.getDate())}日`;
}

function formatTime2ShortYearMonthDate(time: Date) {
    return `${time.getFullYear()}/${formatWithTwoNumber(
        time.getMonth() + 1
    )}/${formatWithTwoNumber(time.getDate())}`;
}

/**
 * 格式化时间。                                                                                      short模式下格式
 * * 刚刚(1分钟内)                                                                              [在short模式下不生效]
 * * {x}分钟前                                                                                          [在short模式下不生效]
 * * 今天,格式化为:[上午|中午|下午] hh:mm                                        | [两者一致]
 * 昨天,格式化为:昨天 [上午|中午|下午] hh:mm                                | 昨天
 * 2~6天前,格式化为:星期几 [上午|中午|下午] hh:mm                      | 星期几
 * 同年时间,格式化为:07月08日 [上午|中午|下午] hh:mm                 | 07/12
 * 非同年时间,格式化为:2019年07月08日 [上午|中午|下午] hh:mm  | 2019/01/02
 * @param time Javascript标准时间
 * @param option { rule: 选择是12小时制还是24小时制,short:是否以短时间形式显示 }
 */
export function formatTime(
    time: number,
    option = { rule: TimeFormatRule.Hour12, short: false }
) {
    if (time < STANDARD) {
        time *= 1000;
    }

    const t = new Date(time);
    const now = new Date();

    if (!option.short) {
        if (isInMinute(t, now)) {
            return "刚刚";
        }

        if (isInHour(t, now)) {
            return `${Math.round(
                Math.abs(t.valueOf() - now.valueOf()) / 1000 / 60
            )}分钟前`;
        }
    }

    const hour = t.getHours();

    if (isSameDay(t, now)) {
        return format2DetailTime(hour, t, option.rule);
    }

    if (isYesterday(t)) {
        if (option.short) {
            return "昨天";
        }
        return "昨天 " + format2DetailTime(hour, t, option.rule);
    }

    if (isIn6Days(t)) {
        if (option.short) {
            return `星期${getDayInWeek(t)}`;
        }
        return (
            `星期${getDayInWeek(t)} ` + format2DetailTime(hour, t, option.rule)
        );
    }

    if (isSameYear(t, now)) {
        if (option.short) {
            return formatTime2ShortMonthDate(t);
        }
        return (
            formatTime2MonthDate(t) +
            " " +
            format2DetailTime(hour, t, option.rule)
        );
    }

    if (option.short) {
        return formatTime2ShortYearMonthDate(t);
    }
    return (
        formatTime2YearMonthDate(t) +
        " " +
        format2DetailTime(hour, t, option.rule)
    );
}