package com.sunyard.ssp.modules.user.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.config.security.permission.MyAccessDecisionManager;
import com.sunyard.config.security.permission.MyFilterSecurityInterceptor;
import com.sunyard.config.security.permission.MySecurityMetadataSource;
import com.sunyard.ssp.common.PageVo;
import com.sunyard.ssp.common.Result;
import com.sunyard.ssp.common.constant.CommonConstant;
import com.sunyard.ssp.common.constant.ValidatorConstant;
import com.sunyard.ssp.modules.user.entity.ScPermission;
import com.sunyard.ssp.modules.user.entity.ScRolePermission;
import com.sunyard.ssp.modules.user.entity.ScUser;
import com.sunyard.ssp.modules.user.service.IScPermissionService;
import com.sunyard.ssp.modules.user.service.IScRolePermissionService;
import com.sunyard.ssp.modules.user.utils.validator.PermissionValidator;
import com.sunyard.ssp.utils.ResultUtil;
import com.sunyard.ssp.utils.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.DataBinder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.sunyard.ssp.common.constant.CommonConstant.STATUS_DISABLE;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author tsz
 * @since 2020-03-11
 */
@RestController
@Slf4j
@RequestMapping("/permission")
@Transactional
public class ScPermissionController {

    @Autowired
    PermissionValidator permissionValidator;

    @Autowired
    WebInvocationPrivilegeEvaluator privilegeEvaluator;

    @Autowired
    HttpServletRequest request;

    @Autowired
    HttpServletResponse response;

    @Autowired
    SecurityUtil securityUtil;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    MyFilterSecurityInterceptor myFilterSecurityInterceptor;

    @Autowired
    MySecurityMetadataSource mySecurityMetadataSource;

    @Autowired
    MyAccessDecisionManager myAccessDecisionManager;

    @Autowired
    IScPermissionService permissionService;

    @Autowired
    IScRolePermissionService rolePermissionService;

    @InitBinder(ValidatorConstant.PERMISSION_BASE_NAME)
    public void initBainder(DataBinder binder){
        binder.addValidators(permissionValidator);
    }

    @RequestMapping(value = "/getById",method = RequestMethod.GET)
    @ResponseBody
    public Result<ScPermission> get(@RequestParam String id){

        ScPermission entity = permissionService.getById(id);
        return new ResultUtil<ScPermission>().setData(entity);
    }

    @RequestMapping(value = "/getAll",method = RequestMethod.GET)
    @ResponseBody
    public Result<List<ScPermission>> getAll(){

        List<ScPermission> list = permissionService.list();
        return new ResultUtil<List<ScPermission>>().setData(list);
    }

    @RequestMapping(value = "/page",method = RequestMethod.GET)
    @ResponseBody
    public Result<IPage<ScPermission>> getByPage(@ModelAttribute PageVo page){
        Page<ScPermission> data = new Page<>( page.getPageNumber(),page.getPageSize());
        IPage<ScPermission> iPage =  permissionService.page(data);
        return new ResultUtil<IPage<ScPermission>>().setData(iPage);
    }

    /*@RequestMapping(value = "/save",method = RequestMethod.POST)
    @ResponseBody
    public Result<ScPermission> save(@ModelAttribute ScPermission entity){

        boolean e = permissionService.save(entity);

        if ( !e ){
            return new ResultUtil<ScPermission>().setErrorMsg("添加失败");
        }
        return new ResultUtil<ScPermission>().setSuccessMsg("添加成功");
    }*/

    /*@RequestMapping(value = "/update",method = RequestMethod.PUT)
    @ResponseBody
    public Result<UpdateChainWrapper<ScPermission>> update(@ModelAttribute ScPermission entity){

        boolean e = permissionService.updateById(entity);
        if ( !e ){
            return new ResultUtil<UpdateChainWrapper<ScPermission>>().setErrorMsg("更新失败");
        }
        return new ResultUtil<UpdateChainWrapper<ScPermission>>().setSuccessMsg("更新成功");
    }*/

    @RequestMapping(value = "/deleteByIds",method = RequestMethod.DELETE)
    @ResponseBody
    public Result<Object> delAllByIds(@RequestParam String[] ids){

        for(String id:ids){
            permissionService.removeById(id);
        }
        return new ResultUtil<Object>().setSuccessMsg("批量通过id删除数据成功");
    }

