package com.yiboshi.science.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.hash.Hashing;
import com.yiboshi.arch.exception.BusinessException;
import com.yiboshi.science.api.ApiHttpRequest;
import com.yiboshi.science.base.BaseServiceImpl;
import com.yiboshi.science.base.Pagination;
import com.yiboshi.science.config.bean.SystemProperties;
import com.yiboshi.science.config.security.SecurityUserHolder;
import com.yiboshi.science.dao.ComUnitDAO;
import com.yiboshi.science.entity.ComPerson;
import com.yiboshi.science.entity.ComUnit;
import com.yiboshi.science.enumeration.CommonEnum;
import com.yiboshi.science.param.dto.*;
import com.yiboshi.science.param.query.ComProjectAuditQueryVO;
import com.yiboshi.science.param.query.ComUnitQueryVO;
import com.yiboshi.science.service.*;
import com.yiboshi.science.utils.RedisKey;
import com.yiboshi.science.utils.RedisUtils;
import com.yiboshi.yac.model.OrgInfo;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 单位表 Service 实现类
 *
 * @author lkl
 * @version 2021-08-26
 */
@Service
@AllArgsConstructor
public class ComUnitServiceImpl extends BaseServiceImpl<ComUnitDAO, ComUnitQueryVO, ComUnitDTO, ComUnit> implements ComUnitService {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private ApiHttpRequest apiHttpRequest;

    private SystemProperties properties;

    @Autowired
    private ComUnitDAO comUnitDAO;

    @Autowired
    private ComSendingRecordService comSendingRecordService;

    @Autowired
    private SystemUserService systemUserService;

    @Autowired
    private ComManagerService comManagerService;

    @Autowired
    private ComPersonService comPersonService;

    @Override
    protected void setCriteriaForQuery(ComUnitQueryVO vo, QueryWrapper<ComUnitQueryVO> criteria) {
        if (Objects.nonNull(vo.getUpTreeCode())) {
            criteria.eq("SUBSTR(a.tree_code,1,LENGTH(a.tree_code)-5)", vo.getUpTreeCode());
        }
        if (Objects.nonNull(vo.getTreeCode())) {
            criteria.likeRight("a.tree_code", vo.getTreeCode());
        }
        if (Objects.nonNull(vo.getUnitName())) {
            criteria.like("a.unit_name", vo.getUnitName());
        }
        if (Objects.nonNull(vo.getUnitType())) {
            criteria.eq("a.unit_type", vo.getUnitType());
        }
        if (Objects.nonNull(vo.getUnitState())) {
            criteria.eq("a.unit_state", vo.getUnitState());
        }
        if (Objects.nonNull(vo.getUnitAddress())) {
            criteria.eq("a.unit_address", vo.getUnitAddress());
        }
        if (Objects.nonNull(vo.getRemark())) {
            criteria.eq("a.remark", vo.getRemark());
        }
        if (Objects.nonNull(vo.getAuditUnit())) {
            criteria.ne("a.id", vo.getAuditUnit());
        }
        if (Objects.nonNull(vo.getAuditResult())) {
            switch (vo.getAuditResult()) {
                case 0:
                    criteria.isNotNull("a.audit_result");
                    break;
                case 10:
                    criteria.and(qw -> qw.isNull("a.audit_result").or().eq("a.audit_result", vo.getAuditResult()));
                    break;
                default:
                    criteria.eq("a.audit_result", vo.getAuditResult());
                    break;
            }
        }
    }

    @Override
    public Pagination<ComUnitDTO> getListByPage(ComUnitQueryVO vo) {
        QueryWrapper criteria = new QueryWrapper();
        setCriteriaForQuery(vo, criteria);
        Page<ComUnitQueryVO> page = new Page<>(vo.getPageIndex(), vo.getPageSize());
        List<ComUnitDTO> dtoList = comUnitDAO.getListByPage(page, criteria).getRecords();
        return new Pagination<>(dtoList, page.getTotal(), vo.getPageSize());
    }

