import { LunchChoice, LunchChoices } from "../../types/gen/types.gen";

const DAY_OF_WEEK_FORMAT = new Intl.DateTimeFormat(undefined, { weekday: 'long' });

/** Vrátí datum v ISO formátu. */
export function formatDate(date: Date, format?: string) {
    let day = String(date.getDate()).padStart(2, '0');
    let month = String(date.getMonth() + 1).padStart(2, "0");
    let year = String(date.getFullYear());

    const f = format ?? 'YYYY-MM-DD';
    return f.replace('DD', day).replace('MM', month).replace('YYYY', year);
}

/** Vrátí human-readable reprezentaci předaného data pro zobrazení. */
export function getHumanDate(date: Date) {
    let currentDay = String(date.getDate()).padStart(2, '0');
    let currentMonth = String(date.getMonth() + 1).padStart(2, "0");
    let currentYear = date.getFullYear();
    let currentDayOfWeek = DAY_OF_WEEK_FORMAT.format(date);
    return `${currentDay}.${currentMonth}.${currentYear} (${currentDayOfWeek})`;
}

/** Vrátí human-readable reprezentaci předaného času pro zobrazení. */
export function getHumanTime(time: Date) {
    let currentHours = String(time.getHours()).padStart(2, '0');
    let currentMinutes = String(time.getMinutes()).padStart(2, "0");
    return `${currentHours}:${currentMinutes}`;
}

/**
 * Vrátí index dne v týdnu, kde pondělí=0, neděle=6
 * 
 * @param date datum
 * @returns index dne v týdnu
 */
export const getDayOfWeekIndex = (date: Date) => {
    // https://stackoverflow.com/a/4467559
    return (((date.getDay() - 1) % 7) + 7) % 7;
}

/** Vrátí true, pokud je předané datum o víkendu. */
export function getIsWeekend(date: Date) {
    const index = getDayOfWeekIndex(date);
    return index == 5 || index == 6;
}

/** Vrátí první pracovní den v týdnu předaného data. */
export function getFirstWorkDayOfWeek(date: Date) {
    const firstDay = new Date(date.getTime());
    firstDay.setDate(date.getDate() - getDayOfWeekIndex(date));
    return firstDay;
}

/** Vrátí poslední pracovní den v týdnu předaného data. */
export function getLastWorkDayOfWeek(date: Date) {
    const lastDay = new Date(date.getTime());
    lastDay.setDate(date.getDate() + (4 - getDayOfWeekIndex(date)));
    return lastDay;
}

/** Vrátí pořadové číslo týdne předaného data v roce dle ISO 8601. */
export function getWeekNumber(inputDate: Date) {
    const date = new Date(inputDate.getTime());
    date.setHours(0, 0, 0, 0);
    date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
    const week1 = new Date(date.getFullYear(), 0, 4);
    return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
}

/**
 * Vrátí JWT token z hlaviček, pokud ho obsahují.
 * 
 * @param req request
 * @returns token, pokud ho hlavičky requestu obsahují
 */
export const parseToken = (req: any) => {
    if (req?.headers?.authorization) {
        return req.headers.authorization.split(' ')[1];
    }
}

/**
 * Ověří přítomnost (not null) předaných parametrů v URL query.
 * V případě nepřítomnosti kteréhokoli parametru vyhodí chybu.
 * 
 * @param req request
 * @param paramNames pole názvů požadovaných parametrů
 */
export const checkQueryParams = (req: any, paramNames: string[]) => {
    for (const name of paramNames) {
        if (req.query[name] == null) {
            throw Error(`Nebyl předán parametr '${name}' v query požadavku`);
        }
    }
}

/**
 * Ověří přítomnost (not null) předaných parametrů v těle requestu.
 * V případě nepřítomnosti kteréhokoli parametru vyhodí chybu.
 * 
 * @param req request
 * @param paramNames pole názvů požadovaných parametrů
 */
export const checkBodyParams = (req: any, paramNames: string[]) => {
    for (const name of paramNames) {
        if (req.body[name] == null) {
            throw Error(`Nebyl předán parametr '${name}' v těle požadavku`);
        }
    }
}


// TODO umístit do samostatného souboru
export class InsufficientPermissions extends Error { }

export class PizzaDayConflictError extends Error { }

export const getUsersByLocation = (choices: LunchChoices, login?: string): string[] => {
    const result: string[] = [];

    for (const location of Object.entries(choices)) {
        const locationKey = location[0] as LunchChoice;
        const locationValue = location[1];
        if (login && locationValue[login]) {
            for (const username in choices[locationKey]) {
                if (choices[locationKey].hasOwnProperty(username)) {
                    result.push(username);
                }
            }
            break;
        }
    }

    return result;
}