    @RequestMapping(value = "/getMenuList",method = RequestMethod.GET)
    public Result<List<ScPermission>> getAllMenuList(){

        List<ScPermission> list = new ArrayList<>();
        // 读取缓存
        ScUser u = securityUtil.getCurrUser();
        /*String key = "permission::userMenuList:" + u.getId();
        String v = redisTemplate.opsForValue().get(key);
        if(StrUtil.isNotBlank(v)){
            list = new Gson().fromJson(v, new TypeToken<List<ScPermission>>(){}.getType());
            if(CollectionUtils.isNotEmpty(list)){
                return new ResultUtil<List<ScPermission>>().setData(list);
            }

        }*/

        // 用户所有权限 已排序去重
        list = permissionService.findByUserId(u.getId());

        List<ScPermission> menuList = new ArrayList<>();
        // 筛选一级页面
        for(ScPermission p : list){
            if(CommonConstant.PERMISSION_PAGE.equals(p.getPType())&&CommonConstant.LEVEL_ONE.equals(p.getPLevel())){
                menuList.add(p);
            }
        }
        // 筛选二级页面
        List<ScPermission> secondMenuList = new ArrayList<>();
        for(ScPermission p : list){
            if(CommonConstant.PERMISSION_PAGE.equals(p.getPType())&&CommonConstant.LEVEL_TWO.equals(p.getPLevel())){
                secondMenuList.add(p);
            }
        }
        // 筛选三级页面
        List<ScPermission> thirdMenuList = new ArrayList<>();
        for(ScPermission p : list){
            if(CommonConstant.PERMISSION_PAGE.equals(p.getPType())&&CommonConstant.LEVEL_THREE.equals(p.getPLevel())){
                thirdMenuList.add(p);
            }
        }
        // 筛选二级页面拥有的按钮权限
        /*List<ScPermission> buttonPermissions = new ArrayList<>();
        for(ScPermission p : list){
            if(CommonConstant.PERMISSION_OPERATION.equals(p.getType())){
                buttonPermissions.add(p);
            }
        }*/

        // 匹配二级页面拥有权限
        /*for(ScPermission p : secondMenuList){
            List<String> permTypes = new ArrayList<>();
            for(ScPermission pe : buttonPermissions){
                if(p.getId().equals(pe.getParentId())){
                    permTypes.add(pe.getButtonType());
                }
            }
            p.setPermTypes(permTypes);
        }*/
        // 匹配一级页面拥有二级页面
        for(ScPermission p : menuList){
            List<ScPermission> secondMenu = new ArrayList<>();
            for(ScPermission pe : secondMenuList){
                if(p.getId().equals(pe.getParentId())){
                    List<ScPermission> thirdMenu = new ArrayList<>();
                    for(ScPermission pt : thirdMenuList){
                        if(pe.getId().equals(pt.getParentId())){
                            thirdMenu.add(pt);
                        }
                    }
                    pe.setChildren(thirdMenu);
                    secondMenu.add(pe);
                }
            }
            p.setChildren(secondMenu);
        }

        // 缓存
        //redisTemplate.opsForValue().set(key, new Gson().toJson(menuList));
        return new ResultUtil<List<ScPermission>>().setData(menuList);
    }


    @RequestMapping(value = "/getAllList",method = RequestMethod.GET)
    public Result<List<ScPermission>> getAllList(){


        // 一级
        List<ScPermission> list = permissionService.findByLevelOrderBySortOrder(CommonConstant.LEVEL_ONE);
        // 二级
        for(ScPermission p1 : list){
            List<ScPermission> children1 = permissionService.findByParentIdOrderBySortOrder(p1.getId());
            p1.setChildren(children1);
            // 三级
            for(ScPermission p2 : children1){
                List<ScPermission> children2 = permissionService.findByParentIdOrderBySortOrder(p2.getId());
                p2.setChildren(children2);
                //四级
                for(ScPermission p3 : children2){
                    List<ScPermission> children3 = permissionService.findByParentIdOrderBySortOrder(p3.getId());
                    p3.setChildren(children3);
                }
            }
        }
        Result<List<ScPermission>> res =  new ResultUtil<List<ScPermission>>().setData(list);
        return res;
    }

    @RequestMapping(value = "/tree",method = RequestMethod.GET)
    public Result<List<ScPermission>> getByTree(@RequestParam(required = false)Long parentId){

        List<ScPermission> list;

        if(parentId != null){
            list = permissionService.findByLevelOrderBySortOrder(CommonConstant.LEVEL_ONE);
        }else{
            list = permissionService.findByParentIdOrderBySortOrder(parentId);
        }
        return new ResultUtil<List<ScPermission>>().setData(list);
    }

    @RequestMapping(value = "/add",method = RequestMethod.POST)
    /*@CacheEvict(key = "'menuList'")*/
    public Result<ScPermission> add(@ModelAttribute(ValidatorConstant.PERMISSION_BASE_NAME) @Validated ScPermission permission){

        // 判断拦截请求的操作权限按钮名是否已存在
        if(CommonConstant.PERMISSION_OPERATION.equals(permission.getPType())){
            List<ScPermission> list = permissionService.list(new QueryWrapper<ScPermission>().lambda()
                    .eq(ScPermission::getTitle,permission.getTitle())
                    .orderByAsc(ScPermission::getSortOrder));
            if(list!=null&&list.size()>0){
                return new ResultUtil<ScPermission>().setErrorMsg("名称已存在");
            }
        }
        permissionService.save(permission);
        //重新加载权限
        mySecurityMetadataSource.loadResourceDefine();
        //手动删除缓存
        Set<String> redisLeys = redisTemplate.keys("permission:*");
        redisTemplate.delete(redisLeys);
        return new ResultUtil<ScPermission>().setData(permission);
    }

