/** @odoo-module **/

import { useEffect, useRef } from "@odoo/owl";
import { user } from "@web/core/user";
import { url } from "@web/core/utils/urls";
import { useBus, useService } from "@web/core/utils/hooks";
import { useSortable } from "@web/core/utils/sortable_owl";
import { useHotkey } from "@web/core/hotkeys/hotkey_hook";

import { Dropdown } from "@web/core/dropdown/dropdown";

export class AppSwitcher extends Dropdown {
    setup() {
        super.setup();
        this.commandPaletteOpen = false;
        this.commandService = useService("command");
        this.ui = useService("ui");
        this.sortableRef = useRef("sortableArea");
        this.state.focusedIndex = null;
        if (user.activeCompany.has_background_image) {
            this.imageUrl = url('/web/image', {
                model: 'res.company',
                field: 'background_image',
                id: user.activeCompany.id,
            });
        } else {
            this.imageUrl = '/fusion_backend_theme/static/src/img/background.png';
        }

        // Drag-and-drop reordering
        useSortable({
            enable: () => this.state.isOpen,
            ref: this.sortableRef,
            elements: ".o_app",
            cursor: "move",
            delay: 500,
            tolerance: 10,
            onDrop: (params) => this._onSortDrop(params),
        });

        useEffect(
            (isOpen) => {
                if (isOpen) {
                    this.state.focusedIndex = null;
                    const openMainPalette = (ev) => {
                        if (
                            !this.commandPaletteOpen &&
                            ev.key.length === 1 &&
                            !ev.ctrlKey &&
                            !ev.altKey
                        ) {
                            this.commandService.openMainPalette(
                                { searchValue: `/${ev.key}` },
                                () => { this.commandPaletteOpen = false; }
                            );
                            this.commandPaletteOpen = true;
                        }
                    };
                    window.addEventListener("keydown", openMainPalette);
                    return () => {
                        window.removeEventListener("keydown", openMainPalette);
                        this.commandPaletteOpen = false;
                    };
                }
            },
            () => [this.state.isOpen]
        );
        useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", () => {
            if (this.state.close) {
                this.state.close();
            }
        });

        // Keyboard navigation
        this._registerHotkeys();
    }

    get maxIconNumber() {
        const w = window.innerWidth;
        if (w < 576) {
            return 3;
        } else if (w < 768) {
            return 4;
        } else {
            return 6;
        }
    }

    _registerHotkeys() {
        const hotkeys = [
            ["ArrowDown", () => this._updateFocusedIndex("nextLine")],
            ["ArrowRight", () => this._updateFocusedIndex("nextColumn")],
            ["ArrowUp", () => this._updateFocusedIndex("previousLine")],
            ["ArrowLeft", () => this._updateFocusedIndex("previousColumn")],
            ["Tab", () => this._updateFocusedIndex("nextElem")],
            ["shift+Tab", () => this._updateFocusedIndex("previousElem")],
            ["Escape", () => {
                if (this.state.close) {
                    this.state.close();
                }
            }],
        ];
        for (const [hotkey, callback] of hotkeys) {
            useHotkey(hotkey, callback, { allowRepeat: true });
        }
    }

    _updateFocusedIndex(cmd) {
        if (!this.state.isOpen) {
            return;
        }
        const apps = this._getAppElements();
        const nbrApps = apps.length;
        const lastIndex = nbrApps - 1;
        const focusedIndex = this.state.focusedIndex;
        if (lastIndex < 0) {
            return;
        }
        if (focusedIndex === null) {
            this.state.focusedIndex = 0;
            return;
        }
        const lineNumber = Math.ceil(nbrApps / this.maxIconNumber);
        const currentLine = Math.ceil((focusedIndex + 1) / this.maxIconNumber);
        let newIndex;
        switch (cmd) {
            case "previousElem":
                newIndex = focusedIndex - 1;
                break;
            case "nextElem":
                newIndex = focusedIndex + 1;
                break;
            case "previousColumn":
                if (focusedIndex % this.maxIconNumber) {
                    newIndex = focusedIndex - 1;
                } else {
                    newIndex = focusedIndex + Math.min(lastIndex - focusedIndex, this.maxIconNumber - 1);
                }
                break;
            case "nextColumn":
                if (focusedIndex === lastIndex || (focusedIndex + 1) % this.maxIconNumber === 0) {
                    newIndex = (currentLine - 1) * this.maxIconNumber;
                } else {
                    newIndex = focusedIndex + 1;
                }
                break;
            case "previousLine":
                if (currentLine === 1) {
                    newIndex = focusedIndex + (lineNumber - 1) * this.maxIconNumber;
                    if (newIndex > lastIndex) {
                        newIndex = lastIndex;
                    }
                } else {
                    newIndex = focusedIndex - this.maxIconNumber;
                }
                break;
            case "nextLine":
                if (currentLine === lineNumber) {
                    newIndex = focusedIndex % this.maxIconNumber;
                } else {
                    newIndex = focusedIndex + Math.min(this.maxIconNumber, lastIndex - focusedIndex);
                }
                break;
        }
        if (newIndex < 0) {
            newIndex = lastIndex;
        } else if (newIndex > lastIndex) {
            newIndex = 0;
        }
        this.state.focusedIndex = newIndex;
    }

    _getAppElements() {
        if (this.menuRef && this.menuRef.el) {
            return [...this.menuRef.el.querySelectorAll('.o_app')];
        }
        return [];
    }

    _onSortDrop({ element, previous }) {
        const apps = this._getAppElements();
        const order = apps.map((el) => {
            const link = el.querySelector('a[data-menu-xmlid]') || el;
            return link.dataset.menuXmlid;
        }).filter(Boolean);

        const elementLink = element.querySelector('a[data-menu-xmlid]') || element;
        const elementId = elementLink.dataset.menuXmlid;
        const elementIndex = order.indexOf(elementId);
        if (elementIndex === -1) {
            return;
        }
        order.splice(elementIndex, 1);
        if (previous) {
            const prevLink = previous.querySelector('a[data-menu-xmlid]') || previous;
            const prevIndex = order.indexOf(prevLink.dataset.menuXmlid);
            order.splice(prevIndex + 1, 0, elementId);
        } else {
            order.splice(0, 0, elementId);
        }
        user.setUserSettings("homemenu_config", JSON.stringify(order));
    }

    onOpened() {
        super.onOpened();
        if (this.menuRef && this.menuRef.el) {
            this.menuRef.el.style.backgroundImage = `url('${this.imageUrl}')`;
        }
    }
}
