import { __ } from "@wordpress/i18n";
import {
    BaseControl,
    Button,
    SelectControl,
    PanelBody,
    ToggleControl,
} from "@wordpress/components";

import "../styles/dynamic-visibility.scss";

import {
    ensureGlobalVisibilityRegistrationApi,
    runVisibilityConditionsRegistration,
} from "../api/registry";

import { registerBuiltInVisibilityConditions } from "../api/built-in";

/**
 * Init only once
 */
let registryInitialized = false;

function ensureRegistryReady() {
    if ( registryInitialized ) {
        return;
    }

    registryInitialized = true;

    ensureGlobalVisibilityRegistrationApi();
    registerBuiltInVisibilityConditions();
    runVisibilityConditionsRegistration();
}

/**
 * Props:
 * - value: { enabled, type, relation, rules }
 * - onChange: (nextValue) => void
 * - rolesOptions: array( { label, value } )
 */
export default function DynamicVisibilityControl( { value, onChange, rolesOptions } ) {
    ensureRegistryReady();

    const dv = value && typeof value === "object" ? value : {};

    const normalizeEnabled = ( raw ) => {
        return raw === true || raw === "true" || raw === 1 || raw === "1";
    };

    const normalizeRules = ( rawRules ) => {
        const rules = Array.isArray( rawRules ) ? rawRules : [];

        return rules.map( ( rule ) => {
            const ruleObject = rule && typeof rule === "object" ? rule : {};

            const type = ruleObject.type ? String( ruleObject.type ) : "logged_in";
            const attrs =
                ruleObject.attrs && typeof ruleObject.attrs === "object" && ! Array.isArray( ruleObject.attrs )
                    ? ruleObject.attrs
                    : {};

            return {
                type,
                attrs,
            };
        });
    };

    const current = {
        enabled: normalizeEnabled( dv.enabled ),
        type: dv.type || "show",
        relation: dv.relation || "AND",
        rules: normalizeRules( dv.rules ),
    };

    const handlers = {
        update( patch ) {
            onChange( {
                enabled: current.enabled,
                type: current.type,
                relation: current.relation,
                rules: current.rules,
                ...patch,
            } );
        },

        addRule() {
            const nextRules = current.rules.slice();

            nextRules.push( {
                type: "logged_in",
                attrs: { value: true },
            } );

            handlers.update( { rules: nextRules } );
        },

        removeRule( index ) {
            const nextRules = current.rules.slice();

            nextRules.splice( index, 1 );
            handlers.update( { rules: nextRules } );
        },

        updateRule( index, patch ) {
            const nextRules = current.rules.slice();
            const rule =
                nextRules[ index ] && typeof nextRules[ index ] === "object"
                    ? nextRules[ index ]
                    : { type: "logged_in", attrs: {} };

            nextRules[ index ] = { ...rule, ...patch };
            handlers.update( { rules: nextRules } );
        },

        updateRuleAttrs( index, attrsPatch ) {
            const nextRules = current.rules.slice();
            const rule =
                nextRules[index] && typeof nextRules[index] === "object"
                    ? nextRules[index]
                    : { type: "logged_in", attrs: {} };

            const nextAttrs =
                rule.attrs && typeof rule.attrs === "object"
                    ? {...rule.attrs, ...( attrsPatch || {} )}
                    : {...( attrsPatch || {} )};

            nextRules[index] = {...rule, attrs: nextAttrs};
            handlers.update( { rules: nextRules } );
        },

        switchRuleType( index, nextType ) {
            const nextRules = current.rules.slice();
            const type = nextType ? String(nextType) : "logged_in";

            // Try to get defaults from registry
            const registry = window.JetMenu?.__jetMenuDVRegistry;
            const condition = registry?.get ? registry.get(type) : null;

            nextRules[ index ] = {
                type,
                attrs: condition?.defaultAttrs ? {...condition.defaultAttrs} : {},
            };

            handlers.update( { rules: nextRules } );
        },
    };

    // Build options from registry
    const registry = window.JetMenu?.__jetMenuDVRegistry;
    const conditionOptions = registry && registry.getOptions ? registry.getOptions() : [];

    const typeOptions = [
        { label: __( "Hide item if condition matches", "jet-menu" ), value: "hide" },
        { label: __( "Show item only if condition matches", "jet-menu" ), value: "show" },
    ];

    const relationOptions = [
        { label: __( "AND (all conditions)", "jet-menu" ), value: "AND" },
        { label: __( "OR (any condition)", "jet-menu" ), value: "OR" },
    ];

    const showRelation = current.rules.length > 1;

    return (
        <PanelBody title={ __( "Dynamic Visibility", "jet-menu" ) } initialOpen={ false }>
            <div className="jet-menu-dv">
                <ToggleControl
                    label={ __( "Enable Dynamic Visibility", "jet-menu" ) }
                    checked={ current.enabled }
                    onChange={ ( next ) => handlers.update( { enabled: !!next } ) }
                />

                { current.enabled && (
                    <>
                        <SelectControl
                            label={ __( "Mode", "jet-menu" ) }
                            value={ current.type }
                            options={ typeOptions }
                            onChange={ ( next ) => handlers.update( { type: next } ) }
                        />

                        <BaseControl label={ __( "Rules", "jet-menu" ) }>
                            <div className="jet-menu-dv__rules">
                                { current.rules.map( ( rule, index ) => {
                                    const ruleObject = rule && typeof rule === "object" ? rule : {};
                                    const ruleType = ruleObject.type || "logged_in";
                                    const attrs = ruleObject.attrs && typeof ruleObject.attrs === "object" ? ruleObject.attrs : {};

                                    const condition = registry && registry.get ? registry.get(ruleType) : null;

                                    return (
                                        <div key={ "dv-rule-" + index } className="jet-menu-dv__rule">
                                            <div className="jet-menu-dv__row">
                                                <SelectControl
                                                    label={__("Condition", "jet-menu")}
                                                    value={ruleType}
                                                    options={conditionOptions}
                                                    onChange={(nextType) => handlers.switchRuleType( index, nextType ) }
                                                />


                                                <Button
                                                    variant="tertiary"
                                                    isDestructive
                                                    className="jet-menu-dv__remove"
                                                    onClick={() => handlers.removeRule(index)}
                                                >
                                                    {__("Remove", "jet-menu")}
                                                </Button>

                                            </div>

                                            { condition && typeof condition.renderUI === "function"
                                                ? condition.renderUI( {
                                                    attrs,
                                                    onChange: ( nextAttrs ) => handlers.updateRuleAttrs( index, nextAttrs ),
                                                    rolesOptions,
                                                } )
                                                : null }
                                        </div>
                                    );
                                } ) }

                                <div className="jet-menu-dv__footer">
                                    <Button variant="secondary" onClick={ () => handlers.addRule() }>
                                        { __( "Add rule", "jet-menu" ) }
                                    </Button>
                                </div>
                            </div>
                        </BaseControl>

                        { showRelation && (
                            <div className="jet-menu-dv__relation">
                                <SelectControl
                                    label={ __( "Conditions relation", "jet-menu" ) }
                                    value={ current.relation }
                                    options={ relationOptions }
                                    onChange={ ( next ) => handlers.update( { relation: next } ) }
                                />
                            </div>
                        ) }
                    </>
                ) }
            </div>
        </PanelBody>
    );
}
