package cn.stylefeng.workflow.instance.modular.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.roses.kernel.rule.pojo.dict.SimpleDict;
import cn.stylefeng.roses.kernel.sys.api.OrganizationServiceApi;
import cn.stylefeng.roses.kernel.sys.api.SysRoleServiceApi;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.roses.kernel.sys.api.pojo.org.HrOrganizationDTO;
import cn.stylefeng.roses.kernel.sys.api.pojo.role.SysRoleDTO;
import cn.stylefeng.workflow.api.entity.design.Model;
import cn.stylefeng.workflow.api.enums.FlowAuthLinkTypeEnum;
import cn.stylefeng.workflow.design.api.ModelApi;
import cn.stylefeng.workflow.instance.modular.auth.entity.FlowAuth;
import cn.stylefeng.workflow.instance.modular.auth.enums.FlowAuthExceptionEnum;
import cn.stylefeng.workflow.instance.modular.auth.mapper.FlowAuthMapper;
import cn.stylefeng.workflow.instance.modular.auth.pojo.request.FlowAuthRequest;
import cn.stylefeng.workflow.instance.modular.auth.service.FlowAuthService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class FlowAuthServiceImpl extends ServiceImpl<FlowAuthMapper, FlowAuth> implements FlowAuthService {

    @Resource
    private ModelApi modelApi;

    @Resource
    private OrganizationServiceApi organizationServiceApi;

    @Resource
    private SysUserServiceApi userServiceApi;

    @Resource
    private SysRoleServiceApi sysRoleServiceApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(FlowAuthRequest flowAuthRequest) {

        LambdaQueryWrapper<FlowAuth> flowAuthLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowAuthLambdaQueryWrapper.eq(FlowAuth::getFlowModelId, flowAuthRequest.getFlowModelId());
        flowAuthLambdaQueryWrapper.select(FlowAuth::getBusinessId);
        List<FlowAuth> alreadyBindList = this.list(flowAuthLambdaQueryWrapper);
        Set<Long> alreadyBindBusinessIdList = alreadyBindList.stream().map(FlowAuth::getBusinessId).collect(Collectors.toSet());

        List<Long> businessIdList = flowAuthRequest.getBusinessIdList();
        if (ObjectUtil.isNotEmpty(alreadyBindBusinessIdList)) {
            businessIdList.removeAll(alreadyBindBusinessIdList);
        }

        if (ObjectUtil.isNotEmpty(businessIdList)) {
            ArrayList<FlowAuth> flowAuths = new ArrayList<>();
            for (Long businessId : businessIdList) {
                FlowAuth flowAuth = new FlowAuth();
                flowAuth.setFlowModelId(flowAuthRequest.getFlowModelId());
                flowAuth.setLinkType(flowAuthRequest.getLinkType());
                flowAuth.setBusinessId(businessId);
                flowAuths.add(flowAuth);
            }
            if (ObjectUtil.isNotEmpty(flowAuths)) {
                this.saveBatch(flowAuths);
            }
        }
    }

    @Override
    public void del(FlowAuthRequest flowAuthRequest) {
        FlowAuth flowAuth = this.queryFlowAuth(flowAuthRequest);
        this.removeById(flowAuth.getFlowAuthId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(FlowAuthRequest flowAuthRequest) {
        this.removeByIds(flowAuthRequest.getBatchDeleteIdList());
    }

    @Override
    public void edit(FlowAuthRequest flowAuthRequest) {
        FlowAuth flowAuth = this.queryFlowAuth(flowAuthRequest);
        BeanUtil.copyProperties(flowAuthRequest, flowAuth);
        this.updateById(flowAuth);
    }

    @Override
    public FlowAuth detail(FlowAuthRequest flowAuthRequest) {
        return this.queryFlowAuth(flowAuthRequest);
    }

    @Override
    public PageResult<FlowAuth> findPage(FlowAuthRequest flowAuthRequest) {

        if (ObjectUtil.isEmpty(flowAuthRequest.getFlowModelId())) {
            return PageResultFactory.createPageResult(new Page<>());
        }

        LambdaQueryWrapper<FlowAuth> wrapper = createWrapper(flowAuthRequest);
        Page<FlowAuth> pageList = this.page(PageFactory.defaultPage(), wrapper);

        List<FlowAuth> records = pageList.getRecords();
        this.fillBusinessName(records);

        return PageResultFactory.createPageResult(pageList);
    }

    @Override
    public List<FlowAuth> findList(FlowAuthRequest flowAuthRequest) {
        LambdaQueryWrapper<FlowAuth> wrapper = this.createWrapper(flowAuthRequest);
        return this.list(wrapper);
    }

    @Override
    public Set<Long> getUserTotalFlowModelIdList(Collection<Long> userComprehensiveIdentitySummary) {
        LambdaQueryWrapper<FlowAuth> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(FlowAuth::getBusinessId, userComprehensiveIdentitySummary);
        queryWrapper.select(FlowAuth::getFlowModelId);
        List<FlowAuth> flowAuthList = this.list(queryWrapper);
        if (ObjectUtil.isEmpty(flowAuthList)) {
            return new HashSet<>();
        }

        return flowAuthList.stream().map(FlowAuth::getFlowModelId).collect(Collectors.toSet());
    }

    private FlowAuth queryFlowAuth(FlowAuthRequest flowAuthRequest) {
        FlowAuth flowAuth = this.getById(flowAuthRequest.getFlowAuthId());
        if (ObjectUtil.isEmpty(flowAuth)) {
            throw new ServiceException(FlowAuthExceptionEnum.FLOW_AUTH_NOT_EXISTED);
        }
        return flowAuth;
    }

    private LambdaQueryWrapper<FlowAuth> createWrapper(FlowAuthRequest flowAuthRequest) {
        LambdaQueryWrapper<FlowAuth> queryWrapper = new LambdaQueryWrapper<>();

        Long flowModelId = flowAuthRequest.getFlowModelId();
        Integer linkType = flowAuthRequest.getLinkType();

        queryWrapper.eq(ObjectUtil.isNotNull(flowModelId), FlowAuth::getFlowModelId, flowModelId);
        if (ObjectUtil.isNotEmpty(linkType) && !FlowAuthLinkTypeEnum.ALL.getType().equals(linkType)) {
            queryWrapper.eq(FlowAuth::getLinkType, linkType);
        }

        return queryWrapper;
    }

    private void fillBusinessName(List<FlowAuth> records) {

        if (ObjectUtil.isEmpty(records)) {
            return;
        }

        List<Long> flowModelIdList = records.stream().map(FlowAuth::getFlowModelId).collect(Collectors.toList());

        List<Model> modelList = modelApi.getModelList(flowModelIdList);
        for (FlowAuth record : records) {
            Long flowModelId = record.getFlowModelId();
            for (Model model : modelList) {
                if (flowModelId.equals(model.getModelId())) {
                    record.setFlowModelName(model.getModelName());
                    break;
                }
            }
        }

        List<Long> orgIdList = records.stream().filter(i -> i.getLinkType().equals(FlowAuthLinkTypeEnum.ORG.getType()))
                .map(FlowAuth::getBusinessId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(orgIdList)) {
            List<HrOrganizationDTO> orgNameList = organizationServiceApi.getOrgNameList(orgIdList);
            for (FlowAuth record : records) {
                Long businessId = record.getBusinessId();
                for (HrOrganizationDTO hrOrganizationDTO : orgNameList) {
                    if (businessId.equals(hrOrganizationDTO.getOrgId())) {
                        record.setBusinessName(hrOrganizationDTO.getOrgName());
                        break;
                    }
                }
            }
        }

        Set<Long> userIdList = records.stream().filter(i -> i.getLinkType().equals(FlowAuthLinkTypeEnum.USER.getType()))
                .map(FlowAuth::getBusinessId).collect(Collectors.toSet());
        if (ObjectUtil.isNotEmpty(userIdList)) {
            List<SimpleDict> userNameList = userServiceApi.batchGetNameByUserIdList(userIdList);
            for (FlowAuth record : records) {
                Long businessId = record.getBusinessId();
                for (SimpleDict simpleDict : userNameList) {
                    if (businessId.equals(simpleDict.getId())) {
                        record.setBusinessName(simpleDict.getName());
                        break;
                    }
                }
            }
        }

        List<Long> roleIdList = records.stream().filter(i -> i.getLinkType().equals(FlowAuthLinkTypeEnum.ROLE.getType()))
                .map(FlowAuth::getBusinessId).collect(Collectors.toList());
        List<SysRoleDTO> roleNameList = sysRoleServiceApi.getRolesByIds(roleIdList);
        if (ObjectUtil.isNotEmpty(roleNameList)) {
            for (FlowAuth record : records) {
                Long businessId = record.getBusinessId();
                for (SysRoleDTO sysRoleDTO : roleNameList) {
                    if (businessId.equals(sysRoleDTO.getRoleId())) {
                        record.setBusinessName(sysRoleDTO.getRoleName());
                        break;
                    }
                }
            }
        }

    }
}