    @RequestMapping(value = "/edit",method = RequestMethod.POST)
    public Result<ScPermission> edit(@ModelAttribute(ValidatorConstant.PERMISSION_BASE_NAME) @Validated ScPermission permission){

        // 判断拦截请求的操作权限按钮名是否已存在
        if(CommonConstant.PERMISSION_OPERATION.equals(permission.getPType())){
            // 若名称修改
            ScPermission p = permissionService.getById(permission.getId());
            if(!p.getTitle().equals(permission.getTitle())){
                List<ScPermission> list = permissionService.list(new QueryWrapper<ScPermission>().lambda()
                        .eq(ScPermission::getTitle,permission.getTitle())
                        .orderByAsc(ScPermission::getSortOrder));
                if(list!=null&&list.size()>0){
                    return new ResultUtil<ScPermission>().setErrorMsg("名称已存在");
                }
            }
        }
        permissionService.updateById(permission);
        //重新加载权限
        mySecurityMetadataSource.loadResourceDefine();
        //手动批量删除缓存
        Set<String> keys = redisTemplate.keys("userPermission:" + "*");
        redisTemplate.delete(keys);
        Set<String> keysUser = redisTemplate.keys("user:" + "*");
        redisTemplate.delete(keysUser);
        Set<String> keysUserMenu = redisTemplate.keys("permission::userMenuList:*");
        redisTemplate.delete(keysUserMenu);
        redisTemplate.delete("permission::allList");
        return new ResultUtil<ScPermission>().setData(permission);
    }

    @RequestMapping(value = "/delByIds/{ids}",method = RequestMethod.DELETE)
    /*@CacheEvict(key = "'menuList'")*/
    public Result<Object> delByIds(@PathVariable String[] ids){

        for(String id:ids){

            ScPermission p = permissionService.getById(id);

            //禁用权限无需判断角色
            if(STATUS_DISABLE.equals(p.getPStatus())){
                continue;
            }

            List<ScRolePermission> list = rolePermissionService.list(new QueryWrapper<ScRolePermission>().lambda()
                    .eq(ScRolePermission::getPermissionId, id));
            if(list!=null&&list.size()>0){
                return new ResultUtil<Object>().setErrorMsg("删除失败，包含正被角色使用关联的菜单或权限");
            }

            List<ScPermission> permissions = permissionService.list(new QueryWrapper<ScPermission>().lambda().eq(ScPermission::getParentId, p.getId()));
            if(CollectionUtils.isNotEmpty(permissions)){
                return new ResultUtil<Object>().setErrorMsg("删除失败，请先删除"+p.getTitle()+"的子权限");
            }
        }

        Set<Long> delSet = new HashSet<>();

        for(String id:ids){
            if(!delSet.contains(id)){
                delPermissionById(Long.parseLong(id), delSet);
            }
        }

        //重新加载权限
        mySecurityMetadataSource.loadResourceDefine();
        //手动删除缓存
        Set<String> redisLeys = redisTemplate.keys("permission:*");
        redisTemplate.delete(redisLeys);
        return new ResultUtil<Object>().setSuccessMsg("批量通过id删除数据成功");
    }

    private void delPermissionById(Long id, Set delSet){
        List<ScPermission> childPermissionList = permissionService.findByParentIdOrderBySortOrder(id);
        if(CollectionUtils.isNotEmpty(childPermissionList)){
            for(ScPermission p:childPermissionList){
                delPermissionById(p.getId(), delSet);
            }
        }
        delSet.add(id);
        permissionService.removeById(id);
        rolePermissionService.remove(new QueryWrapper<ScRolePermission>().lambda().eq(ScRolePermission::getPermissionId,id));
    }

    @RequestMapping(value = "/search",method = RequestMethod.GET)
    public Result<List<ScPermission>> searchPermissionList(@RequestParam String title){

        List<ScPermission> list = permissionService.list(new QueryWrapper<ScPermission>().lambda()
                .like(ScPermission::getTitle, "%"+title+"%").orderByAsc(ScPermission::getSortOrder));
        return new ResultUtil<List<ScPermission>>().setData(list);
    }

    @RequestMapping(value = "/isNeedVerify" , method = RequestMethod.GET)
    public Result<Integer> isNeedVerify(@RequestParam String path){

        DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(myFilterSecurityInterceptor);
        URL url;
        try {
            url = new URL(path);
        } catch (MalformedURLException e) {
            return new ResultUtil<Integer>().setErrorMsg(e.getMessage());
        }

        SecurityContextHolder.getContext();

        boolean isAllowed = defaultWebInvocationPrivilegeEvaluator.isAllowed(url.getPath(),SecurityContextHolder.getContext().getAuthentication());
        if(!isAllowed){
            return new ResultUtil<Integer>().setData(1, "用户无调用权限");
        }

        boolean needVerify = mySecurityMetadataSource.needVerify(url.getPath());

//        if(needVerify){
//            return new ResultUtil<Integer>().setData(2, "需要复核");
//        }

        return new ResultUtil<Integer>().setData(3, "无需复核");
    }

}
