package com.codvision.auth.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.codvision.auth.entity.Org;
import com.codvision.auth.entity.OrgProperty;
import com.codvision.auth.entity.dto.SysOrgQueryDTO;
import com.codvision.auth.entity.excel.OrgExcel;
import com.codvision.auth.entity.vo.OrgVO;
import com.codvision.auth.mapper.OrgMapper;
import com.codvision.auth.mapper.SysUserMapper;
import com.codvision.auth.service.IOrgPropertyService;
import com.codvision.auth.service.IOrgService;
import com.codvision.base.config.excel.AbstractExportClass;
import com.codvision.base.config.excel.ExcelDataReadListener;
import com.codvision.base.entity.ExcelCheck;
import com.codvision.base.entity.ExcelErrorRecord;
import com.codvision.base.enums.DelFlag;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.service.impl.BaseServiceImpl;
import com.codvision.webcore.util.PageUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.codvision.base.support.BaseConst.SLASH_SYMBOL;

/**
 * <p>
 * 组织信息 服务实现类
 * </p>
 *
 * @author lingee
 * @since 2023-11-10
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class OrgServiceImpl extends BaseServiceImpl<OrgMapper, Org, OrgVO> implements IOrgService {

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private IOrgPropertyService orgPropertyService;

    @Override
    public OrgVO getDetail(Integer oid) {
        return getBaseMapper().getByOid(oid);
    }

    @Override
    public Org getDefaultOrg() {
        return this.getOne(Wrappers.<Org>lambdaQuery().orderByAsc(Org::getOid).last("limit 1"));
    }

    @Override
    public IPage<OrgVO> findAll(QueryPage<Org> queryPage, SysOrgQueryDTO queryDTO) {
        IPage<Org> page = queryPage.getPage();
        List<OrgVO> voList = this.getBaseMapper().findAll(page, queryDTO);
        return PageUtil.convertPage(page, voList);
    }

    @Override
    public OrgVO addOrg(Org org) {
        isExist(org);
        if (org.getType() == 2 && org.getLevel() > 1) {
            org.setDistrictOid(getDistrictOid(org.getPoid()));
        }
        this.save(org);
        return getDetail(org.getOid());
    }

    @Override
    public OrgVO updateOrg(Org org) {
        Org existOrg = getDetail(org.getOid());
        Preconditions.checkArgument(ObjectUtil.isNotEmpty(existOrg), 30001);

        if (!Objects.equals(org.getPoid(), existOrg.getPoid())) {
            isExist(org);
        } else if (!Objects.equals(org.getName(), existOrg.getName())) {
            Org checkOrg = getBaseMapper().getByNameAndPoid(org.getName(), org.getPoid());
            Preconditions.checkArgument(checkOrg == null, "组织已存在");
        }

        this.updateById(org);
        return getDetail(org.getOid());
    }

    @Override
    public void logicDel(Integer oid) {
        Org org = this.getById(oid);
        Preconditions.checkArgument(org != null, 30001);

        // 判断当前组织是否已有账号在使用
        int count = sysUserMapper.checkUserOrg(oid);
        Preconditions.checkArgument(count <= 0, "已有账号正在使用该组织，暂无法删除");

        this.update(Wrappers.<Org>lambdaUpdate().setSql("del_flag = 1, del_key = oid")
                .eq(Org::getDelFlag, DelFlag.NO_DELETED.getFlag()).like(Org::getOidPath, "/" + oid + "/"));
    }

    @Override
    public OrgVO getTree(Integer root) {
        SysOrgQueryDTO queryDTO = new SysOrgQueryDTO();
        if (root != null) {
            queryDTO.setRoot(String.valueOf(root));
            queryDTO.setSubQuery(true);
        }

        List<OrgVO> orgList = this.getBaseMapper().findAll(null, queryDTO);
        if (CollectionUtils.isEmpty(orgList)) {
            return new OrgVO();
        }
        if (root == null) {
            return orgList.stream().filter(m -> m.getPoid() == null)
                    .peek(m -> m.getChildren().addAll(sort(getChildren(m, orgList)))).iterator().next();
        }

        return orgList.stream().filter(m -> root.equals(m.getOid()))
                .peek(m -> m.getChildren().addAll(sort(getChildren(m, orgList)))).iterator().next();
    }

    @Override
    @SneakyThrows
    public String excelImport(MultipartFile file) {
        Map<String, Object> paraMap = new HashMap<>(16);
        paraMap.put("isIgnoreWkt", true);

        SysOrgQueryDTO queryDTO = new SysOrgQueryDTO();
        queryDTO.setIsIgnoreWkt(true);
        List<OrgVO> orgList = getBaseMapper().findAll(null, queryDTO);
        List<OrgProperty> orgPropertyList = orgPropertyService.list();

        String reqNo = IdUtil.fastSimpleUUID();
        IOrgService service = SpringUtil.getBean(IOrgService.class);
        ExcelDataReadListener<OrgExcel> listener = new ExcelDataReadListener<OrgExcel>(cachedDataList -> service.excelSave(cachedDataList, orgList, orgPropertyList)) {
            @Override
            protected void validator(OrgExcel orgExcel, ExcelCheck<OrgExcel> check, AnalysisContext context) {
                checkOne(reqNo, orgExcel, check, context.readRowHolder().getRowIndex(), orgList, orgPropertyList);
            }
        };
        EasyExcel.read(file.getInputStream(), OrgExcel.class, listener).sheet().doRead();
        if (listener.isHeadAbnormal()) {
            throw new BusinessException("文件格式错误，请检查导入模板");
        }

        return listener.existErrorData() ? reqNo : null;
    }

    @Override
    public void excelSave(List<OrgExcel> cachedDataList, List<OrgVO> orgList, List<OrgProperty> orgPropertyList) {
        if (CollectionUtils.isNotEmpty(cachedDataList)) {
            List<Org> orgs = new ArrayList<>();
            for (OrgExcel orgExcel : cachedDataList) {
                Org org = new Org();
                org.setType("行政区划".equals(orgExcel.getType()) ? 1 : 2);
                org.setName(orgExcel.getName());
                org.setDistrictCode(orgExcel.getDistrictCode());
                Optional<OrgProperty> propertyOptional = orgPropertyList.stream().filter(orgProperty -> orgExcel.getOrgPropertyName().equals(orgProperty.getName())).findFirst();
                propertyOptional.ifPresent(orgProperty -> org.setOrgPropertyId(orgProperty.getId()));
                Optional<OrgVO> optional = orgList.stream().filter(o -> orgExcel.getParentOrgName().equals(o.getPath())).findFirst();
                optional.ifPresent(o -> org.setPoid(o.getOid()));
                Optional<OrgVO> isExist = orgList.stream().filter(o -> o.getName().equals(orgExcel.getName()) && o.getPoid().equals(org.getPoid())).findFirst();
                if (!isExist.isPresent()) {
                    orgs.add(org);
                }
            }
            if (CollectionUtil.isNotEmpty(orgs)) {
                this.saveBatch(orgs);
            }
        }
    }

    @Override
    public void excelExport(SysOrgQueryDTO queryBean) {
        new AbstractExportClass<OrgExcel, Org>() {
            @Override
            protected List<OrgExcel> getList(QueryPage<Org> queryPage, Object queryBean) {
                List<OrgExcel> list = new ArrayList<>();
                List<OrgVO> orgList = getBaseMapper().findAll(queryPage.getPage(), (SysOrgQueryDTO) queryBean);
                List<OrgProperty> orgPropertyList = orgPropertyService.list();
                if (CollectionUtils.isNotEmpty(orgList)) {
                    for (Org org : orgList) {
                        OrgExcel orgExcel = new OrgExcel();
                        orgExcel.setName(org.getName());
                        Optional<OrgProperty> propertyOptional = orgPropertyList.stream().filter(orgProperty -> org.getOrgPropertyId().equals(orgProperty.getId())).findFirst();
                        propertyOptional.ifPresent(orgProperty -> orgExcel.setOrgPropertyName(orgProperty.getName()));
                        orgExcel.setType(org.getType().equals(1) ? "行政区划" : "部门");
                        orgExcel.setDistrictCode(org.getDistrictCode());
                        if (org.getPoid() != null) {
                            Org parentOrg = getBaseMapper().selectById(org.getPoid());
                            if (parentOrg != null) {
                                orgExcel.setParentOrgName(parentOrg.getPath());
                            }
                        }
                        list.add(orgExcel);
                    }
                }
                return list;
            }
        }.excelUser("组织信息表", queryBean, OrgExcel.class);
    }

    @Override
    public List<OrgVO> findAll(SysOrgQueryDTO queryDTO) {
        return this.getBaseMapper().findAll(null, queryDTO);
    }

    /**
     * 递归获取上级行政区划类型组织oid
     *
     * @param oid 组织id
     * @return Integer
     */
    private Integer getDistrictOid(Integer oid) {
        if (oid == null) {
            return null;
        }
        OrgVO orgVO = getDetail(oid);
        if (orgVO.getType() == 1) {
            return orgVO.getOid();
        }
        return getDistrictOid(orgVO.getPoid());
    }

    /**
     * 判断组织是否存在
     *
     * @param o 组织信息
     */
    private void isExist(Org o) {
        OrgVO org;
        if (o.getPoid() == null) {
            org = getBaseMapper().getByNameWithOutPoid(o.getName());
        } else {
            org = getBaseMapper().getByNameAndPoid(o.getName(), o.getPoid());
        }

        if (org != null) {
            throw new BusinessException("组织已存在");
        }
    }

    /**
     * 获取子组织列表
     *
     * @param parentOrg 父组织
     * @param orgList   组织列表
     * @return 子组织列表
     */
    private List<OrgVO> getChildren(OrgVO parentOrg, List<OrgVO> orgList) {
        return orgList.stream()
                .filter(cur -> cur.getPoid() != null && Objects.equals(cur.getPoid(), parentOrg.getOid()))
                .peek(m -> m.getChildren().addAll(sort(getChildren(m, orgList)))).collect(Collectors.toList());
    }

    /**
     * 组织列表排序
     *
     * @param orgList 组织列表
     * @return 排序后的组织列表
     */
    private List<OrgVO> sort(List<OrgVO> orgList) {
        if (CollectionUtils.isEmpty(orgList)) {
            return Lists.newArrayList();
        }

        orgList.sort(Comparator.comparing(OrgVO::getOrderby));
        return orgList;
    }


    private void checkOne(String reqNo,
                          OrgExcel orgExcel,
                          ExcelCheck<OrgExcel> check,
                          Integer rowIndex,
                          List<OrgVO> orgList,
                          List<OrgProperty> orgPropertyList) {
        Map<String, Object> fieldsErrorMap = new HashMap<>(16);
        if (StringUtils.isBlank(orgExcel.getName())) {
            fieldsErrorMap.put("name", "组织名称必填");
        }
        if (StringUtils.isBlank(orgExcel.getType())) {
            fieldsErrorMap.put("type", "组织类型必填");
        }

        String parentOrgName = orgExcel.getParentOrgName();
        if (StringUtils.isBlank(parentOrgName)) {
            fieldsErrorMap.put("parentOrgName", "父级组织路径必填");
        } else {
            if (!(parentOrgName.startsWith(SLASH_SYMBOL) && parentOrgName.endsWith(SLASH_SYMBOL))) {
                fieldsErrorMap.put("parentOrgName", "格式错误");
            } else {
                Optional<OrgVO> optional = orgList.stream().filter(o -> orgExcel.getParentOrgName().equals(o.getPath())).findFirst();

                if (!optional.isPresent()) {
                    fieldsErrorMap.put("parentOrgName", "无法获取到父级组织的信息");
                }
            }
        }

        String orgPropertyName = orgExcel.getOrgPropertyName();
        if (StringUtils.isBlank(orgPropertyName)) {
            fieldsErrorMap.put("orgPropertyName", "组织属性必填");
        } else {
            Optional<OrgProperty> optional = orgPropertyList.stream().filter(orgProperty -> orgExcel.getOrgPropertyName().equals(orgProperty.getName())).findFirst();
            if (!optional.isPresent()) {
                fieldsErrorMap.put("orgPropertyName", "无法获取到组织属性的信息");
            } else {
                OrgProperty property = new OrgProperty();
                optional.ifPresent(orgProperty -> property.setCodeLength(orgProperty.getCodeLength()));
                if (property.getCodeLength() != null && orgExcel.getDistrictCode().length() != property.getCodeLength()) {
                    fieldsErrorMap.put("districtCode", "行政区划编码长度不正确");
                }
            }
        }

        if (fieldsErrorMap.isEmpty()) {
            check.getCacheList().add(orgExcel);
        } else {
            ExcelErrorRecord record = new ExcelErrorRecord();
            record.setReqNo(reqNo);
            record.setRowIndex(rowIndex);
            record.setOriginalJsonValue(JSONUtil.toJsonStr(orgExcel));
            record.setErrorMsgJson(JSONUtil.toJsonStr(fieldsErrorMap));
            record.setTargetObject(orgExcel.getClass().getName());
            check.getErrorList().add(record);
        }
    }
}