    public ComUnitDTO getByTreeCode(String treeCode) {
        return comUnitDAO.getUnitByTreeCode(treeCode);
    }

    public ComUnitDTO getUnitById(String id) {
        ComUnitDTO dto = comUnitDAO.getUnitById(id);
        if (null != dto && null != dto.getUnitType()) {
            dto.setUnitTypeName(CommonEnum.unitType.getEnum(dto.getUnitType()).getDescription());
            dto.setManagers(comPersonService.getManagerListByUnitId(id));
        }
        return dto;
    }

    public ComUnitDTO getUpUnitById(String id) {
        ComUnitDTO dto = comUnitDAO.getUnitById(id);
        String upTreeCode = "";
        if (null != dto && Objects.nonNull(dto.getTreeCode())) {
            if ((dto.getTreeCode().length() / properties.getDefaultCodeLength()) > 3) {
                upTreeCode = dto.getTreeCode().substring(0, 10);
            } else {
                upTreeCode = dto.getTreeCode().substring(0, dto.getTreeCode().length() - properties.getDefaultCodeLength());
            }
        }
        ComUnitDTO unit=this.getByTreeCode(upTreeCode);
        return dto;
    }

    public List<ComUnit> getUnitProjLimit(int queryType) {
        List<ComUnit> list = this.entityList(new ComUnit());
        if (queryType == 1) {
            list = list.stream().filter(e -> e.getUnitType().equals(0) && e.getTreeCode().length() == properties.getDefaultCodeLength() * 2).collect(Collectors.toList());
        } else {
            list = list.stream().filter(e -> !e.getUnitType().equals(0) && e.getTreeCode().length() == properties.getDefaultCodeLength() * 2 && e.getProvinceSubjection().equals(1)).collect(Collectors.toList());
        }
        return list;
    }

    public boolean updateUnitProjLimit(List<UnitProjLimitDTO> list) {
        list.forEach(f -> {
            ComUnit unit = new ComUnit();
            unit.setId(f.getId());
            unit.setProjLimit(f.getProjLimit());
            comUnitDAO.updateById(unit);
        });

        return true;
    }

    @Override
    protected void setCriteriaOrder(QueryWrapper<ComUnit> wrapper) {
        wrapper.orderByAsc("unit_type");
        wrapper.orderByAsc("tree_code");
    }

    public String getNewCode(String fatherCode) {
        String newCode = comUnitDAO.getMaxTreeCode(fatherCode);
        return fatherCode + newCode.substring(newCode.length() - 5);
    }

    @Transactional
    public String register(ComUnitDTO dto) {
        if (!apiHttpRequest.SMSCheck(dto.getMobile(), dto.getCode()))
            throw new BusinessException("验证码错误或已过期!");
//        // 查询验证码
//        dto.setCertId(dto.getCertId().toLowerCase());
//        String value = (String) redisUtils.get(dto.getMobile());
//        if (StringUtils.isBlank(value))
//            throw new BusinessException("验证码错误或已过期!");
//        if (StringUtils.isBlank(dto.getCode()) || !dto.getCode().equalsIgnoreCase(value))
//            throw new BusinessException("验证码错误或已过期!");
        if (null != isExist(dto.getUnitName()))
            throw new BusinessException("单位已注册,请检查后再试!");
        if (systemUserService.isExist(dto.getUsername()))
            throw new BusinessException("用户已存在!");
        if (comPersonService.isCertIdExist(dto.getCertId().toLowerCase(), null))
            throw new BusinessException("证件号已注册,请检查后再试!");
        if (comPersonService.isMobileExist(dto.getMobile()))
            throw new BusinessException("该手机号已注册,请检查后再试!");
        //添加单位
        ComUnit comUnit = this.convert2Entity(dto);
        comUnit.setTreeCode(this.getNewCode(dto.getUpTreeCode()));
        comUnit.setUnitState(CommonEnum.unitState.cancel.getCode());
        comUnit.setAuditResult(1);
        if (Objects.isNull(dto.getUpTreeCode()))
            throw new BusinessException("参数错误,请检查后再试!");

        String treeCode = dto.getUpTreeCode();
        if ((treeCode.length() / properties.getDefaultCodeLength()) > 2) {
            treeCode = treeCode.substring(0, 10);
        }

        ComUnitDTO d = this.getByTreeCode(treeCode);
        if (d == null)
            throw new BusinessException("上级单位不存在,或已删除,请稍后再试");
        comUnit.setAuditUnit(d.getId());

        String id = this.insert(comUnit);
        dto.setId(id);
        comManagerService.registerManager(dto);
        return id;
    }

