export type ClassValue = ClassValue[] | DOMTokenList | Record<string, Nullable<boolean>> | string | number | bigint | null | boolean | undefined;

/**
 * Parses the value provided, recursing on any of its sub values if needed.
 * @param mix The value to parse.
 */
const toVal = (mix: ClassValue) => {
    if (!mix) {
        return "";
    }
    const classes: string[] = [];

    switch (typeof mix) {
        case "string":
            classes.push(mix);
            break;
        case "object":
            if (Array.isArray(mix)) {
                classes.push(mix.map((val) => val && toVal(val)).filterJoin(" "));
            } else {
                for (const [k, v] of Object.entries(mix)) {
                    if (v) {
                        classes.push(k);
                    }
                }
            }
            break;
        default:
            if (mix) {
                console.warn(`Unable to parse ${mix} as a CSS class. Coercing to string.`);
                classes.push(mix.toString());
            }
    }
    return classes.filterJoin(" ");
};

/**
 * Parses the provided class values and returns a string of CSS classes.
 * @param inputs One or more class names or objects that resolve to class names.
 * @returns A resolved string of class names concatenated together.
 */
const clsx = (...inputs: ClassValue[]) => {
    return inputs.map((input) => {
        if (input) {
            const val = toVal(input);
            if (val) {
                return val;
            }
        }
    }).filterJoin(" ");
};

export default clsx;
