<template>
    <div class="menu-builder" v-cloak>

        <div v-if="message && messageType"
             class="rounded-sm p-4 mb-4 border "
             :class="{
                'bg-red-100 border-red-400' : isErrorMessage(),
                'bg-yellow-100 border-yellow-400' : isWarningMessage(),
                'bg-blue-100 border-blue-400' : isInfoMessage(),
                'bg-green-100 border-green-400' : isSuccessMessage(),
            }"
        >
            <div class="flex">
                <div class="flex-shrink-0">
                    <svg class="h-5 w-5"
                         :class="{
                            'text-red-400' : isErrorMessage(),
                            'text-yellow-400' : isWarningMessage(),
                            'text-blue-400' : isInfoMessage(),
                            'text-green-400' : isSuccessMessage(),
                        }"
                         viewBox="0 0 20 20"
                         fill="currentColor"
                    >
                        <path fill-rule="evenodd"
                              d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
                              clip-rule="evenodd"
                        />
                    </svg>
                </div>
                <div class="ml-3">
                    <div
                        class="text-sm leading-5"
                        :class="{
                            'text-red-800' : isErrorMessage(),
                            'text-yellow-800' : isWarningMessage(),
                            'text-blue-800' : isInfoMessage(),
                            'text-green-800' : isSuccessMessage(),
                        }"
                    >
                        <p>{{ message }}</p>
                    </div>
                </div>
            </div>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
            <div class="col-span-1">
                <menu-builder-form
                    :current-item="currentItem"
                    :mode="mode"
                    :entity-types="entityTypes"
                    :parent-items="items"
                ></menu-builder-form>
            </div>
            <div class="col-span-1 lg:col-span-2">
                <div v-if="items && items.length" :class="['has-items', state]">
                    <draggable :list="items" @update="updateSortOrder">
                        <transition-group name="list" tag="div">
                            <menu-builder-item
                                v-for="(item, index) in items"
                                v-if="item.attributes.parent_id == null"
                                :key="item.id"
                                :depth="1"
                                :index="index"
                                :item="item"
                                :current-item="currentItem"
                                :menu-item-resource-url="menuItemResourceUrl"
                            >
                            </menu-builder-item>
                        </transition-group>
                    </draggable>
                </div>
                <div v-else :class="['no-items', state]">
                    <v-card>
                        <div class="message text-center">
                            <div class="message-text font-semibold">
                                {{ message }}
                            </div>
                        </div>
                    </v-card>
                </div>
            </div>
        </div>
    </div>
</template>


<script>
import MenuBuilderForm from "./MenuBuilderForm";
import MenuBuilderItem from "./MenuBuilderItem";
import Draggable from 'vuedraggable';

