package com.engine.salary.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salarysob.po.SalarySobPO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.entity.taxdeclaration.bo.TaxDeclarationBO;
import com.engine.salary.entity.taxdeclaration.param.TaxDeclarationListQueryParam;
import com.engine.salary.entity.taxdeclaration.param.TaxDeclarationSaveParam;
import com.engine.salary.entity.taxdeclaration.po.TaxDeclarationPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.salaryaccounting.SalaryAcctRecordStatusEnum;
import com.engine.salary.enums.salarysob.IncomeCategoryEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.datacollection.AddUpSituationMapper;
import com.engine.salary.mapper.salaryacct.SalaryAcctRecordMapper;
import com.engine.salary.mapper.taxdeclaration.TaxDeclarationMapper;
import com.engine.salary.service.*;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import weaver.general.BaseBean;
import weaver.hrm.User;

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

@Slf4j
public class TaxDeclarationServiceImpl extends Service implements TaxDeclarationService {

    private final Boolean isLog = "true".equals(new BaseBean().getPropValue("hrmSalary", "log"));

    private TaxDeclarationMapper getTaxDeclarationMapper() {
        return MapperProxyFactory.getProxy(TaxDeclarationMapper.class);
    }

    private SalaryAcctRecordMapper getSalaryAcctRecordMapper() {
        return MapperProxyFactory.getProxy(SalaryAcctRecordMapper.class);
    }

    private AddUpSituationMapper getAddUpSituationMapper() {
        return MapperProxyFactory.getProxy(AddUpSituationMapper.class);
    }

    private TaxDeclarationDetailService getTaxDeclarationDetailService(User user) {
        return ServiceUtil.getService(TaxDeclarationDetailServiceImpl.class, user);
    }

    private AddUpSituationService getAddUpSituationService(User user) {
        return ServiceUtil.getService(AddUpSituationServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private SalaryAcctResultService getSalaryAcctResultService(User user) {
        return ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

    private SalaryAcctRecordService getSalaryAcctRecordService(User user) {
        return ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user);
    }

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    @Override
    public List<TaxDeclarationPO> listByTaxCycleAndTaxAgentIds(YearMonth taxCycle, Collection<Long> taxAgentIds) {
        if (Objects.isNull(taxCycle) || CollectionUtils.isEmpty(taxAgentIds)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }
        TaxDeclarationPO po = TaxDeclarationPO.builder().taxCycle(SalaryDateUtil.toDate(taxCycle, 1)).taxAgentIds(taxAgentIds).build();
        List<TaxDeclarationPO> taxDeclarationPOS = getTaxDeclarationMapper().listSome(po);
        return taxDeclarationPOS;
    }

    @Override
    public PageInfo<TaxDeclarationPO> listPageByParam(TaxDeclarationListQueryParam queryParam) {
        long currentEmployeeId = user.getUID();

        // 分页参数
        TaxDeclarationPO po = TaxDeclarationPO.builder().build();
        LocalDateRange localDateRange = new LocalDateRange();
        if (Objects.nonNull(queryParam.getFromSalaryMonth())) {
            localDateRange.setFromDate(SalaryDateUtil.localDateToDate(queryParam.getFromSalaryMonth().atDay(1)));
        }
        if (Objects.nonNull(queryParam.getEndSalaryMonth())) {
            localDateRange.setEndDate(SalaryDateUtil.localDateToDate(queryParam.getEndSalaryMonth().atEndOfMonth()));
        }
        po.setSalaryMonths(localDateRange);


        // 分权
        Boolean openDevolution = getTaxAgentService(user).isNeedAuth(currentEmployeeId);
        if (openDevolution) {
            // 查询负责管理的个税扣缴义务人
            Collection<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listAllTaxAgents(currentEmployeeId);
            if (CollectionUtils.isEmpty(taxAgentPOS)) {
                return new PageInfo<>(new ArrayList<>());
            }
            Set<Long> taxAgentIds = SalaryEntityUtil.properties(taxAgentPOS, TaxAgentPO::getId);
            po.setTaxAgentIds(taxAgentIds);
        }

        // 查询个税申报表
        List<TaxDeclarationPO> taxDeclarationPOS = getTaxDeclarationMapper().listSome(po);
        return SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(),
                taxDeclarationPOS, TaxDeclarationPO.class);

    }

    //根据id查询taxAgents
    @Override
    public List<TaxAgentPO> countByTaxDeclarationId(Collection<Long> taxAgentIds) {
        if (CollectionUtils.isEmpty(taxAgentIds)) {
            return Collections.emptyList();
        }
        return getTaxAgentService(user).listByIds(taxAgentIds);
    }

    //根据id获取TaxDeclaration
    @Override
    public TaxDeclarationPO getById(Long id) {
        return getTaxDeclarationMapper().getById(id);
    }

    @Override
    public void save(TaxDeclarationSaveParam saveParam) {
        long currentEmployeeId = user.getUID();

        // 个税扣缴义务人id
        Set<Long> taxAgentIds;

        Long taxAgentId = saveParam.getTaxAgentId();

        if (taxAgentId == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "请选择要申报的扣缴义务人！"));
        }

