
import * as React from "react";
import classNames from 'classnames';
import { ExtensibleFunction } from './extensibleFunction';

export type ClassNamesParam = Parameters<typeof classNames>[0];

/**
 * API-enhancing wrapper class for CSS Module classname maps
 */
export class StyleSheet extends ExtensibleFunction<[ClassNamesParam, ClassNamesParam?], string> {
    constructor(styles: any) {
        super((...args) => {
            return this.class(...args);
        })

        if (Object.keys(styles).length == 1 && 'default' in styles) {
            styles = styles.default;
        }

        this.styles = styles;
        Object.assign(this, styles);
    }

    private readonly styles: any;

    /** Creates a simple React Component for the given style classes */
    element(classes: ClassNamesParam, type: string = 'div', globalClass?: ClassNamesParam) {
        return styledElement(this.styles, classes, {
            type,
            globals: globalClass,
        })
    }

    /**
     * Generate a CSS class string (including both local class-name and global class-name - eg "separated boMS4sbCae8GgTcsACMg") using the given local class-name.
     * 
     * As a shortcut, the StyleSheet instance itself can be called as a function with the same parameters - eg `styles('separated')` instead of `styles.class('separated')`.
     * 
     * Accepts any parameters that the `classnames` package accepts.
     */
    class(classes: ClassNamesParam, globalClass?: ClassNamesParam) {
        return mClassNames(this.styles, classes, globalClass);
    }
}

/**
 * Wraps a map of local to global style names (generated by CSS Modules) in a more useful API.
 * Same as calling `new StyleSheet(style_map)`.
 */
export function styleSheet(...args: ConstructorParameters<typeof StyleSheet>) {
    return new StyleSheet(...args);
}

export function extendHTMLElement(classes: string | string[], Element = 'div') {
    const sfc: React.FC = React.forwardRef((props: React.HTMLProps<HTMLElement>, ref) => {
        // @ts-ignore
        return <Element ref={ref} {...props} className={classNames(props.className, classes)} />
    });
    return sfc as React.HTMLFactory<HTMLElement>;
}

export function styledElement(styles: any, classes: ClassNamesParam, {
    type = 'div',
    globals,
}: { type?: string, globals?: ClassNamesParam } = {}) {
    return extendHTMLElement(mClassNames(styles, classes, globals), type);
}

export function moduleClassNames(styles: any, doubleNames: ClassNamesParam, extraNames?: ClassNamesParam) {
    const stylesNames = classNames(doubleNames).split(' ').map(n => styles[n]);
    return classNames(doubleNames, stylesNames, extraNames);
}

export const mClassNames = moduleClassNames;
