/*
 * Copyright (c) 2024 codvision.com All Rights Reserved.
 */

package com.codvision.basic.service.impl;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
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.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.codvision.auth.entity.Org;
import com.codvision.auth.entity.excel.PersonExcel;
import com.codvision.auth.mapper.OrgMapper;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.util.AuthUtil;
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.entity.SysDictItem;
import com.codvision.base.entity.SysTag;
import com.codvision.base.mapper.SysDictItemMapper;
import com.codvision.base.mapper.SysTagMapper;
import com.codvision.basic.entity.Person;
import com.codvision.basic.entity.PersonAddressRelation;
import com.codvision.basic.entity.inspection.InspectionTarget;
import com.codvision.basic.entity.inspection.InspectionTask;
import com.codvision.basic.entity.vo.PersonCountVO;
import com.codvision.basic.entity.vo.PersonPortraitVO;
import com.codvision.basic.entity.vo.PersonVO;
import com.codvision.basic.mapper.PersonAddressRelationMapper;
import com.codvision.basic.mapper.PersonMapper;
import com.codvision.basic.mapper.inspection.InspectionTargetMapper;
import com.codvision.basic.mapper.inspection.InspectionTaskMapper;
import com.codvision.basic.service.PersonService;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.commoncore.utils.Preconditions;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.util.QueryPageUtil;
import lombok.RequiredArgsConstructor;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static com.codvision.basic.constant.BasicConstant.BIRTHDAY_REGEX;


/**
 * @author KX
 */
@Service
@RequiredArgsConstructor
public class PersonServiceImpl extends ServiceImpl<PersonMapper, Person> implements PersonService {

    private final OrgMapper orgMapper;
    private final SysTagMapper sysTagMapper;
    private final SysDictItemMapper sysDictItemMapper;
    private final PersonAddressRelationMapper relationMapper;
    private final InspectionTaskMapper inspectionTaskMapper;
    private final InspectionTargetMapper inspectionTargetMapper;

    @Override
    @SuppressWarnings("all")
    public IPage<PersonVO> getPageResult(QueryPage<Map<String, Object>> queryPage) {
        SysUserBean currentUser = AuthUtil.getCurrentUser();
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        if (!paraMap.containsKey("oid") || paraMap.get("oid") == null) {
            paraMap.put("oid", currentUser.getActiveOid());
        }
        return getBaseMapper().getPageResult(queryPage.getPage(), paraMap);
    }

    @Override
    public PersonVO details(Integer id) {
        return getBaseMapper().details(id);
    }

    @Override
    public void add(Person person) {
        LambdaQueryWrapper<Person> queryWrapper = Wrappers.<Person>lambdaQuery().eq(Person::getIdType, person.getIdType()).eq(Person::getIdCard, person.getIdCard());
        Long count = getBaseMapper().selectCount(queryWrapper);
        Preconditions.checkArgument(count <= 0, "该人员已录入请勿重复录入！");
        addInspectionTarget(person);
        getBaseMapper().insert(person);
    }