        if (taxAgentId != null) {
            taxAgentIds = Collections.singleton(taxAgentId);
        } else {
            //管理的扣缴义务人一次性申报逻辑
            Boolean openDevolution = getTaxAgentService(user).isOpenDevolution();
            if (BooleanUtils.isFalse(openDevolution)) {
                taxAgentIds = SalaryEntityUtil.properties(getTaxAgentService(user).listAll(), TaxAgentPO::getId);
            } else {
                taxAgentIds = SalaryEntityUtil.properties(getTaxAgentService(user).listAllTaxAgentsAsAdmin(currentEmployeeId), TaxAgentPO::getId);
            }
        }

        // 检查是否具有权限
        if (CollectionUtils.isEmpty(taxAgentIds)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "对不起，您不具备任何个税扣缴义务人的管理权限"));
        }

        // 查询个税扣缴义务人
        List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listByIds(taxAgentIds);
        Map<Long, String> taxAgentNameMap = SalaryEntityUtil.convert2Map(taxAgentPOS, TaxAgentPO::getId, TaxAgentPO::getName);

        // 薪资所属月的日期范围
        LocalDateRange salaryMonthDateRange = SalaryDateUtil.localDate2Range(SalaryDateUtil.localDateToDate(saveParam.getSalaryMonth().atDay(1)));
        if (Objects.isNull(salaryMonthDateRange)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "薪资所属月参数错误"));
        }

        // 查询薪资所属月个税扣缴义务人已经生成过的个税申报表
        List<TaxDeclarationPO> taxDeclarationPOS = listBySalaryMonthTax(TaxDeclarationPO.builder().salaryMonths(salaryMonthDateRange).taxAgentIds(taxAgentNameMap.keySet()).build());
        // 已经生成过个税申报表，不允许再次生成个税申报表
        if (CollectionUtils.isNotEmpty(taxDeclarationPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(107986, "{0}在{1}已经生成过个税申报表，不允许再次生成")
                    .replace("{0}", taxAgentNameMap.get(taxDeclarationPOS.get(0).getTaxAgentId()))
                    .replace("{1}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
        }
        // 查询薪资所属月的薪资核算记录
        List<SalaryAcctRecordPO> salaryAcctRecordPOS = listBySalaryMonth(SalaryAcctRecordPO.builder().salaryMonths(salaryMonthDateRange).build());
        // 无薪资核算记录，不允许生成个税申报表
        if (CollectionUtils.isEmpty(salaryAcctRecordPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98874, "{0}无申报数据").replace("{0}", saveParam.getSalaryMonth().toString()));
        }
        // 查询薪资核算结果
        List<SalaryAcctResultPO> salaryAcctResultPOS = getSalaryAcctResultService(user)
                .listBySalaryAcctRecordIdsAndTaxAgentIds(SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getId), taxAgentIds);

        // 无薪资核算结果，不允许生成个税申报表
        if (CollectionUtils.isEmpty(salaryAcctResultPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(110093, "{0}无可申报数据")
                    .replace("{0}",  SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
        }

        Set<Long> salaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryAcctRecordId);
        salaryAcctRecordPOS = salaryAcctRecordPOS.stream().filter(salaryAcctRecordPO -> salaryAcctRecordIds.contains(salaryAcctRecordPO.getId())).collect(Collectors.toList());
        // 如果存在未归档的，也不允许生成个税申报表
        boolean notArchived = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue()));
        if (notArchived) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98875, "{0}有未归档数据，请全部归档后再申报")
                    .replace("{0}",  SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
        }
        // 如果当前薪资所属月下存在不同的税款所属期，属于异常业务场景，不允许生成个税申报表
        Date taxCycle = salaryAcctRecordPOS.get(0).getTaxCycle();
        boolean differentTaxCycle = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> salaryAcctRecordPO.getTaxCycle().compareTo(taxCycle) != 0);
        if (differentTaxCycle) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98876, "{0}存在不同的税款所属期，无法正常生成个税申报表，请调整账套设置，重新核算后再生成个税申报表")
                    .replace("{0}",  SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
        }
        // 查询薪资账套
        Set<Long> salarySobIds = SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getSalarySobId);
        List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByIds(salarySobIds);

        // 查询所有薪资项目
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listAll();


        // 处理要保存的数据
        TaxDeclarationBO.Result result = TaxDeclarationBO.handle(saveParam, taxCycle, user, salaryItemPOS, salarySobPOS, salaryAcctResultPOS);
        // 保存个税申报表
        if (CollectionUtils.isNotEmpty(result.getNeedInsertTaxDeclarations())) {
            if (isLog) {
                log.info("salary TaxDeclaration step1 save {}", result.getNeedInsertTaxDeclarations().size());
            }
            getTaxDeclarationMapper().batchInsert(result.getNeedInsertTaxDeclarations());
        }
        // 保存个税申报表明细
        if (CollectionUtils.isNotEmpty(result.getNeedInsertTaxDeclarationDetails())) {
            if (isLog) {
                log.info("salary TaxDeclaration step2 detail save {}", result.getNeedInsertTaxDeclarationDetails().size());
            }
            getTaxDeclarationDetailService(user).batchSave(result.getNeedInsertTaxDeclarationDetails());
        }
        // 保存累计情况
        if (CollectionUtils.isNotEmpty(result.getNeedInsertAddUpSituations())) {
            if (isLog) {
                log.info("salary TaxDeclaration step3 AddUpSituations save {}", result.getNeedInsertAddUpSituations().size());
            }
            getAddUpSituationService(user).deleteByTaxYearMonthAndTaxAgentIds(SalaryDateUtil.localDate2YearMonth(taxCycle), taxAgentIds);
            getAddUpSituationService(user).batchSave((List) result.getNeedInsertAddUpSituations());
        }
        // 更新薪资核算记录的状态
        if (isLog) {
            log.info("salary TaxDeclaration step4 AcctRecordStatus save {}", salaryAcctRecordIds.size());
        }
        getSalaryAcctRecordService(user).updateStatusByIds(salaryAcctRecordIds, SalaryAcctRecordStatusEnum.DECLARED);

        // 记录日志
        result.getNeedInsertTaxDeclarations().stream().forEach(declare -> {
            String taxAgentName = taxAgentNameMap.getOrDefault(declare.getTaxAgentId(), "");
            String targetName = SalaryDateUtil.getFormatYearMonth(declare.getSalaryMonth()) + " " + taxAgentName + " " + IncomeCategoryEnum.parseByValue(declare.getIncomeCategory()).getDefaultLabel();
            LoggerContext<TaxDeclarationPO> loggerContext = new LoggerContext<>();
            loggerContext.setUser(user);
            loggerContext.setTargetId(declare.getId().toString());
            loggerContext.setTargetName(targetName);
            loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
            loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel( 0, "生成个税申报表"));
            loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel( 0, "生成个税申报表"));
            loggerContext.setNewValues(declare);
            SalaryElogConfig.taxDeclarationLoggerTemplate.write(loggerContext);
        });
    }

    @Override
    public void delete(SalaryAcctRecordPO salaryAcctRecordPO) {
        SalarySobPO sobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        if (sobPO == null || sobPO.getTaxAgentId() == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "账套信息异常"));
        }

        // 薪资所属月的日期范围
        LocalDateRange taxCycleDateRange = SalaryDateUtil.localDate2Range(salaryAcctRecordPO.getTaxCycle());
        if (Objects.isNull(taxCycleDateRange)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }
        List<TaxDeclarationPO> taxDeclarationPOS = listByTaxCycleAndTaxAgentIds(SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getTaxCycle()), Collections.singleton(sobPO.getTaxAgentId()));

        Set<Long> taxDeclarationIds = SalaryEntityUtil.properties(taxDeclarationPOS, TaxDeclarationPO::getId);
        if (CollectionUtils.isNotEmpty(taxDeclarationIds)) {
            // 删除个税申报表
            getTaxDeclarationMapper().deleteByIds(taxDeclarationIds);
            // 删除个税申报表详情
            getTaxDeclarationDetailService(user).deleteByTaxDeclarationIds(taxDeclarationIds);
        }

        // 删除往期累计情况
        getAddUpSituationService(user).deleteAddUpSituationList(salaryAcctRecordPO.getTaxCycle(), sobPO.getTaxAgentId());
    }


    public List<TaxDeclarationPO> listBySalaryMonthTax(TaxDeclarationPO build) {
        return getTaxDeclarationMapper().listSome(build);
    }

    public List<SalaryAcctRecordPO> listBySalaryMonth(SalaryAcctRecordPO po) {
        return getSalaryAcctRecordMapper().listSome(po);
    }

    @Override
    public boolean checkByAuthority(TaxDeclarationPO taxDeclarationPO, Long employeeId) {
        // 判断是否开启了分权
        Boolean openDevolution = getTaxAgentService(user).isOpenDevolution();
        // 判断是否是总管理员
        Boolean isChief = getTaxAgentService(user).isChief(employeeId);
        if (openDevolution && !isChief) {
            // 查询负责管理的个税扣缴义务人
            Collection<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listAllTaxAgentsAsAdmin(employeeId);
            if (CollectionUtils.isEmpty(taxAgentPOS)) {
                return false;
            }
            Set<Long> taxAgentIds = SalaryEntityUtil.properties(taxAgentPOS, TaxAgentPO::getId);
            return taxAgentIds.contains(taxDeclarationPO.getTaxAgentId());
        }
        // 查询个税申报表
        return true;
    }

    @Override
    public void withDrawTaxDeclaration(Long taxDeclarationId) {
        TaxDeclarationPO po = getTaxDeclarationMapper().getById(taxDeclarationId);
        if(Objects.isNull(po)){
            throw new SalaryRunTimeException("个税申报表不存在");
        }
        // 获取当前个税扣缴义务人下的薪资账套
        List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByTaxAgentId(po.getTaxAgentId());
        List<Long> salarySobIds = salarySobPOS.stream().map(SalarySobPO::getId).collect(Collectors.toList());
        // 获取记录
        LocalDateRange dateRange = new LocalDateRange(po.getSalaryMonth(), po.getSalaryMonth());
        List<SalaryAcctRecordPO> salaryAcctRecords = getSalaryAcctRecordService(user).listBySalarySobIdsAndSalaryMonth(salarySobIds, dateRange);
        List<Long> salaryAcctRecordIds = salaryAcctRecords.stream().map(SalaryAcctRecordPO::getId).collect(Collectors.toList());
        // 删除个税申报表
        getTaxDeclarationMapper().deleteByIdZj(po.getId());
        // 修改薪资核算记录状态为已归档
        if(CollectionUtils.isNotEmpty(salaryAcctRecordIds)){
            getSalaryAcctRecordService(user).updateStatusByIds(salaryAcctRecordIds,SalaryAcctRecordStatusEnum.ARCHIVED);
        }

        // 查询个税扣缴义务人名称
        String bar = "_";
        TaxAgentPO taxAgentPO = getTaxAgentService(user).getById(po.getTaxAgentId());
        String targetName = SalaryDateUtil.getFormatYearMonth(po.getSalaryMonth()) + bar + taxAgentPO.getName()  + bar + IncomeCategoryEnum.parseByValue(po.getIncomeCategory()).getDefaultLabel();
        // 记录日志
        LoggerContext<TaxDeclarationPO> loggerContext = new LoggerContext<>();
        loggerContext.setUser(user);
        loggerContext.setTargetId(taxDeclarationId.toString());
        loggerContext.setTargetName(targetName);
        loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
        loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel( 0, "撤回个税申报表"));
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel( 0, "撤回个税申报表"));
        SalaryElogConfig.taxDeclarationLoggerTemplate.write(loggerContext);
    }
}