export default {
    name : "MenuBuilder",
    components : {
        MenuBuilderForm,
        MenuBuilderItem,
        Draggable
    },
    props : {
        menu : {
            type : Object
        },
        entityTypes : {
            type : Array
        },
        menuResourceUrl : {
            type : String,
            required : true
        },
        menuItemResourceUrl : {
            type : String,
            required : true
        },
    },
    computed : {
        allItems() {
            let allItems = [];

            let gatherItems = (items) => {
                items.forEach((item) => {
                    allItems.push(item);

                    if (this.hasChildren(item)) {
                        gatherItems(item.relationships.nested_children);
                    }
                });
            };
            gatherItems(this.items);

            return allItems;
        }
    },
    data() {
        return {
            isDirty : false,

            mode : 'create',
            state : 'ready',

            message : null,
            messageType : null,

            items : [],
            currentItem : this.defaultItem(),
        }
    },
    methods : {
        createItem() {
            this.currentItem = this.defaultItem();
            this.setMode("create");
        },
        storeItem(args) {
            if (!args.item) {
                this.showError('No item provided for store');

                return;
            }

            // Assign the new item
            this.isDirty = true;
            this.currentItem = args.item;
            this.currentItem.is_new = true;
            this.currentItem.attributes.menu_id = this.menu.id;
            this.currentItem.attributes.sort_order = this.setSortOrder(args.item);

            // Send a post request to store the new item
            window.axios
                .post(`${this.menuItemResourceUrl}`, {
                    data : this.currentItem,
                })
                .then(() => {
                    this.reset();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },

        editItem(args) {
            this.currentItem = args.item;
            this.setMode('edit');
        },
        updateItem(args) {
            if (!args.item) {
                this.showError('No item provided for update');

                return;
            }

            // If the parent_id is 0, set it to null
            if (args.item.attributes.parent_id === 0) {
                args.item.attributes.parent_id = null;
            }

            // Assign the item
            this.isDirty = true;
            this.currentItem = args.item;
            this.currentItem.is_dirty = true;
            this.currentItem.attributes.menu_id = this.menu.id;
            this.currentItem.attributes.sort_order = this.setSortOrder(args.item);

            // Send a post request to update the item
            window.axios
                .post(`${this.menuItemResourceUrl}/${this.currentItem.id}/update`, {
                    data : this.currentItem,
                })
                .then(() => {
                    this.reset();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },
        deleteItem(args) {
            if (!args.item) {
                this.showError('No item provided for delete');

                return;
            }

            // Assign the item
            this.isDirty = true;
            this.currentItem = args.item;
            this.currentItem.is_dirty = true;

            // Send a post request to store the new item
            window.axios
                .post(`${this.menuItemResourceUrl}/${this.currentItem.id}/delete`, {
                    data : this.currentItem,
                })
                .then(() => {
                    this.reset();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },

        setMode(mode) {
            this.mode = mode;
        },
        setState(state) {
            this.state = state;
        },
        setMessage(message) {
            this.message = message;
        },
        setMessageType(messageType) {
            this.messageType = messageType;
        },

        reset() {
            this.isDirty = false;
            this.resetMode();
            this.resetState();
            this.resetMessage();
            this.resetCurrentItem();
        },
        resetMode() {
            this.setMode("create");
        },
        resetState() {
            this.setState('ready');
        },
        resetMessage() {
            this.setMessage(null);
            this.resetMessageType(null);
        },
        resetMessageType() {
            this.setMessageType(null);
        },
        resetCurrentItem() {
            this.currentItem = this.defaultItem();
            this.resetMode();
        },

        updateSortOrder() {
            this.items.map((item, index) => {
                item.attributes.sort_order = index + 1;
                window.axios
                    .post(`${this.menuItemResourceUrl}/${item.id}/update`, {
                        data : item
                    });
            });
        },
        fetchExistingItems() {
            this.setState('loading');
            this.setMessage('Loading, please wait...');

            // Make sure we have a menu object
            if (!this.menu) {
                this.showError('Menu object not found for Menu Builder');

                return;
            }

            // Fetch existing menu items from the API
            window.axios
                .get(`${this.menuResourceUrl}/${this.menu.id}`)
                .then((response) => {
                    let result = response.data;
                    if (!result.data) {
                        this.showError('No data returned from API for menu items');

                        return;
                    }

                    // Make sure we have the menu relationships returned
                    let relationships = result.data.relationships;
                    if (!relationships || !relationships.items) {
                        this.showError('No relationships returned from API for menu items');

                        return;
                    }

                    // If there are no existing items, set the no-data state
                    if (relationships.items.length < 1) {
                        this.setState('no-data');
                        this.setMessage('Please complete the form to add new items');

                        return;
                    }

                    // Otherwise, return the existing items
                    this.items = relationships.items;
                    this.setState('ready');
                    this.resetMessage();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },
        defaultItem() {
            return {
                id : null,
                type : 'menu_item',
                attributes : {
                    uuid : null,
                    menu_id : this.menu.id,
                    parent_id : null,

                    name : null,
                    url : null,

                    entity_type : null,
                    entity_id : null,
                    entity_route : null,

                    class : null,
                    image : null,
                    type : 'internal',
                    target : '_self',
                    sort_order : null,
                },
                relationships : {
                    nested_children : []
                },
                is_new : true,
                is_dirty : false,
            }
        },

        findItemById(itemId) {
            if (!itemId) {
                return null;
            }

            return this.allItems.find((item) => {
                return item.id.toString() === itemId.toString();
            });
        },
        hasParent(item) {
            return item.attributes.parent_id !== null;
        },
        hasChildren(item) {
            return item.relationships
                && item.relationships.nested_children
                && item.relationships.nested_children.length > 0;
        },
        setSortOrder(item) {

            // If the item has no parent, set the sort order based on the main items length
            if (!this.hasParent(item)) {
                return this.items.length + 1;
            }

            // Try and find the parent item
            let parentItem = this.findItemById(item.attributes.parent_id);

            // If no parent item found
            if (!parentItem) {
                return this.items.length + 1;
            }

            // If the parent has no children
            if (!this.hasChildren(parentItem)) {
                return 1;
            }

            // Otherwise, set the sort order based on the parent's children length
            return parentItem.relationships.nested_children.length + 1;
        },

        showError(message) {
            this.setState('error');
            this.setMessage(message);
            this.setMessageType('error');
            console.error(message);
        },
        isErrorMessage() {
            return this.messageType === 'error';
        },
        isWarningMessage() {
            return this.messageType === 'warning';
        },
        isInfoMessage() {
            return this.messageType === 'info';
        },
        isSuccessMessage() {
            return this.messageType === 'success';
        },

        registerEventListeners() {
            window.EventBus.$on('menuBuilder:createItem', () => this.createItem());
            window.EventBus.$on('menuBuilder:storeItem', (args) => this.storeItem(args));
            window.EventBus.$on('menuBuilder:editItem', (args) => this.editItem(args));
            window.EventBus.$on('menuBuilder:updateItem', (args) => this.updateItem(args));
            window.EventBus.$on('menuBuilder:deleteItem', (args) => this.deleteItem(args));
            window.EventBus.$on('menuBuilder:showError', (message) => this.showError(message));

            // Handle unsaved changes warning
            let form = document.getElementById('menu-builder-form');
            if (form.length) {
                form.addEventListener('submit', (e) => {
                    this.isDirty = false;
                    window.removeEventListener('beforeunload', () => {
                    });
                });
            }

            window.addEventListener('beforeunload', (e) => {
                if (this.isDirty) {
                    e.preventDefault();
                    e.returnValue = 'Leave page?, changes that you made may not be saved.';
                }
            });
        }
    },
    mounted() {
        this.fetchExistingItems();
        this.registerEventListeners();
    }
}
</script>
