import type { IUserBase, IUserPassword, IUserRoles } from "@devowl-wp/api";
import type { EIso639One } from "@devowl-wp/iso-codes";

import { DEFAULT_LANGUAGE } from "../../route/supported-languages/supported-languages.get.js";

import type { ESupportedLanguage } from "../../route/supported-languages/supported-languages.get.js";

enum EUserRole {
    Admin = "admin",
    Creator = "creator",
    Translator = "translator",
    Client = "client",
}

/**
 * Specific interface for real cookie banner role parameters.
 */
interface IUserRoleParameters {
    allowedLanguages: EIso639One[];
}

/**
 * Specific interface for real cookie banner application users.
 */
interface IUser extends IUserBase, IUserPassword, IUserRoles<EUserRole, IUserRoleParameters> {}

type UserLight = Pick<IUser, "email" | "emailMd5" | "firstName" | "lastName" | "role" | "roleParameters">;

const roleHierarchy: Record<EUserRole, EUserRole[]> = {
    [EUserRole.Admin]: [EUserRole.Admin, EUserRole.Creator, EUserRole.Translator],
    [EUserRole.Creator]: [EUserRole.Creator, EUserRole.Translator],
    [EUserRole.Translator]: [EUserRole.Translator],
    [EUserRole.Client]: [EUserRole.Client],
};
Object.freeze(roleHierarchy);

/**
 * Returns user role hierarchy definition.
 *
 * Use Case:
 * You are a Creator and want to translate something (=Role Translator)
 *
 * Example:
 * Use this function, and get hierarchy for Creator.
 * Next check if Creator has a mapping to the role Translator
 *
 * Result:
 * Creator has also the Role Translator.
 */
function getUserRoleHierarchy() {
    return roleHierarchy;
}

type UserRoleInfo = Pick<IUser, "role" | "roleParameters">;

/**
 * Retrieves the list of allowed languages for a user based on their role.
 * If the user is an Admin, it signifies no restrictions by returning undefined.
 * Otherwise, it returns the allowed languages if specified, or an empty array
 * indicating no specific allowances.
 *
 * @param user An object representing the user, including their role and role parameters.
 * @returns An array of allowed language codes or undefined if no restrictions apply.
 */
function getAllowedLanguages(user: UserRoleInfo): EIso639One[] | undefined {
    // If the user is an Admin, immediately return undefined, signifying no restrictions.
    if (user?.role === EUserRole.Admin) {
        return undefined;
    }

    // Return allowedLanguages if they exist; otherwise, return empty => nothing allowed, but "en".
    return user?.roleParameters?.allowedLanguages || [];
}

/**
 * Determines if a specific language is allowed for a user based on their role
 * and allowed languages. The default language is always considered allowed.
 *
 * @param user An object representing the user, including their role and role parameters.
 * @param language A language code to check against the user's allowed languages.
 * @returns A boolean indicating whether the language is allowed for the user.
 */
function isLanguageAllowed(user: UserRoleInfo, language: string | EIso639One | ESupportedLanguage): boolean {
    const allowed = getAllowedLanguages(user);
    if (allowed === undefined || language === DEFAULT_LANGUAGE) {
        return true;
    } else {
        const set = new Set(allowed ? allowed.map((lang) => lang.toString()) : []);
        return set.has(language);
    }
}

export {
    type IUser,
    type UserLight,
    type UserRoleInfo,
    type IUserRoleParameters,
    EUserRole,
    getUserRoleHierarchy,
    getAllowedLanguages,
    isLanguageAllowed,
};
