package eu.dnetlib.uoaadmintools.services;

import eu.dnetlib.uoaadmintools.dao.MenuDAO;
import eu.dnetlib.uoaadmintools.dao.MenuItemDAO;
import eu.dnetlib.uoaadmintools.entities.menu.Menu;
import eu.dnetlib.uoaadmintools.entities.menu.MenuFull;
import eu.dnetlib.uoaadmintools.entities.menu.MenuItem;
import eu.dnetlib.uoaadmintools.entities.menu.MenuItemFull;
import eu.dnetlib.uoaadmintoolslibrary.handlers.ContentNotFoundException;
import eu.dnetlib.uoaadmintoolslibrary.handlers.MismatchingContentException;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class MenuService {
    private final Logger log = Logger.getLogger(this.getClass());

    @Autowired
    private MenuDAO menuDAO;

    @Autowired
    private MenuItemDAO menuItemDAO;

    public MenuItem getMenuItem(String id) {
        return menuItemDAO.findById(id);
    }

    public MenuItemFull getMenuItemFull(String id) {
            return this.getMenuItemFull(id, 1);
    }

    public MenuItemFull getMenuItemFull(String id, int maxDepth) {
        MenuItem menuItem = menuItemDAO.findById(id);
        MenuItemFull menuItemFull = new MenuItemFull(menuItem);
        if(maxDepth == 0) {
            menuItemFull.setItems(new ArrayList<>());
            return menuItemFull;
        }

        List<MenuItemFull> menuItemsFull = new ArrayList<>();
        for(String menuItemString : menuItem.getItems()) {
            menuItemsFull.add(this.getMenuItemFull(menuItemString, maxDepth-1));
        }
        menuItemFull.setItems(menuItemsFull);

        return menuItemFull;
    }

    public List<MenuItem> getMenuItemsByParent(String parentId, String portalPid) {
        if(portalPid != null) {
            return menuItemDAO.findByParentItemIdAndPortalPid(parentId, portalPid);
        } else {
            return menuItemDAO.findByParentItemId(parentId);
        }
    }

    public List<MenuItemFull> getRootMenuItemsFull(String portalPid) {
        List<MenuItem> rootMenuItems = this.getMenuItemsByParent(null, portalPid);
        List<MenuItemFull> rootMenuItemsFull = new ArrayList<>();
        for(MenuItem rootMenuItem : rootMenuItems) {
            MenuItemFull rootMenuItemFull = new MenuItemFull(rootMenuItem);

            List<MenuItemFull> childrenMenuItemsFull = new ArrayList<>();
            for(String childMenuItemString : rootMenuItem.getItems()) {
                childrenMenuItemsFull.add(this.getMenuItemFull(childMenuItemString));
            }
            rootMenuItemFull.setItems(childrenMenuItemsFull);
            rootMenuItemsFull.add(rootMenuItemFull);
        }

        return rootMenuItemsFull;
    }

    public MenuFull getMenuFull(String portalPid) {
        MenuFull menuFull = new MenuFull();

        Menu menu = menuDAO.findByPortalPid(portalPid);
        List<MenuItemFull> featuredMenuItems = new ArrayList<>();
        List<MenuItemFull> menuItems = new ArrayList<>();

        if(menu == null) {
            menuFull.setFeaturedMenuEnabled(true);
            menuFull.setMenuEnabled(true);
        } else {
            for (String menuItemId : menu.getFeaturedMenuItems()) {
                featuredMenuItems.add(getFullRootMenuItemById(menuItemId, portalPid, true));
            }

            for (String menuItemId : menu.getMenuItems()) {
                menuItems.add(getFullRootMenuItemById(menuItemId, portalPid, false));
            }

            menuFull.setFeaturedMenuEnabled(menu.getIsFeaturedMenuEnabled());
            menuFull.setMenuEnabled(menu.getIsMenuEnabled());
        }

        menuFull.setPortalPid(portalPid);
        menuFull.setFeaturedMenuItems(featuredMenuItems);
        menuFull.setMenuItems(menuItems);

        return menuFull;
    }

    private MenuItemFull getFullRootMenuItemById(String menuItemId, String portalPid, Boolean isFeatured) {
        MenuItem rootMenuItem = menuItemDAO.findById(menuItemId);
        if(rootMenuItem.getIsFeatured() != isFeatured) {
            // EXCEPTION - MismatchingContent
            throw new MismatchingContentException("getFullRootMenuItemById: Menu item should "+(isFeatured ? "" : "not ")+"be featured");
        }
        if(!rootMenuItem.getPortalPid().equals(portalPid)) {
            // EXCEPTION - MismatchingContent
            throw new MismatchingContentException("getFullRootMenuItemById: Menu item with id: "+rootMenuItem.getId()+" has portal pid: "+rootMenuItem.getPortalPid()+" instead of "+portalPid);
        }
        if(rootMenuItem.getParentItemId() != null) {
            // EXCEPTION - MismatchingContent
            throw new MismatchingContentException("getFullRootMenuItemById: Menu item should be root (no parentId), but instead parentId: "+rootMenuItem.getParentItemId());
        }

        MenuItemFull rootMenuItemFull = new MenuItemFull(rootMenuItem);

        List<MenuItemFull> childrenMenuItemsFull = new ArrayList<>();
        for(String childMenuItemString : rootMenuItem.getItems()) {
            childrenMenuItemsFull.add(this.getMenuItemFull(childMenuItemString));
        }
        rootMenuItemFull.setItems(childrenMenuItemsFull);
        return rootMenuItemFull;
    }

    private MenuItemFull insertMenuItem(MenuItem menuItem, String portalPid) {
        MenuItem parent = null;
        if(menuItem.getParentItemId() != null && !menuItem.getParentItemId().equals("")) {
            parent = getMenuItem(menuItem.getParentItemId());
            if (!parent.getPortalPid().equals(portalPid)) {
                // EXCEPTION - MismatchingContent
                throw new MismatchingContentException("insertMenuItem: parent ("+parent.getParentItemId()+") of MenuItem has portalPid: " + parent.getPortalPid() + " instead of " + portalPid);
            }
        } else {
            menuItem.setParentItemId(null);
        }

        if(menuItem.getItems() == null) {
            List<String> menuItems = new ArrayList<String>();
            menuItem.setItems(menuItems);
        }
        menuItemDAO.save(menuItem);

        if(parent != null) {
            List<String> siblingsOfNew = parent.getItems();
            siblingsOfNew.add(menuItem.getId());
            parent.setItems(siblingsOfNew);
            menuItemDAO.save(parent);
        }

        MenuItemFull menuItemFull  = new MenuItemFull(menuItem);
        menuItemFull.setItems(new ArrayList<>());



        return menuItemFull;
    }

    public MenuItemFull insertMenuItemInMenu(MenuItem menuItem, String portalPid) {
        MenuItemFull menuItemFull = insertMenuItem(menuItem, portalPid);
        Menu menu = menuDAO.findByPortalPid(portalPid);
        if(menu == null) {
            menu = new Menu(portalPid);
        }
        if(menuItem.getIsFeatured() && menuItem.getParentItemId() == null) {
            menu.getFeaturedMenuItems().add(menuItemFull.getId());
        } else if(menuItem.getParentItemId() == null) {
            menu.getMenuItems().add(menuItemFull.getId());
        }
        menuDAO.save(menu);
        return menuItemFull;
    }

    public MenuItemFull updateMenuItem(MenuItemFull menuItemFull) {
        MenuItem menuItem = getMenuItemByMenuItemFull(menuItemFull);

        // Update should not affect parent or children - only basic info can be updated
        MenuItem oldMenuItem = getMenuItem(menuItemFull.getId());
        menuItem.setItems(oldMenuItem.getItems());
        menuItem.setParentItemId(oldMenuItem.getParentItemId());

        menuItemDAO.save(menuItem);
        menuItemFull = getMenuItemFull(menuItem.getId());

        return menuItemFull;
    }

    public MenuItemFull updateMenu(MenuItemFull menuItemFull, String portalPid) {
        menuItemFull = updateMenuItem(menuItemFull);
        Menu menu = menuDAO.findByPortalPid(portalPid);
        if(menu == null) {
            // EXCEPTION - ContentNotFoundException
            throw new ContentNotFoundException("updateMenu: No Menu found for portal pid: "+portalPid);
        }
        List<String> featuredMenuItems = menu.getFeaturedMenuItems();
        List<String> menuItems = menu.getMenuItems();
        if (menuItemFull.getIsFeatured() && menuItemFull.getParentItemId() == null && !featuredMenuItems.contains(menuItemFull.getId())) {
            featuredMenuItems.add(menuItemFull.getId());
        } else if ((!menuItemFull.getIsFeatured() || menuItemFull.getParentItemId() != null) && featuredMenuItems.contains(menuItemFull.getId())) {
            featuredMenuItems.remove(menuItemFull.getId());
        } else if(!menuItemFull.getIsFeatured() && menuItemFull.getParentItemId() == null && !menuItems.contains(menuItemFull.getId())) {
            menuItems.add(menuItemFull.getId());
        } else if ((menuItemFull.getIsFeatured() || menuItemFull.getParentItemId() != null) && menuItems.contains(menuItemFull.getId())) {
            menuItems.remove(menuItemFull.getId());
        }
        menuDAO.save(menu);

        return menuItemFull;
    }

    public Boolean deleteMenuItem(String id, String portalPid) throws Exception {
        Menu menu = menuDAO.findByPortalPid(portalPid);
        if(menu == null) {
            // EXCEPTION - ContentNotFoundException
            throw new ContentNotFoundException("deleteMenuItem: No Menu found for portal pid: "+portalPid);
        }
        List<String> featuredMenuItems = menu.getFeaturedMenuItems();
        List<String> menuItems = menu.getMenuItems();
        if(featuredMenuItems.contains(id)) {
            featuredMenuItems.remove(id);
        } else if(menuItems.contains(id)) {
            menuItems.remove(id);
        }
        menuDAO.save(menu);

//        menuItemDAO.delete(id);
        log.debug("delete menu item; "+id);
        List<String> menuItemsToDelete = new ArrayList<>();
        menuItemsToDelete.add(id);
        return deleteMenuItems(menuItemsToDelete, portalPid);
    }

    public Boolean deleteMenuItems(List<String> menuItems, String portalPid) throws Exception {
        if(menuItems == null) {
            return true;
        }
        for (String id: menuItems) {
            MenuItem menuItem = menuItemDAO.findById(id);

            if(!portalPid.equals(menuItem.getPortalPid())) {
                // EXCEPTION - MismatchingContent
                throw new MismatchingContentException("Delete Menu Items: MenuItem with id: "+id+" has portalPid: "+menuItem.getPortalPid()+" instead of "+portalPid);
            }

            deleteMenuItems(menuItem.getItems(), portalPid);

            if(menuItem.getParentItemId() != null && !menuItem.getParentItemId().equals("")) {
                MenuItem parent = menuItemDAO.findById(menuItem.getParentItemId());
                List<String> siblingsOfDeleted = parent.getItems();
                siblingsOfDeleted.remove(id);
                parent.setItems(siblingsOfDeleted);
                menuItemDAO.save(parent);
            }

            menuItemDAO.delete(id);
        }

        return true;
    }

    public Boolean deleteMenuByPortalPid(String portalPid) throws Exception {
        Menu menu = menuDAO.findByPortalPid(portalPid);
        if(menu != null) {
            deleteMenuItems(menu.getMenuItems(), portalPid);
            deleteMenuItems(menu.getFeaturedMenuItems(), portalPid);
            menuDAO.deleteByPortalPid(portalPid);
        }
        return true;
    }

        public Boolean reorderMenuItems(List<MenuItemFull> menuItemsFull, String portalPid) {
        List<String> menuItemIds = new ArrayList<>();
//        menuItemIds = menuItemsFull.stream().map((MenuItem menuItem) -> menuItem.getId()).collect(Collectors.toList());
            boolean isFeatured = menuItemsFull.get(0).getIsFeatured();
        String parentId = menuItemsFull.get(0).getParentItemId();

        for (MenuItemFull menuItem: menuItemsFull) {
            if (!portalPid.equals(menuItem.getPortalPid())) {
                // EXCEPTION - MismatchingContent
                throw new MismatchingContentException("reorderMenuItems: MenuItems for reordering have not the same portalPid");
            }
            if(menuItem.getIsFeatured() != isFeatured) {
                // EXCEPTION - MismatchingContent
                throw new MismatchingContentException("reorderMenuItems: MenuItems for reordering have not the same isFeatured");
            }
            if((menuItem.getParentItemId() == null && parentId != null) || (parentId == null && menuItem.getParentItemId() != null) || (parentId != null && !menuItem.getParentItemId().equals(parentId))) {
                // EXCEPTION - MismatchingContent
                throw new MismatchingContentException("reorderMenuItems: MenuItems for reordering have not the same parentItemId");
            }
            menuItemIds.add(menuItem.getId());

        }

        List<String> savedMenuItems;
        if(parentId != null) {
            MenuItem parent =  menuItemDAO.findById(parentId);
            savedMenuItems = parent.getItems();
            menuItemIds = this.addSavedMenuItems(savedMenuItems, menuItemIds, menuItemsFull);
            parent.setItems(menuItemIds);
            menuItemDAO.save(parent);
        } else if(isFeatured) {
            Menu menu = menuDAO.findByPortalPid(portalPid);
            savedMenuItems = menu.getFeaturedMenuItems();
            menuItemIds = this.addSavedMenuItems(savedMenuItems, menuItemIds, menuItemsFull);
            menu.setFeaturedMenuItems(menuItemIds);
            menuDAO.save(menu);
        } else {
            Menu menu = menuDAO.findByPortalPid(portalPid);
            savedMenuItems = menu.getMenuItems();
            menuItemIds = this.addSavedMenuItems(savedMenuItems, menuItemIds, menuItemsFull);
            menu.setMenuItems(menuItemIds);
            menuDAO.save(menu);
        }

        return true;
    }

    private List<String> addSavedMenuItems(List<String> savedMenuItems, List<String> menuItemIds, List<MenuItemFull> menuItemsFull) {
        for (String menuId : savedMenuItems) {
            if (!menuItemIds.contains(menuId)) {
                menuItemIds.add(menuId);
            }
        }
        return  menuItemIds;
    }


    private MenuItem getMenuItemByMenuItemFull(MenuItemFull menuItemFull) {
        MenuItem menuItem = new MenuItem();
        menuItem.setId(menuItemFull.getId());
        menuItem.setTitle(menuItemFull.getTitle());
        menuItem.setUrl(menuItemFull.getUrl());
        menuItem.setType(menuItemFull.getType());
        menuItem.setTarget(menuItemFull.getTarget());
        menuItem.setRoute(menuItemFull.getRoute());
        menuItem.setPortalPid(menuItemFull.getPortalPid());
        menuItem.setParentItemId(menuItemFull.getParentItemId());
        menuItem.setIsFeatured(menuItemFull.getIsFeatured());

        List<MenuItemFull> menuItemsFull = menuItemFull.getItems();
        List<String> menuItems = new ArrayList<String>();
        if(menuItemsFull != null) {
            for (MenuItemFull childMenuItemFull : menuItemsFull) {
                menuItems.add(childMenuItemFull.getId());
            }
        }
        menuItem.setItems(menuItems);

        return menuItem;
    }

    public Menu toggleMenu(String pid, String status, String isFeatured) throws Exception {
        Menu menu = menuDAO.findByPortalPid(pid);
        if(isFeatured != null && Boolean.parseBoolean(isFeatured)) {
            menu.setFeaturedMenuEnabled(Boolean.parseBoolean(status));
        } else {
            menu.setMenuEnabled(Boolean.parseBoolean(status));
        }
        return menuDAO.save(menu);
    }
}
