package pwc.taxtech.atms.service.impl;

import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import pwc.taxtech.atms.common.CommonConstants;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.dto.navtree.DevTreeDto;
import pwc.taxtech.atms.dto.navtree.RolePermissionDisplayDto;
import pwc.taxtech.atms.dto.permission.RolePermissionDto;
import pwc.taxtech.atms.entity.Permission;
import pwc.taxtech.atms.entity.PermissionExample;
import pwc.taxtech.atms.entity.RolePermission;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class PermissionServiceImpl extends AbstractService {

    /* (non-Javadoc)
     * @see pwc.taxtech.atms.service.PermissionService#getIvhTreePermissionsByRoleId(java.lang.String, java.lang.String)
     */
    public RolePermissionDisplayDto getIvhTreePermissionsByRoleId(String roleId, String serviceType) {

        RolePermissionDisplayDto rolePermissionDisplayDto = new RolePermissionDisplayDto();

        List<RolePermission> rolePermissionList = rolePermissionMapper.selectByRoleAndServiceTypeWithAssociation(roleId, serviceType);
        if (rolePermissionList == null || rolePermissionList.isEmpty()) {
            return rolePermissionDisplayDto;
        }
        List<RolePermissionDto> rolePermissionDtoList = new ArrayList<>();
        rolePermissionList.stream().forEach(rolePermission -> {
            RolePermissionDto rolePermissionDto = new RolePermissionDto();
            rolePermissionDto.setId(rolePermission.getId());
            rolePermissionDto.setCode(rolePermission.getPermission().getCode());
            rolePermissionDto.setRoleId(rolePermission.getRoleId());
            rolePermissionDto.setPermissionId(rolePermission.getPermissionId());
            rolePermissionDto.setRoleName(rolePermission.getRole().getName());
            rolePermissionDto.setPermissionName(rolePermission.getPermission().getName());
            rolePermissionDto.setServiceType(rolePermission.getPermission().getServiceTypeId());
            rolePermissionDto.setParentId(rolePermission.getPermission().getParentId());
            rolePermissionDto.setPLevel(rolePermission.getPermission().getpLevel());

            rolePermissionDtoList.add(rolePermissionDto);
        });
        rolePermissionDisplayDto.setRolePermissionList(rolePermissionDtoList);
        if (rolePermissionDtoList.isEmpty()) {
            return rolePermissionDisplayDto;
        }

        rolePermissionDisplayDto.setPermissionDevTreeList(getRolePermissionIvhTree(rolePermissionDtoList));
        return rolePermissionDisplayDto;
    }


    /* (non-Javadoc)
     * @see pwc.taxtech.atms.service.PermissionService#getIvhTreePermissionsByRoleIdList(java.util.List, java.lang.String)
     */
    @SuppressWarnings("rawtypes")
    public List<DevTreeDto> getIvhTreePermissionsByRoleIdList(List<String> roleList, String serviceType) {

        if (roleList == null || roleList.isEmpty()) {
            return new ArrayList<DevTreeDto>();
        }
        List<RolePermission> rolePermissionList = rolePermissionMapper.selectByRoleListAndServiceTypeWithAssociation(roleList, serviceType);
        if (rolePermissionList == null || rolePermissionList.isEmpty()) {
            return new ArrayList<DevTreeDto>();
        }
        List<RolePermissionDto> rolePermissionDtoListTemp = new ArrayList<>();
        rolePermissionList.stream().forEach(rolePermission -> {
            RolePermissionDto rolePermissionDto = new RolePermissionDto();
            rolePermissionDto.setPermissionId(rolePermission.getPermissionId());
            rolePermissionDto.setCode(rolePermission.getPermission().getCode());
            rolePermissionDto.setPermissionName(rolePermission.getPermission().getName());
            rolePermissionDto.setServiceType(rolePermission.getPermission().getServiceTypeId());
            rolePermissionDto.setParentId(rolePermission.getPermission().getParentId());

            rolePermissionDtoListTemp.add(rolePermissionDto);
        });
        List<RolePermissionDto> rolePermissionDtoList = rolePermissionDtoListTemp.stream().distinct().collect(Collectors.toList());

        return getRolePermissionIvhTree(rolePermissionDtoList);
    }

    /* (non-Javadoc)
     * @see pwc.taxtech.atms.service.PermissionService#getAllPermissions(java.lang.String)
     */
    @SuppressWarnings("rawtypes")
    public List<DevTreeDto> getAllPermissions(String serviceType) {

        PermissionExample permissionExample = new PermissionExample();
        permissionExample.createCriteria()
                .andIsActiveEqualTo(CommonConstants.ACTIVE_STATUS)
                .andServiceTypeIdEqualTo(serviceType);
        permissionExample.setOrderByClause("code ASC");
        List<Permission> allPermissions = permissionMapper.selectByExample(permissionExample);

        List<Permission> topList = allPermissions.stream()
                .filter(permission -> StringUtils.isEmpty(permission.getParentId()))
                .collect(Collectors.toList());
        List<DevTreeDto> permissionDevTreeList = new ArrayList<>();
        for (Permission topPermission : topList) {
            DevTreeDto devTreeNode = new DevTreeDto<>();
            devTreeNode.setId(topPermission.getId());
            devTreeNode.setText(topPermission.getName());
            devTreeNode.setCode(topPermission.getCode());
            devTreeNode.setParentId(null);
            setSubPermissionListRecursive(devTreeNode, allPermissions);
            permissionDevTreeList.add(devTreeNode);
        }
        return permissionDevTreeList;
    }

    @SuppressWarnings("rawtypes")
    private List<DevTreeDto> getRolePermissionIvhTree(List<RolePermissionDto> rolePermissionDtoList) {

        //copy 
        List<RolePermissionDto> outputRolePermissionDtoList = new ArrayList<>();
        outputRolePermissionDtoList.addAll(rolePermissionDtoList);

        //get all permission list
        List<Permission> allPermissionList = permissionMapper.selectByExample(null);
        if (outputRolePermissionDtoList.size() != allPermissionList.size()) {
            Collections.copy(outputRolePermissionDtoList, rolePermissionDtoList);
            for (RolePermissionDto rolePermissionDto : rolePermissionDtoList) {
                addParentPermissionToListRecursive(rolePermissionDto, allPermissionList, outputRolePermissionDtoList);
            }
        } else {
            outputRolePermissionDtoList = new ArrayList<>();
            for (Permission permission : allPermissionList) {
                RolePermissionDto rolePermissionDto = new RolePermissionDto();
                CommonUtils.copyProperties(permission, rolePermissionDto);
                rolePermissionDto.setId(null);
                rolePermissionDto.setPermissionId(permission.getId());
                rolePermissionDto.setPermissionName(permission.getName());
                rolePermissionDto.setServiceType(permission.getServiceTypeId());
                outputRolePermissionDtoList.add(rolePermissionDto);
            }
        }

        //根据resultList中情况生成ivhtree
        List<RolePermissionDto> topList = outputRolePermissionDtoList.stream()
                .filter(rPermissionDto -> StringUtils.isEmpty(rPermissionDto.getParentId()))
                .sorted(Comparator.comparing(RolePermissionDto::getCode))
                .collect(Collectors.toList());
        List<DevTreeDto> permissionDevTreeList = new ArrayList<>();
        for (RolePermissionDto topRolePermissionDto : topList) {
            DevTreeDto devTreeNode = new DevTreeDto<>();
            devTreeNode.setId(topRolePermissionDto.getPermissionId());
            devTreeNode.setText(topRolePermissionDto.getPermissionName());
            devTreeNode.setCode(topRolePermissionDto.getCode());
            devTreeNode.setParentId(null);
            setSubRolePermissionListRecursive(devTreeNode, outputRolePermissionDtoList);
            permissionDevTreeList.add(devTreeNode);
        }
        return permissionDevTreeList;
    }

    private void addParentPermissionToListRecursive(RolePermissionDto rolePermissionDto,
                                                    List<Permission> allPermissionList, List<RolePermissionDto> outputRolePermissionDtoList) {

        Optional<Permission> optional = allPermissionList.stream().
                filter(p -> p.getId().equals(rolePermissionDto.getParentId())).findFirst();
        if (optional.isPresent()) {
            Permission parentPermission = optional.get();
            RolePermissionDto parentRolePermissionDto = new RolePermissionDto();
            CommonUtils.copyProperties(parentPermission, parentRolePermissionDto);
            parentRolePermissionDto.setId(null);
            parentRolePermissionDto.setPermissionId(parentPermission.getId());
            parentRolePermissionDto.setPermissionName(parentPermission.getName());
            parentRolePermissionDto.setServiceType(parentPermission.getServiceTypeId());

            if (outputRolePermissionDtoList.stream()
                    .filter(rpDto -> rpDto.getPermissionId().equals(parentPermission.getId())).count() == 0) {
                outputRolePermissionDtoList.add(parentRolePermissionDto);
            }
            addParentPermissionToListRecursive(parentRolePermissionDto, allPermissionList, outputRolePermissionDtoList);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void setSubRolePermissionListRecursive(DevTreeDto currentNode, List<RolePermissionDto> allRolePermissionDtoList) {

        List<RolePermissionDto> subRolePermissionDtoList = allRolePermissionDtoList.stream()
                .filter(rPermissionDto -> currentNode.getId().equals(rPermissionDto.getParentId())).collect(Collectors.toList());
        if (subRolePermissionDtoList != null && !subRolePermissionDtoList.isEmpty()) {
            currentNode.setExpanded(true);
            currentNode.setItems(new ArrayList<>());
            for (RolePermissionDto rolePermissionDto : subRolePermissionDtoList) {
                DevTreeDto subNode = new DevTreeDto();
                subNode.setId(rolePermissionDto.getPermissionId());
                subNode.setText(rolePermissionDto.getPermissionName());
                subNode.setCode(rolePermissionDto.getCode());
                subNode.setParentId(rolePermissionDto.getParentId());
                currentNode.getItems().add(subNode);
                setSubRolePermissionListRecursive(subNode, allRolePermissionDtoList);
            }
        } else {
            currentNode.setExpanded(false);
            currentNode.setItems(new ArrayList<>());
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void setSubPermissionListRecursive(DevTreeDto currentNode, List<Permission> allPermissionList) {

        List<Permission> subPermissionList = allPermissionList.stream()
                .filter(permission -> currentNode.getId().equals(permission.getParentId())).collect(Collectors.toList());
        if (subPermissionList != null && !subPermissionList.isEmpty()) {
            currentNode.setExpanded(true);
            currentNode.setItems(new ArrayList<>());
            for (Permission permission : subPermissionList) {
                DevTreeDto subNode = new DevTreeDto();
                subNode.setId(permission.getId());
                subNode.setText(permission.getName());
                subNode.setCode(permission.getCode());
                subNode.setParentId(permission.getParentId());
                subNode.setRelyOnCodes(permission.getRelyOnCodes());
                currentNode.getItems().add(subNode);
                setSubPermissionListRecursive(subNode, allPermissionList);
            }
        } else {
            currentNode.setExpanded(false);
            currentNode.setItems(new ArrayList<>());
        }
    }
}