    @Override
    public boolean updateById(Person entity) {
        addInspectionTarget(entity);
        return SqlHelper.retBool(getBaseMapper().updateById(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchLogicDel(List<Integer> personIds) {
        // 根据人员删除关系
        for (Integer personId : personIds) {
            relationMapper.delete(Wrappers.<PersonAddressRelation>lambdaQuery().eq(PersonAddressRelation::getPersonId, personId));
        }

        getBaseMapper().deleteBatchIds(personIds);
    }

    @Override
    public PersonCountVO getPersonCount(String addressCode, String addressType) {
        if ("4".equals(addressType)) {
            return getBaseMapper().getPersonCountByPark(addressCode);
        } else if ("1".equals(addressType)) {
            return getBaseMapper().getPersonCountByBuilding(addressCode);
        }
        return getBaseMapper().getPersonCountByHouse(addressCode);
    }

    @Override
    public IPage<PersonVO> getPersonListByAddressCode(QueryPage<Map<String, Object>> queryPage) {
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        String addressCodes = (String) paraMap.get("addressCodes");
        return getBaseMapper().getPersonListByAddressCode(queryPage.getPage(), addressCodes);
    }

    /**
     * 群体画像
     *
     * @param personIds 人员id
     * @return List<PersonPortraitVo>
     */
    @Override
    public List<PersonPortraitVO> getPersonPortrait(List<Integer> personIds) {
        List<PersonPortraitVO> personPortraitVoList = new ArrayList<>();
        // 性别统计
        PersonPortraitVO sexPortrait = PersonPortraitVO.builder().type("PERSON_SEX").label("性别")
                .personPortraitVoList(getBaseMapper().getSexCount(personIds)).build();
        personPortraitVoList.add(sexPortrait);
        // 年龄统计
        Map<String, Object> sexAndAge = getBaseMapper().getAgeCount(personIds);
        List<PersonPortraitVO> agePortraitVoList = new ArrayList<>();
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("0-10").num(Integer.parseInt(sexAndAge.get("age_zero_ten").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("10-20").num(Integer.parseInt(sexAndAge.get("age_ten_twenty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("20-30").num(Integer.parseInt(sexAndAge.get("age_twenty_thirty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("30-40").num(Integer.parseInt(sexAndAge.get("age_thirty_forty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("40-50").num(Integer.parseInt(sexAndAge.get("age_forty_fifty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("50-60").num(Integer.parseInt(sexAndAge.get("age_fifty_sixty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("60-70").num(Integer.parseInt(sexAndAge.get("age_sixty_seventy").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("70-80").num(Integer.parseInt(sexAndAge.get("age_seventy_eighty").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("80-90").num(Integer.parseInt(sexAndAge.get("age_eighty_ninety").toString())).build());
        agePortraitVoList.add(PersonPortraitVO.builder().type("AGE").label("90-100").num(Integer.parseInt(sexAndAge.get("age_ninety_hundred").toString())).build());
        PersonPortraitVO agePortrait = PersonPortraitVO.builder().type("AGE").label("年龄")
                .personPortraitVoList(agePortraitVoList).build();
        personPortraitVoList.add(agePortrait);
        // 文化程度统计
        PersonPortraitVO educationPortrait = PersonPortraitVO.builder().type("EDUCATION").label("文化程度")
                .personPortraitVoList(getBaseMapper().getEducationCount(personIds)).build();
        personPortraitVoList.add(educationPortrait);
        // 婚姻状况
        PersonPortraitVO maritalPortrait = PersonPortraitVO.builder().type("MARITAL_STATUS").label("婚姻状况")
                .personPortraitVoList(getBaseMapper().getMaritalCount(personIds)).build();
        personPortraitVoList.add(maritalPortrait);
        // 人口类型
        PersonPortraitVO personTypePortrait = PersonPortraitVO.builder().type("PERSON_TYPE").label("人口类型")
                .personPortraitVoList(getBaseMapper().getPersonTypeCount(personIds)).build();
        personPortraitVoList.add(personTypePortrait);

        // 人员标签
        PersonPortraitVO personTagPortrait = PersonPortraitVO.builder().type("PERSON_TAGS").label("人员标签")
                .personPortraitVoList(getBaseMapper().getPersonTagCount(personIds)).build();
        personPortraitVoList.add(personTagPortrait);

        return personPortraitVoList;
    }

    @SneakyThrows
    @Override
    public String excelImport(MultipartFile file) {
        String reqNo = IdUtil.fastSimpleUUID();
        ExcelDataReadListener<PersonExcel> listener = new ExcelDataReadListener<PersonExcel>(SpringUtil.getBean(PersonServiceImpl.class)::excelSave) {
            @Override
            protected void validator(PersonExcel personExcel, ExcelCheck<PersonExcel> check, AnalysisContext context) {
                checkOne(reqNo, personExcel, check, context.readRowHolder().getRowIndex());
            }
        };
        EasyExcel.read(file.getInputStream(), PersonExcel.class, listener).sheet().doRead();
        if (listener.isHeadAbnormal()) {
            throw new BusinessException("文件格式错误，请检查导入模板");
        }

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

    private void checkOne(String reqNo, PersonExcel personExcel, ExcelCheck<PersonExcel> check, Integer rowIndex) {
        Map<String, Object> fieldsErrorMap = new HashMap<>(16);
        if (StringUtils.isBlank(personExcel.getName())) {
            fieldsErrorMap.put("name", "姓名必填");
        }
        if (StringUtils.isBlank(personExcel.getOrgPath())) {
            fieldsErrorMap.put("orgPath", "所属组织必填");
        } else {
            Org org = orgMapper.getOrgByPath(personExcel.getOrgPath());
            if (org == null) {
                fieldsErrorMap.put("orgPath", "系统内无相关组织信息");
            }
        }

        if (StringUtils.isBlank(personExcel.getSex())) {
            fieldsErrorMap.put("sex", "性别必填");
        }

        if (StringUtils.isBlank(personExcel.getAge())) {
            fieldsErrorMap.put("age", "年龄必填");
        } else {
            if (!NumberUtil.isNumber(personExcel.getAge())) {
                fieldsErrorMap.put("age", "年龄输入不正确");
            }
        }

        if (StringUtils.isBlank(personExcel.getPhone())) {
            fieldsErrorMap.put("phone", "联系电话必填");
        } else {
            if (!Validator.isMobile(personExcel.getPhone())) {
                fieldsErrorMap.put("phone", "联系电话格式不正确");
            }
        }

        if (StringUtils.isBlank(personExcel.getIdType())) {
            fieldsErrorMap.put("idType", "证件类型必填");
        }

        if (StringUtils.isBlank(personExcel.getIdCard())) {
            fieldsErrorMap.put("idCard", "证件号码必填");
        }

        // 校验是否已存在
        if (StringUtils.isNotEmpty(personExcel.getIdType()) && StringUtils.isNotEmpty(personExcel.getIdCard())) {
            LambdaQueryWrapper<SysDictItem> wrapper = Wrappers.<SysDictItem>lambdaQuery()
                    .eq(SysDictItem::getDictType, "ID_TYPE").eq(SysDictItem::getLabel, personExcel.getIdType());
            SysDictItem idType = sysDictItemMapper.selectOne(wrapper);
            LambdaQueryWrapper<Person> queryWrapper = Wrappers.<Person>lambdaQuery().eq(Person::getIdType, idType.getItemValue()).eq(Person::getIdCard, personExcel.getIdCard());
            Long count = getBaseMapper().selectCount(queryWrapper);
            if (count > 0) {
                fieldsErrorMap.put("idCard", "该人员已存在系统，请勿重复录入");
            }
        }

        if (StringUtils.isBlank(personExcel.getPersonType())) {
            fieldsErrorMap.put("personType", "人口类型必填");
        }

        if (StringUtils.isBlank(personExcel.getNativePlace())) {
            fieldsErrorMap.put("nativePlace", "籍贯必填");
        }

        if (StringUtils.isNotEmpty(personExcel.getBirthday()) && !Pattern.compile(BIRTHDAY_REGEX).matcher(personExcel.getBirthday()).matches()) {
            fieldsErrorMap.put("birthday", "出生日期格式不正确，应为yyyy-MM-dd格式");
        }

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

    @Transactional(rollbackFor = Exception.class)
    public void excelSave(List<PersonExcel> cachedDataList) {
        if (CollectionUtils.isNotEmpty(cachedDataList)) {
            for (PersonExcel personExcel : cachedDataList) {

                // 校验是否已存在/过滤掉同一个excel中存在证件类型和证件号码相同的人员信息
                if (StringUtils.isNotEmpty(personExcel.getIdType()) && StringUtils.isNotEmpty(personExcel.getIdCard())) {
                    LambdaQueryWrapper<SysDictItem> wrapper = Wrappers.<SysDictItem>lambdaQuery()
                            .eq(SysDictItem::getDictType, "ID_TYPE").eq(SysDictItem::getLabel, personExcel.getIdType());
                    SysDictItem idType = sysDictItemMapper.selectOne(wrapper);
                    LambdaQueryWrapper<Person> queryWrapper = Wrappers.<Person>lambdaQuery().eq(Person::getIdType, idType.getItemValue()).eq(Person::getIdCard, personExcel.getIdCard());
                    Long count = getBaseMapper().selectCount(queryWrapper);
                    if (count > 0) {
                        continue;
                    }
                }

                Org org = orgMapper.getOrgByPath(personExcel.getOrgPath());
                SysDictItem personSex = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "PERSON_SEX").eq(SysDictItem::getLabel, personExcel.getSex()));
                SysDictItem idType = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "ID_TYPE").eq(SysDictItem::getLabel, personExcel.getIdType()));
                SysDictItem personType = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "PERSON_TYPE").eq(SysDictItem::getLabel, personExcel.getPersonType()));

                Person person = Person.builder().name(personExcel.getName()).usedName(personExcel.getUsedName()).oid(org.getOid())
                        .sex(personSex.getItemValue()).age(Integer.parseInt(personExcel.getAge()))
                        .phone(personExcel.getPhone()).idType(idType.getItemValue()).idCard(personExcel.getIdCard())
                        .personType(personType.getItemValue()).nativePlace(personExcel.getNativePlace())
                        .birthday(personExcel.getBirthday()).inhabitedAddress(personExcel.getInhabitedAddress())
                        .residenceAddress(personExcel.getResidenceAddress()).remark(personExcel.getRemark()).build();
                // 民族
                if (StringUtils.isNotEmpty(personExcel.getNation())) {
                    SysDictItem nation = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "NATION").eq(SysDictItem::getLabel, personExcel.getNation()));
                    person.setNation(nation.getItemValue());
                }
                // 文化程度
                if (StringUtils.isNotEmpty(personExcel.getEducation())) {
                    SysDictItem education = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "EDUCATION").eq(SysDictItem::getLabel, personExcel.getEducation()));
                    person.setEducation(education.getItemValue());
                }
                // 人员标签
                if (StringUtils.isNotEmpty(personExcel.getPersonTags())) {
                    SysTag personTag = sysTagMapper.selectOne(Wrappers.<SysTag>lambdaQuery().eq(SysTag::getTagName, personExcel.getPersonTags()).eq(SysTag::getDictItemValue, "PERSON"));
                    person.setPersonTags(String.valueOf(personTag.getId()));
                }
                // 国籍
                if (StringUtils.isNotEmpty(personExcel.getNationality())) {
                    SysDictItem nationality = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "NATIONALITY").eq(SysDictItem::getLabel, personExcel.getNationality()));
                    person.setNationality(nationality.getItemValue());
                }
                // 婚姻状况
                if (StringUtils.isNotEmpty(personExcel.getMaritalStatus())) {
                    SysDictItem maritalStatus = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "MARITAL_STATUS").eq(SysDictItem::getLabel, personExcel.getMaritalStatus()));
                    person.setMaritalStatus(maritalStatus.getItemValue());
                }
                // 政治面貌
                if (StringUtils.isNotEmpty(personExcel.getPoliticFace())) {
                    SysDictItem politicFace = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "POLITIC_FACE").eq(SysDictItem::getLabel, personExcel.getPoliticFace()));
                    person.setPoliticFace(politicFace.getItemValue());
                }
                // 兵役状况
                if (StringUtils.isNotEmpty(personExcel.getMilitaryStatus())) {
                    SysDictItem militaryStatus = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "MILITARY_STATUS").eq(SysDictItem::getLabel, personExcel.getMilitaryStatus()));
                    person.setMilitaryStatus(militaryStatus.getItemValue());
                }
                // 宗教信仰
                if (StringUtils.isNotEmpty(personExcel.getReligious())) {
                    SysDictItem religious = sysDictItemMapper.selectOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getDictType, "RELIGIOUS").eq(SysDictItem::getLabel, personExcel.getReligious()));
                    person.setReligious(religious.getItemValue());
                }
                getBaseMapper().insert(person);
            }
        }
    }

    /**
     * 导出人员信息
     *
     * @param paramMap 查询条件
     */
    @Override
    public void excelExport(Map<String, Object> paramMap) {
        new AbstractExportClass<PersonExcel, Object>() {
            @Override
            protected List<PersonExcel> getList(QueryPage<Object> queryPage, Object queryBean) {
                IPage<PersonExcel> personExcelIPage = getBaseMapper().excelExport(queryPage.getPage(), paramMap);
                return personExcelIPage.getRecords();
            }
        }.excelUser("人员信息表", null, PersonExcel.class);

    }

    /**
     * 判断是否新增检查对象
     * @param person 人员信息
     */
    private void addInspectionTarget(Person person) {
        if (StringUtils.isEmpty(person.getPersonTags())) {
            return;
        }
        for (InspectionTask inspectionTask : inspectionTaskMapper.getTaskByCheckTag("PERSON", person.getPersonTags())) {
            InspectionTarget exist = inspectionTargetMapper.selectOne(Wrappers.<InspectionTarget>lambdaQuery().eq(InspectionTarget::getTargetCode, person.getIdCard()).eq(InspectionTarget::getTaskId, inspectionTask.getId()));
            if (exist == null) {
                InspectionTarget inspectionTarget = new InspectionTarget();
                inspectionTarget = updateInspectionTarget(inspectionTarget, person);
                inspectionTargetMapper.insert(inspectionTarget);
            } else {
                exist = updateInspectionTarget(exist, person);
                exist.setTaskId(inspectionTask.getId());
                inspectionTargetMapper.updateById(exist);
            }
        }
    }

    public InspectionTarget updateInspectionTarget(InspectionTarget inspectionTarget, Person person) {
        inspectionTarget.setFlagActive(1);
        inspectionTarget.setCategory("PERSON");
        inspectionTarget.setDataSource("1");
        inspectionTarget.setTargetName(person.getName());
        inspectionTarget.setTargetCode(person.getIdCard());
        inspectionTarget.setTargetAddress(person.getInhabitedAddress());
        inspectionTarget.setTargetAlias(person.getUsedName());
        inspectionTarget.setOid(person.getOid());
        inspectionTarget.setRemark(person.getRemark());
        inspectionTarget.setMobile(person.getPhone());
        inspectionTarget.setTagIdList(person.getPersonTags());
        return inspectionTarget;
    }

}
