import { getSidebarCustomize } from "./getSidebarCustomize.js";
import { CONTROL_VISIBILITY_CHANGED_EVENT } from "../events/visibilityChanged.js";
import { listenPanelExpanded } from "../preview/listenPanelExpanded.js";

import type { ControlVisibilityChangedEvent } from "../events/visibilityChanged.js";

/**
 * First level: Controls which should be toggle visibility.
 * Second level: Controls which need to match the value.
 */
type Conditionals = {
    [toggle: string]: {
        [conditionalControl: string]: ((value: any) => boolean) | number | string | boolean;
    };
};

const CONDITIONAL_HIDDEN_CLASS_NAME = "conditional-hidden";

/**
 * Register a list of conditionals and listen to dependent changes.
 *
 * @see https://digitalapps.com/hide-show-controls-based-on-other-settings-in-wordpress-customizer/
 */
function conditionalControls(panel: string, conditionals: Conditionals) {
    const customize = getSidebarCustomize();

    // Iterate over all conditional controls and toggle them depending on values
    for (const controlToToggle of Object.keys(conditionals)) {
        customize.control(controlToToggle, (controlToToggleHandle: any) => {
            const dependantControls = conditionals[controlToToggle];
            let currentVisibility: boolean;

            function visibility() {
                let visible = true;

                // Iterate all depending controls and check their value
                for (const dependantControl of Object.keys(dependantControls)) {
                    const dependantMustMatch = dependantControls[dependantControl];
                    const dependantValue = customize(dependantControl).get();

                    // Booleans need to be handled in another way because it is populated as "" in the control
                    if (typeof dependantMustMatch === "function") {
                        if (!dependantMustMatch(dependantValue)) {
                            visible = false;
                            break;
                        }
                    } else {
                        const checkBoolean = dependantMustMatch === true || dependantMustMatch === false;
                        if (
                            // Checkbox handling
                            (checkBoolean && !!+dependantValue !== dependantMustMatch) ||
                            // Normal matching
                            (!checkBoolean && dependantValue !== dependantMustMatch)
                        ) {
                            visible = false;
                            break;
                        }
                    }
                }

                // Toggle visibility
                if (["code_editor"].indexOf(controlToToggleHandle.params.type) > -1) {
                    if (visible) {
                        controlToToggleHandle.container.fadeTo(0, 1);
                    } else {
                        controlToToggleHandle.container.fadeTo(0, 0);
                    }
                } else {
                    if (visible) {
                        controlToToggleHandle.activate();
                    } else {
                        controlToToggleHandle.deactivate();
                    }
                }

                if (visible) {
                    controlToToggleHandle.container.removeClass(CONDITIONAL_HIDDEN_CLASS_NAME);
                } else {
                    controlToToggleHandle.container.addClass(CONDITIONAL_HIDDEN_CLASS_NAME);
                }

                if (visible !== currentVisibility) {
                    currentVisibility = visible;
                    document.dispatchEvent(
                        new CustomEvent<ControlVisibilityChangedEvent>(CONTROL_VISIBILITY_CHANGED_EVENT, {
                            detail: {
                                setting: controlToToggle,
                            },
                        }),
                    );
                }

                return visible;
            }

            visibility();

            // Retrigger if one of the values changes
            // Iterate all depending controls and check their value
            Object.keys(dependantControls).forEach((dependantControl) =>
                customize(dependantControl).bind("change", () => {
                    visibility();
                }),
            );

            // When a section got opened, recalculate the view
            listenPanelExpanded(
                panel,
                (state) => {
                    state && visibility();
                },
                true,
            );
        });
    }
}

export { conditionalControls, type Conditionals, CONDITIONAL_HIDDEN_CLASS_NAME };