    @Transactional
    public String audit(ComUnitDTO unit) {
        ComUnit comUnit = this.entityById(unit.getId());
        if (comUnit == null) {
            throw new BusinessException("单位不存在或已删除");
        }
        if (null == comUnit.getAuditResult()) {
            throw new BusinessException("单位状态不允许审核");
        }
        comUnit = this.convert2Entity(unit);
        comUnit.setUnitState(unit.getAuditResult().equals(10) ? CommonEnum.unitState.normal.getCode() : CommonEnum.unitState.cancel.getCode());
        List<ComPersonDTO> list = comManagerService.getManagerListByUnitId(comUnit.getId());
        if (unit.getAuditResult().equals(10)) {
            if (comUnit.getAuditResult().equals(1)) {
                for (ComPersonDTO e : list) {
                    if (Objects.nonNull(e.getMobile())) {
                        this.SMSSending(e.getMobile(), e.getPersonName(), properties.getSystemName(), "单位", "已审核通过", "请使用注册账户登录系统");
                        break;
                    }
                }
            }
        } else {
            if (comUnit.getAuditResult().equals(1)) {
                for (ComPersonDTO e : list) {
                    if (Objects.nonNull(e.getMobile())) {
                        this.SMSSending(e.getMobile(), e.getPersonName(), properties.getSystemName(), "单位", "审核不通过" + "(" + unit.getAuditContent() + ")", "请联系上级主管部门处理");
                        break;
                    }
                }
            }
        }
        comUnit.setAuditDate(new Date());
        comUnit.setAuditPerson(SecurityUserHolder.getPersonId());
        comUnit.setAuditUnit(SecurityUserHolder.getUnitId());
        this.update(comUnit);
        refreshRedis();
        return unit.getId();
    }

    private void SMSSending(String mobile, String name, String systemName, String userType, String result, String message) {
        SMSSend(mobile, name, systemName, userType, result, message, apiHttpRequest, 3);
    }

    private void SMSSend(String mobile, String name, String systemName, String userType, String result, String message, ApiHttpRequest apiHttpRequest, Integer sendType) {
        long time = System.currentTimeMillis();
        String sign = Hashing.sha256().hashString(mobile + time, StandardCharsets.UTF_8).toString();
        SMSParameterDTO sms = new SMSParameterDTO();
        sms.setMobile(mobile);
        sms.setAppId(4);
        sms.setTTypeId(71);
        sms.setSmsType(2);
        sms.setCode(null);
        sms.setTimestamp(time);
        sms.setSign(sign);
        SMSTemplateDTO smsTmp = new SMSTemplateDTO();
        smsTmp.setName(name);
        smsTmp.setSystemName(systemName);
        smsTmp.setUserType(userType);
        smsTmp.setResult(result);
        smsTmp.setMessage(message);
        sms.setParamMap(smsTmp);
        if (apiHttpRequest.SMSSending(sms))
            comSendingRecordService.record(mobile, 1, sendType);
        else
            comSendingRecordService.record(mobile, 2, sendType);
    }

    @Transactional
    public String updateUnitState(ComUnitDTO e) {
        ComUnit comUnit = this.entityById(e.getId());
        if (comUnit == null) {
            throw new BusinessException("单位信息不存在");
        }
        refreshRedis();
        return this.update(this.convert2Entity(e));
    }

