import { NumberValue, Schema, StringValue } from '@coveo/bueno';
import dayjs from 'dayjs';
import quarterOfYear from 'dayjs/plugin/quarterOfYear.js';
import { assertDateAboveAPIMinimum, formatDateForSearchApi, } from './date-format.js';
dayjs.extend(quarterOfYear);
const validRelativeDatePeriods = ['past', 'now', 'next'];
const validRelativeDateUnits = [
    'minute',
    'hour',
    'day',
    'week',
    'month',
    'quarter',
    'year',
];
const buildRelativeDateDefinition = (period) => {
    const isNow = period === 'now';
    return {
        amount: new NumberValue({ required: !isNow, min: 1 }),
        unit: new StringValue({
            required: !isNow,
            constrainTo: validRelativeDateUnits,
        }),
        period: new StringValue({
            required: true,
            constrainTo: validRelativeDatePeriods,
        }),
    };
};
/**
 * Validates a relative date and throws if it's invalid.
 * @param date The relative date, in `RelativeDate` or string format
 */
export function validateRelativeDate(date) {
    if (typeof date === 'string' && !isRelativeDateFormat(date)) {
        throw new Error(`The value "${date}" is not respecting the relative date format "period-amount-unit"`);
    }
    const relativeDate = typeof date === 'string' ? parseRelativeDate(date) : date;
    new Schema(buildRelativeDateDefinition(relativeDate.period)).validate(relativeDate);
    const dayJsDate = relativeToAbsoluteDate(relativeDate);
    const stringifiedDate = JSON.stringify(relativeDate);
    if (!dayJsDate.isValid()) {
        throw new Error(`Date is invalid: ${stringifiedDate}`);
    }
    assertDateAboveAPIMinimum(dayJsDate);
}
export function serializeRelativeDate(relativeDate) {
    const { period, amount, unit } = relativeDate;
    switch (period) {
        case 'past':
        case 'next':
            return `${period}-${amount}-${unit}`;
        case 'now':
            return period;
    }
}
function relativeToAbsoluteDate(relativeDate) {
    const { period, amount, unit } = relativeDate;
    switch (period) {
        case 'past':
            return dayjs().subtract(amount, unit);
        case 'next':
            return dayjs().add(amount, unit);
        case 'now':
            return dayjs();
    }
}
export function formatRelativeDateForSearchApi(relativeDate) {
    return formatDateForSearchApi(relativeToAbsoluteDate(parseRelativeDate(relativeDate)));
}
function splitDate(date) {
    return date.toLocaleLowerCase().split('-');
}
export function isRelativeDateFormat(date) {
    const [period, amount, unit] = splitDate(date);
    if (period === 'now') {
        return true;
    }
    if (!validRelativeDatePeriods.includes(period)) {
        return false;
    }
    if (!validRelativeDateUnits.includes(unit)) {
        return false;
    }
    const intAmount = parseInt(amount);
    if (Number.isNaN(intAmount) || intAmount <= 0) {
        return false;
    }
    return true;
}
export function isRelativeDate(date) {
    return !!date && typeof date === 'object' && 'period' in date;
}
function parseRelativeDate(date) {
    const [period, amount, unit] = splitDate(date);
    if (period === 'now') {
        return {
            period: 'now',
        };
    }
    return {
        period: period,
        amount: amount ? parseInt(amount) : undefined,
        unit: unit ? unit : undefined,
    };
}
/**
 * Deserializes a relative date string value into a valid `RelativeDate` object.
 * Throws an error if the date is invalid.
 * @param date The string serialized with the format "period-amount-unit"
 * @returns The parsed `RelativeDate` object.
 */
export function deserializeRelativeDate(date) {
    validateRelativeDate(date);
    return parseRelativeDate(date);
}
