package com.xy.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.collections.list.JArrayList;
import com.xy.collections.map.JMap;
import com.xy.consts.CommConsts;
import com.xy.dto.BatchDeleteDto;
import com.xy.dto.CommDto;
import com.xy.dto.SysRoleDto;
import com.xy.entity.*;
import com.xy.mapper.SysRoleMapper;
import com.xy.redis.RedisCache;
import com.xy.service.SysDeptRoleService;
import com.xy.service.SysRoleMenuService;
import com.xy.service.SysRoleService;
import com.xy.service.SysUserRoleService;
import com.xy.util.ExcelUtils;
import com.xy.utils.Beans;
import com.xy.utils.DataTime;
import com.xy.utils.PageBean;
import com.xy.utils.R;
import com.xy.vo.SysRoleVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.xy.utils.PlusBeans.toIPage;
import static com.xy.utils.PlusBeans.toPageBean;

/***
 * 角色服务实现类
 * @author 谭斌
 * @date 2022/12/12 17:17
 */

@Api(tags = "角色管理")
@Service
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    private final SysRoleMenuService sysRoleMenuService;
    private final SysDeptRoleService sysDeptRoleService;
    private final SysUserRoleService sysUserRoleService;
    private final SysMenuServiceImpl sysMenuService;
    private final SysRoleMenuServiceImpl sysRoleMenuServiceImpl;
    private final SysSystemServiceImpl sysSystemService;

    @ApiOperation(value = "新增、更新角色信息", notes = "新增、更新角色信息")
    @PostMapping("saveOrUpdate")
    public R<Long> saveOrUpdate(@Valid @RequestBody SysRoleDto.SaveOrUpdate sysRoleDto) {
        Long id = sysRoleDto.getId();
        LambdaQueryWrapper<SysRole> lqw = new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getCode, sysRoleDto.getCode())
                .eq(SysRole::getSysId, sysRoleDto.getSysId());
        if (id != null) {
            lqw.ne(SysRole::getId, id);
        }
        long count = count(lqw);
        if (count > 0) {
            return R.fail("此角色标识已存在");
        }
        SysRole sysRole = Beans.copy(SysRole.class, sysRoleDto).saveOrUpdate(RedisCache.getLoginId(Long.class), id);
        saveOrUpdate(sysRole);
        List<Long> menuIds = sysRoleDto.getMenuIds();
        if (CollUtil.isNotEmpty(menuIds)) {
            Boolean selectAdmin = false;
            if (CommConsts.ADMIN_ROLE_CODE.equals(sysRole.getCode())) {
                selectAdmin = true;
            }

            Long sysRoleId = sysRole.getId();
            String clientType = sysRoleDto.getClientType();
            if (StrUtil.isNotEmpty(clientType)) {
                //如果有客户端类型，只能移除次客户端类型对应的菜单
                List<SysMenu> sysMenus = sysMenuService.list(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getClientType, clientType).eq(SysMenu::getSysId, sysRoleDto.getSysId()));
                if (CollUtil.isNotEmpty(sysMenus)) {
                    List<Long> mIds = sysMenus.stream().map(SysMenu::getId).collect(Collectors.toList());
                    sysRoleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, sysRoleId)
                            .in(SysRoleMenu::getMenuId, mIds)
                    );

                }
            } else {
                sysRoleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, sysRoleId));
            }

            //角色菜单绑定
            Boolean finalSelectAdmin = selectAdmin;
            List<SysRoleMenu> sysRoleMenus = menuIds.stream().map(menuId -> {
                SysRoleMenu sysRoleMenu = new SysRoleMenu()
                        .createId()
                        .setSelectAdmin(finalSelectAdmin)
                        .setMenuId(menuId)
                        .setSysId(sysRoleDto.getSysId())
                        .setRoleId(sysRoleId);
                return sysRoleMenu;

            }).collect(Collectors.toList());
            //批量保存菜单角色绑定关系
            sysRoleMenuService.saveBatch(sysRoleMenus);
        }

        return R.ok(sysRole.getId());
    }


    /**
     * 更新菜单管理员查询权限
     *
     * @param updateAdminRoleMenu
     * @return
     */
    @ApiOperation(value = "更新菜单管理员查询权限", notes = "更新菜单管理员查询权限")
    @PostMapping("updateAdminRoleMenu")
    public R<Boolean> updateAdminRoleMenu(@Valid @RequestBody @Validated SysRoleDto.UpdateAdminRoleMenu updateAdminRoleMenu) {
        return R.ok(sysRoleMenuService.updateAdminRoleMenu(updateAdminRoleMenu));
    }

    @ApiOperation(value = "删除角色", notes = "删除角色")
    @DeleteMapping("batchDelete")
    public R<Boolean> batchDelete(@Valid @RequestBody BatchDeleteDto batchDeleteDto) {
        List<Long> ids = batchDeleteDto.getIds();
        if (CollUtil.isNotEmpty(ids)) {
            for (Long id : ids) {
                deleteRoleById(new CommDto().setId(id));
            }
        }
        return R.ok(Boolean.TRUE);
    }

    @ApiOperation(value = "删除角色", notes = "删除角色")
    @DeleteMapping("deleteRoleById")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> deleteRoleById(CommDto commDto) {
        Long id = commDto.getId();
        //角色 sys_role
        removeById(id);
        //角色菜单 sys_role_menu
        sysRoleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, id));
        //部门角色 sys_dept_role
        sysDeptRoleService.remove(Wrappers.<SysDeptRole>lambdaQuery().eq(SysDeptRole::getRoleId, id));
        //用户角色 sys_user_role
        sysUserRoleService.remove(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getRoleId, id));
        return R.ok(Boolean.TRUE);
    }


    @ApiOperation(value = "分页获取角色信息", notes = "分页获取角色信息")
    @PostMapping("page")
    public R<PageBean<SysRoleVo>> getRolePage(@RequestBody SysRoleDto.Page sysRoleDto) {
        PageBean pageBean = sysRoleDto.getPage();
        LambdaQueryWrapper<SysRole> lqw = new LambdaQueryWrapper<>();
        lqw.ne(SysRole::getCode, "xy_admin");
        lqw.eq(sysRoleDto.getSysId() != null, SysRole::getSysId, sysRoleDto.getSysId());
        lqw.likeRight(StrUtil.isNotEmpty(sysRoleDto.getName()), SysRole::getName, sysRoleDto.getName());
        lqw.likeRight(StrUtil.isNotEmpty(sysRoleDto.getCode()), SysRole::getCode, sysRoleDto.getCode());
        lqw.eq(sysRoleDto.getStatus() != null, SysRole::getStatus, sysRoleDto.getStatus());
        lqw.ge(ObjectUtil.isNotEmpty(sysRoleDto.getBeginCreateTime()), SysRole::getCreateTime, sysRoleDto.getBeginCreateTime());
        lqw.le(ObjectUtil.isNotEmpty(sysRoleDto.getEndCreateTime()), SysRole::getCreateTime, sysRoleDto.getEndCreateTime());
        lqw.orderByDesc(pageBean.getOrders().size() == 0, SysRole::getCreateTime);

        boolean b = RedisCache.getDataAuth(sysRoleDto.getSysId());
        //管理员查询权限查询所有，普通查询权限查询预留和自创建
        if (!b) {
            lqw.and(sysRoleLambdaQueryWrapper -> sysRoleLambdaQueryWrapper
                    .eq(SysRole::getCreateUser, RedisCache.getLoginId(Long.class))
                    .or()
                    .eq(SysRole::getIsLeave, true)
            );
        }
        //角色菜单信息
        IPage<SysRole> page = page(toIPage(pageBean), lqw);
        PageBean<SysRoleVo> sysRoleVoPageBean = toPageBean(SysRoleVo.class, page);
        List<SysRoleVo> records = sysRoleVoPageBean.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            //拿到角色菜单信息返回
            records.forEach(sysRoleVo -> {
                Long id = sysRoleVo.getId();
                List<Long> menuIdsByRoleId = sysRoleMenuService.getMenuIdsByRoleId(id, null);
                sysRoleVo.setMenuIds(menuIdsByRoleId);
            });
            sysRoleVoPageBean.setRecords(records);
        }
        return R.ok(sysRoleVoPageBean);
    }

    @SneakyThrows
    @PostMapping("export")
    @ApiOperation("导出角色菜单")
    public void export(HttpServletResponse response) {
        List<SysRole> xyAdmins = list(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getCode, "xy_admin")
        );
        //所有角色信息
        List<SysRole> sysRoles = list();
        JMap<Long, SysRole> sysRoleJMaps = new JArrayList<>(sysRoles).toMap(SysRole::getId).cover();
        //所有系统信息
        List<SysSystem> sysSystems = sysSystemService.list();
        JMap<Long, SysSystem> sysSystemJMaps = new JArrayList<>(sysSystems).toMap(SysSystem::getId).cover();
        //所有菜单信息
        List<SysMenu> sysMenus = sysMenuService.list();
        JMap<Long, SysMenu> sysMenuJMaps = new JArrayList<>(sysMenus).toMap(SysMenu::getId).cover();
        //查询角色菜单信息
        List<SysRoleMenu> sysRoleMenus = sysRoleMenuServiceImpl.list(new LambdaQueryWrapper<SysRoleMenu>()
                .notIn(SysRoleMenu::getRoleId, new JArrayList<>(xyAdmins).getProperty(SysRole::getId))
                .orderByAsc(SysRoleMenu::getRoleId, SysRoleMenu::getSysId)
        );
        List<ExportInfo> exportInfos = new ArrayList<>();
        sysRoleMenus.forEach(sysRoleMenu -> {
            SysRole sysRole = sysRoleJMaps.get(sysRoleMenu.getRoleId());
            SysSystem sysSystem = sysSystemJMaps.get(sysRoleMenu.getSysId());
            SysMenu sysMenu = sysMenuJMaps.get(sysRoleMenu.getMenuId());
            ExportInfo exportInfo = new ExportInfo();
            exportInfo.setRoleName(sysRole.getName());
            exportInfo.setSysName(sysSystem.getName());
            exportInfo.setMenuName(sysMenu.getName());
            exportInfo.setEnName(sysMenu.getEnName());
            exportInfo.setRemark(sysMenu.getRemark());
            exportInfos.add(exportInfo);
        });
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("角色权限表_" + DataTime.getSring("yyyy-MM-dd") + ".xlsx", "UTF-8"));
        response.setContentType("application/xlsx");
        EasyExcel.write(response.getOutputStream(), ExportInfo.class)
                .autoCloseStream(false)
                .registerWriteHandler(new ExcelUtils.MergeCellStrategyHandler(true, 1, Arrays.asList(0, 1)))
                .registerWriteHandler(new ExcelUtils.CustomCellStyleHandler())
                .sheet("角色权限表")
                .doWrite(exportInfos);
    }

    @Data
    public static class ExportInfo {

        @ColumnWidth(25)
        @ExcelProperty("角色名称")
        private String roleName;

        @ColumnWidth(25)
        @ExcelProperty("所属系统")
        private String sysName;

        @ColumnWidth(50)
        @ExcelProperty("菜单名称")
        private String menuName;

        @ColumnWidth(50)
        @ExcelProperty("菜单英文名称")
        private String enName;

        @ColumnWidth(100)
        @ExcelProperty("备注")
        private String remark;
    }

}
