package com.njzscloud.supervisory.sys.menu.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.supervisory.sys.menu.contant.MenuCategory;
import com.njzscloud.supervisory.sys.menu.mapper.MenuMapper;
import com.njzscloud.supervisory.sys.menu.pojo.entity.MenuEntity;
import com.njzscloud.supervisory.sys.menu.pojo.param.MenuAddParam;
import com.njzscloud.supervisory.sys.menu.pojo.param.MenuModifyParam;
import com.njzscloud.supervisory.sys.menu.pojo.param.MenuSearchParam;
import com.njzscloud.supervisory.sys.menu.pojo.result.MenuDetailResult;
import com.njzscloud.supervisory.sys.resource.contant.ResourceOrigin;
import com.njzscloud.supervisory.sys.resource.pojo.entity.ResourceEntity;
import com.njzscloud.supervisory.sys.resource.service.ResourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜单信息表
 */
@Slf4j
@Service
public class MenuService extends ServiceImpl<MenuMapper, MenuEntity> implements IService<MenuEntity> {

    @Autowired
    private ResourceService resourceService;

    /**
     * 新增
     */
    @Transactional(rollbackFor = Exception.class)
    public Long add(MenuAddParam menuAddParam) {
        String sn = menuAddParam.getSn();
        boolean exists = this.exists(Wrappers.<MenuEntity>lambdaQuery().eq(MenuEntity::getSn, sn));
        Assert.isFalse(exists, () -> Exceptions.exception("资源编码重复"));
        MenuEntity menuEntity = BeanUtil.copyProperties(menuAddParam, MenuEntity.class);
        Long pid = menuEntity.getPid();
        if (pid == null) {
            menuEntity.setPid(0L);
            pid = 0L;
        }
        String title = menuEntity.getTitle();
        if (pid == 0) {
            menuEntity.setTier(1);
            menuEntity.setBreadcrumb(CollUtil.newArrayList(title));
        } else {
            MenuEntity parent = this.getById(pid);
            Assert.notNull(parent, () -> Exceptions.exception("上级菜单不存在"));
            menuEntity.setTier(parent.getTier() + 1);
            List<String> breadcrumb = parent.getBreadcrumb();
            breadcrumb.add(title);
            menuEntity.setBreadcrumb(breadcrumb);
        }

        Integer sort = menuEntity.getSort();
        if (sort == null || sort == 0) {
            Integer maxSort = this.getOne(Wrappers.<MenuEntity>query().eq("pid", pid).orderByDesc("sort").select("IFNULL(MAX(sort),0) sort")).getSort();
            menuEntity.setSort(maxSort + 1);
        } else {
            this.update(Wrappers.<MenuEntity>lambdaUpdate().eq(MenuEntity::getPid, pid).ge(MenuEntity::getSort, sort).setIncrBy(MenuEntity::getSort, 1));
        }

        this.save(menuEntity);
        Long menuEntityId = menuEntity.getId();
        exists = resourceService.exists(Wrappers.<ResourceEntity>lambdaQuery().eq(ResourceEntity::getSn, sn));
        Assert.isFalse(exists, () -> Exceptions.exception("资源编码重复"));
        resourceService.save(new ResourceEntity()
                .setSn(sn)
                .setDataId(menuEntityId)
                .setTableName(ResourceOrigin.Menu.getVal())
                .setMemo("菜单资源-" + menuAddParam.getMenuCategory().getTxt() + "-" + menuAddParam.getTitle())
        );
        return menuEntityId;
    }

