import {CardType} from "./cards";
import {Color} from "./Color";
import {__, getRegisteredCardRenderers} from "../globals";
import {ComponentMeta, ComponentType} from "./ComponentsMeta";
import {FC, ReactElement, ReactNode} from "react";
import {OfferData, TestableData} from "./store";
import {PopupWindowStateContext} from "./atoms";

export type CardRendererContext = 'testable' | 'tier-base' | 'tier-subchild' | 'offers' | string;

export type CardRendererParameters = {
    color: Color,
    context: CardRendererContext,
    id: string,
    partialId?: string,
    localData?: TestableData | OfferData
};

export type CardRenderer<Options extends object = { [p: string]: any }> = {
    type?: string,
    icon: false | (({className}: { className: string, context?: 'card' | string }, color?: Color) => ReactElement),
    CustomCardComponent?: (parameters: CardRendererParameters, extraData?: Record<any, any>) => false | FC<{
        parameters: CardRendererParameters,
        extraData?: Record<any, any>
    }>,
    ComputedValue: false | ((cardRendererParameters: CardRendererParameters) => ReactNode),
    AboveComputedValueTooltip?: (cardRendererParameters: CardRendererParameters) => ReactNode,
    AboveComputedValue?: (cardRendererParameters: CardRendererParameters) => ReactNode | object,
    BelowComputedValue?: (cardRendererParameters: CardRendererParameters) => ReactNode,
    OutsideComputedValue?: (cardRendererParameters: CardRendererParameters) => ReactNode,
    prefix?: string | boolean | ((scope: PopupWindowStateContext<any>['scope']) => boolean | string),
    example?: string | ((scope: PopupWindowStateContext<any>['scope']) => string | undefined),
    // for testables that can have fixed values (tiered branches)
    suggestedScopeMarker?: (suggestedScope: string) => ({ prefix?: string, label: string } | ReactNode);
    removeFixedValues?: (options: Options) => void,
    canRenderInSelectedSidebarPopup?: (options: Options, currentlySelectedType: string) => boolean,
    canShowAsAvailableWhileBeingAmongstSelected?: (options: Options, currentlySelectedType: string) => boolean,
}

const modules = import.meta.glob(['./cards/*.tsx', './cards/conditions/*.tsx', "./cards/stubs/*.tsx"], {eager: true});


export const cardRenderers: {
    filters: Record<CardType['type'], any>,
    conditions: Record<CardType['type'], any>,
    offers: Record<CardType['type'], any>
} = {
    filters: {
        //       [Categories.type!]: Categories,
        //     [Quantity.type!]: Quantity,
        //   [Products.type!]: Products,
    },
    conditions: {
        // [UserRole.type!]: UserRole
    },
    offers: {}
}

const addCardRenderer = (type: ComponentType, cardRenderer: CardRenderer) => {
    cardRenderers[type][cardRenderer.type!] = cardRenderer
}

Object.values(modules).forEach((module: any) => {
    let typeOfCard: ComponentType | undefined;
    // Find the type of card from the meta (filters, conditions, offers, etc.)
    const exports = Object.entries(module);

    exports.forEach(([key, value]) => {
        if (key.endsWith('Meta') && typeof value === 'object' && value !== null) {
            typeOfCard = (value as ComponentMeta).type
        }
    });
    // loop again and now add the card renderer to the cardRenderers object
    // we have to loop again in case the renderer was exported before the meta, we don't want nasty bugs just because the order of exports is different
    // we can extract this into a function and then check in the first loop and return if found
    // but let's keep it simple for now
    exports.forEach(([key, value]) => {
        if (key.endsWith('Renderer') && typeof value === 'object' && value !== null) {
            const cardRenderer = value as CardRenderer;
            if (typeOfCard && cardRenderer.type) {
                addCardRenderer(typeOfCard, cardRenderer);
            }
        }
    });
});
/**
 * Add the external components
 */
for (const externalComponentType in getRegisteredCardRenderers()) {
    const externalCardRenderers = getRegisteredCardRenderers()[externalComponentType as ComponentType] || [];

    for (const externalCardRendererKey in externalCardRenderers) {
        const externalCardRenderer = externalCardRenderers[externalCardRendererKey];
        addCardRenderer(externalComponentType as ComponentType, externalCardRenderer);
    }
}

/**
 * For templates, you always have to return two [string], even if you dont want the first or second one you should return an empty bracket.
 *
 * The first bracket has a secondary weight, it is lighter and smaller than the second one.
 * The second bracket has a primary weight, it is larger and bolder and is the same as the main name.
 *
 * Examples:
 *
 * "[Items in] [Specific] *" -> Items in Specific Categories
 * "[] [Specific] *" -> Specific Products
 * "[These] [] *" -> These Tags
 *
 */
export const getNamePrefixedForCardRenderer = (cardRenderer: CardRenderer, scope: PopupWindowStateContext<any>['scope']): [string | '', string] => {
    let prefixedTemplate: string

    const defaultPrefixes = ['', ''] as [string, string]
    let customPrefix = typeof cardRenderer.prefix === 'function' ? cardRenderer.prefix(scope) : cardRenderer.prefix;
    if (!cardRenderer || customPrefix === false) {
        return defaultPrefixes;
    } else if (typeof customPrefix === 'string') {
        prefixedTemplate = customPrefix;
    } else {
        prefixedTemplate = __('[Items in] [Specific] *');
    }

    const prefixChunks = [...prefixedTemplate.replace('*', '').matchAll(/\[(.*?)\]/g)].map(matches => matches[1])

    return defaultPrefixes.map((defaultPrefix, index) => {
        if (prefixChunks[index]) {
            return prefixChunks[index].trim();
        }

        return defaultPrefix;
    }) as [string, string]
}