    @Transactional
    public String save(ComUnitDTO dto) {
        if (null == dto.getId()) {
            return insertUnit(dto);
        } else {
            return updateUnit(dto);
        }
    }

    @Transactional
    public String insertUnit(ComUnitDTO unit) {
        ComUnit comUnit = this.convert2Entity(unit);
        comUnit.setTreeCode(this.getNewCode(unit.getUpTreeCode()));
        comUnit.setUnitState(CommonEnum.unitState.normal.getCode());
        String id = this.insert(comUnit);
        comManagerService.addManagerByList(unit.getManagers(), id, getManagerRoleByUnitType(comUnit.getUnitType(), comUnit.getTreeCode()));
        refreshRedis();
        return id;
    }

    @Transactional
    public String updateUnit(ComUnitDTO dto) {
        ComUnit comUnit = this.entityById(dto.getId());
        if (comUnit == null)
            throw new BusinessException("单位不存在或已删除!");
        ComUnit unit = isExist(dto.getUnitName());
        if (null != unit && !unit.getId().equals(dto.getId()))
            throw new BusinessException("单位名称已存在!");
        if (null != dto.getUpTreeCode() && !dto.getUpTreeCode().equals(comUnit.getTreeCode().substring(0, comUnit.getTreeCode().length() - 5))) {
            dto.setTreeCode(this.getNewCode(dto.getUpTreeCode()));
//            if (Objects.nonNull(comUnit.getAuditUnit())) {
//                String treeCode = dto.getUpTreeCode();
//                if ((treeCode.length() / properties.getDefaultCodeLength()) > 2) {
//                    treeCode = treeCode.substring(0, 10);
//                }
//                ComUnitDTO d = this.getByTreeCode(treeCode);
//                if (null != d) {
//                    dto.setAuditUnit(d.getId());
//                }
//            }
        }
        String roleId = null;
        if (null != dto.getUnitType() && !dto.getUnitType().equals(comUnit.getUnitType()))
            roleId = getManagerRoleByUnitType(dto.getUnitType(), dto.getTreeCode());
        if (null != roleId) {
            comManagerService.updateManagerRole(dto.getId(), roleId);
        }
        refreshRedis();
        return this.update(this.convert2Entity(dto));
    }

    public String getManagerRoleByUnitType(Integer unitType, String treeCode) {
        String roleId = null;
        if (null != unitType && unitType.equals(CommonEnum.unitType.gov.getCode())) {
            if (null != treeCode && treeCode.length() == properties.getDefaultCodeLength())
                roleId = CommonEnum.systemRole.topGov.getCode().toString();
            else
                roleId = CommonEnum.systemRole.gov.getCode().toString();
        } else
            roleId = CommonEnum.systemRole.unit.getCode().toString();
        return roleId;
    }

    @Transactional(rollbackFor = Exception.class)
    public String deleteUnit(String Id) {
        ComPerson comPerson = new ComPerson();
        comPerson.setUnitId(Id);
        List<ComPerson> list = comPersonService.entityList(comPerson);
        if (null != list && list.size() > 0)
            throw new BusinessException("该单位下有人员信息,禁止删除");
        refreshRedis();
        this.deleteById(Id);
        return Id;
    }

    public ComUnit isExist(String unitName) {
        ComUnit comUnit = new ComUnit();
        comUnit.setUnitName(unitName.replace(" ", ""));
        comUnit = this.getEntity(comUnit);
        if (null != comUnit)
            return comUnit;
        else
            return null;
    }

    public List<ComUnitDTO> getListByTreeCode(String treeCode) {
        List<ComUnitDTO> list = getList();
        return list.stream().filter(e -> e.getTreeCode().substring(0, e.getTreeCode().length() - 5).equals(treeCode)).collect(Collectors.toList());
    }