    /**
     * 修改
     */
    @Transactional(rollbackFor = Exception.class)
    public void modify(MenuModifyParam menuModifyParam) {
        MenuEntity menuEntity = BeanUtil.copyProperties(menuModifyParam, MenuEntity.class);
        Long id = menuEntity.getId();
        Long pid = menuEntity.getPid();
        MenuEntity oldMenuEntity = this.getById(id);
        Assert.notNull(oldMenuEntity, () -> Exceptions.exception("菜单不存在"));
        MenuCategory oldMenuCategory = oldMenuEntity.getMenuCategory();
        String routeName = menuEntity.getRouteName();

        if (oldMenuCategory == MenuCategory.Page || oldMenuCategory == MenuCategory.SubPage && routeName != null) {
            Assert.notBlank(routeName, () -> Exceptions.exception("路由名称不能为空"));
        } else if (oldMenuCategory == MenuCategory.Catalog || oldMenuCategory == MenuCategory.Btn && routeName != null) {
            Assert.isTrue(StrUtil.isBlank(routeName), () -> Exceptions.exception("菜单目录和按钮不用指定路由名称"));
        }
        Long oldPid = oldMenuEntity.getPid();
        if (pid != null && !pid.equals(oldPid)) {
            MenuEntity parent = this.getById(pid);
            Assert.notNull(parent, () -> Exceptions.exception("上级菜单不存在"));
            String title = menuEntity.getTitle();
            List<String> breadcrumb = parent.getBreadcrumb();
            breadcrumb.add(StrUtil.isBlank(title) ? oldMenuEntity.getTitle() : title);
            menuEntity.setBreadcrumb(breadcrumb)
                    .setTier(parent.getTier() + 1);

            this.modifyChild(menuEntity);
        }

        Integer sort = menuEntity.getSort();
        if (sort != null && sort != 0) {
            List<MenuEntity> list = this.list(Wrappers.<MenuEntity>lambdaQuery()
                    .eq(MenuEntity::getPid, pid == null ? oldPid : pid)
                    .ge(MenuEntity::getSort, sort)
                    .ne(MenuEntity::getId, id)
                    .select(MenuEntity::getSort, MenuEntity::getId)
            );
            if (CollUtil.isNotEmpty(list)) {
                list.forEach(item -> item.setSort(sort + 1));
                this.updateBatchById(list);
            }
        }

        this.updateById(menuEntity);

        String title = menuModifyParam.getTitle();
        if (StrUtil.isBlank(title) || oldMenuEntity.getTitle().equals(title)) return;
        resourceService.update(Wrappers.<ResourceEntity>lambdaUpdate()
                .eq(ResourceEntity::getTableName, ResourceOrigin.Menu.getVal())
                .eq(ResourceEntity::getDataId, id)
                .set(ResourceEntity::getMemo, "菜单资源-" + oldMenuEntity.getMenuCategory().getTxt() + "-" + title));
    }

    private void modifyChild(MenuEntity menuEntity) {
        MenuCategory menuCategory = menuEntity.getMenuCategory();
        if (menuCategory == MenuCategory.SubPage || menuCategory == MenuCategory.Btn) return;

        List<String> breadcrumb = menuEntity.getBreadcrumb();
        int tier = menuEntity.getTier() + 1;
        Long pid = menuEntity.getId();


        List<MenuEntity> children = this.list(Wrappers.<MenuEntity>lambdaQuery().eq(MenuEntity::getPid, pid))
                .stream()
                .map(it -> {
                    List<String> temp = CollUtil.newArrayList(breadcrumb);
                    temp.add(it.getTitle());
                    return new MenuEntity()
                            .setId(it.getId())
                            .setBreadcrumb(temp)
                            .setTier(tier);
                }).collect(Collectors.toList());

        // noinspection SpringTransactionalMethodCallsInspection
        this.updateBatchById(children);

        children.forEach(this::modifyChild);
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(List<Long> ids) {
        boolean occupied = resourceService.occupied(ids, ResourceOrigin.Menu);
        Assert.isFalse(occupied, () -> Exceptions.exception("菜单已被分配，不能删除"));

        this.removeBatchByIds(ids);
        resourceService.delRes(ids, ResourceOrigin.Menu);
    }

    /**
     * 详情
     */
    public MenuDetailResult detail(Long id) {
        return BeanUtil.copyProperties(this.getById(id), MenuDetailResult.class);
    }

    /**
     * 查询所有
     */
    public List<MenuDetailResult> listAll(Long pid, MenuSearchParam menuSearchParam) {
        String title = menuSearchParam.getTitle();
        String routeName = menuSearchParam.getRouteName();
        return this.list(Wrappers.<MenuEntity>lambdaQuery()
                        .eq(pid != null, MenuEntity::getPid, pid)
                        .like(StrUtil.isNotBlank(title), MenuEntity::getTitle, title)
                        .like(StrUtil.isNotBlank(routeName), MenuEntity::getRouteName, routeName)
                        .orderByAsc(Arrays.asList(MenuEntity::getTier, MenuEntity::getSort)))
                .stream()
                .map(it -> BeanUtil.copyProperties(it, MenuDetailResult.class))
                .collect(Collectors.toList());
    }
}