    public List<ComUnitDTO> getListByType(Integer unitType) {
        List<ComUnitDTO> list = getList();
        if (null != unitType) {
            if (unitType.equals(1))
                list = list.stream().filter(e -> !e.getUnitType().equals(0) && e.getUnitState().equals(1)).collect(Collectors.toList());
            else if (unitType.equals(0))
                list = list.stream().filter(e -> e.getUnitType().equals(0) && e.getUnitState().equals(1)).collect(Collectors.toList());
        } else
            list = list.stream().filter(e -> e.getUnitState().equals(1)).collect(Collectors.toList());
        return list;
    }

    public List<ComUnitDTO> getList() {
        List<ComUnitDTO> list = null;
        Object obj = redisUtils.get(RedisKey.UnitList);
        if (null == obj) {
            list = comUnitDAO.getList();
            list.forEach((e) -> {
                if (null != e.getUnitType())
                    e.setUnitTypeName(CommonEnum.unitType.getEnum(e.getUnitType()).getDescription());
            });
            redisUtils.set(RedisKey.UnitList, list, 7, TimeUnit.DAYS);
        } else
            list = (List<ComUnitDTO>) obj;
        return list;
    }

    public DataStatisticsDTO getRegisterUnitCount(ComUnitQueryVO v) {
        QueryWrapper criteria = new QueryWrapper();
        this.setCriteriaForQuery(v, criteria);
        return comUnitDAO.getRegisterUnitCount(criteria);
    }

    public DataStatisticsDTO getRegisterUnitCountByDay(ComUnitQueryVO v) {
        QueryWrapper criteria = new QueryWrapper();
        this.setCriteriaForQuery(v, criteria);
        return comUnitDAO.getRegisterUnitCountByDay(criteria);
    }

    @Override
    public ComUnit getUnitInfoByLongId(Long id) {
        ComUnit comUnit = null;
//        List<ComUnit> list = getList().stream().filter(e -> e.getLongId().equals(id)).collect(Collectors.toList());
//        if (list.size() > 0)
//            comUnit = list.stream().findFirst().get();
        return new ComUnit();
    }

    @Override
    public void getUnitFromRemoteServer() {
        OrgInfo orgInfo = apiHttpRequest.remoteUnitInfo();
        if (null != orgInfo) {
            ComUnit unit = this.getUnitInfoByLongId(orgInfo.getId());
            if (null == unit) {
                unit = new ComUnit();
                unit.setId(orgInfo.getId().toString());
                unit.setLongId(orgInfo.getId());
                unit.setParentId(orgInfo.getParentId());
                unit.setUnitName(orgInfo.getUnitName());
                unit.setTreeCode(orgInfo.getTreeCode());
                unit.setCreated(new Date());
                this.save(unit);
            } else {
                if (!unit.getUnitName().equals(orgInfo.getUnitName())) {
                    unit.setUnitName(orgInfo.getUnitName());
                    this.update(unit);
                }
                if (null == unit.getParentId()) {
                    unit.setParentId(orgInfo.getParentId());
                    this.update(unit);
                }
            }
            if (null != orgInfo.getParentId()) {
                ComUnit parentUnit = this.getUnitInfoByLongId(orgInfo.getParentId());
                if (null == parentUnit) {
                    OrgInfo patent = apiHttpRequest.remoteParentUnit(orgInfo.getParentId().toString());
                    if (null != patent) {
                        unit = new ComUnit();
                        unit.setId(patent.getId().toString());
                        unit.setLongId(patent.getId());
                        unit.setParentId(patent.getParentId());
                        unit.setUnitName(patent.getUnitName());
                        unit.setTreeCode(orgInfo.getTreeCode().substring(0, orgInfo.getTreeCode().length() - properties.getDefaultCodeLength()));
                        unit.setCreated(new Date());
                        this.save(unit);
                    }
                }
            }
            refreshRedis();
        }
    }

    /**
     * 刷新Redis单位信息
     */
    public void refreshRedis() {
        redisUtils.del(RedisKey.UnitList);
    }
}