package com.yeejoin.amos.boot.module.hygf.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.biz.common.utils.ExcelUtils;
import com.yeejoin.amos.boot.module.hygf.api.Enum.*;
import com.yeejoin.amos.boot.module.hygf.api.dto.DropDown;
import com.yeejoin.amos.boot.module.hygf.api.dto.RepaymentBatchDto;
import com.yeejoin.amos.boot.module.hygf.api.dto.RepaymentDto;
import com.yeejoin.amos.boot.module.hygf.api.entity.PrivilegeCompany;
import com.yeejoin.amos.boot.module.hygf.api.entity.Repayment;
import com.yeejoin.amos.boot.module.hygf.api.entity.ToDoTasks;
import com.yeejoin.amos.boot.module.hygf.api.mapper.JpStationMapper;
import com.yeejoin.amos.boot.module.hygf.api.mapper.RepaymentMapper;
import com.yeejoin.amos.boot.module.hygf.api.service.IRepaymentService;
import com.yeejoin.amos.boot.module.hygf.biz.vo.RepaymentExcelVO;
import com.yeejoin.amos.boot.module.hygf.biz.vo.RouthPathVO;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.typroject.tyboot.core.rdbms.service.BaseService;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import javax.servlet.http.HttpServletResponse;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 还款实现层
 *
 * @author hzz
 * @date 2024-09-26
 */
@Service
@Slf4j
public class RepaymentServiceImpl extends BaseService<RepaymentDto, Repayment, RepaymentMapper> implements IRepaymentService {
    @Autowired
    ToDoTasksServiceImpl toDoTasksService;
    @Autowired
    RepaymentMapper repaymentMapper;
    @Autowired
    JpStationMapper jpStationMapper;
    @Value("${hygf.sms.repaymentCode}")
    private String smsTempCode;
    @Autowired
    CommonServiceImpl commonService;

    public Page<RepaymentDto> queryForRepaymentPage(String sequenceNbr, String companyName, String loanPeriod,
                                                    String messageState, String repayState, String repayDate, String period, int current, int size) {
        PageHelper.startPage(current, size);
        List<RepaymentDto> list = repaymentMapper.queryPage(sequenceNbr, companyName, loanPeriod, messageState, repayState, repayDate, period);
        PageInfo<RepaymentDto> pages = new PageInfo(list);
        List<RepaymentDto> pagesList = pages.getList();
        if (CollectionUtil.isNotEmpty(pagesList)) {
            //为了前端页面展示
            for (RepaymentDto repaymentDto : pagesList) {
                repaymentDto.setMessageStateStr(RepaymentMessageStateEnum.getByCode(repaymentDto.getMessageState()).getRemark());
                repaymentDto.setRepayStateStr(RepaymentRepayStateEnum.getByCode(repaymentDto.getRepayState()).getRemark());
            }
        }
        Page<RepaymentDto> page = new Page<>();
        page.setCurrent(current);
        page.setTotal(pages.getTotal());
        page.setSize(size);
        page.setRecords(pages.getList());
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    public void batchConfirmMessageState(List<String> sequenceNbrList) {
        if (CollectionUtil.isNotEmpty(sequenceNbrList)) {
            LambdaUpdateWrapper<Repayment> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(Repayment::getMessageState, RepaymentMessageStateEnum.CONFIRM.getCode())
                    .set(Repayment::getCronSendState, RepaymentCronSendStateEnum.UN_SEND.getCode())
                    .set(Repayment::getMessageCommitTime, new Date())
                    .in(Repayment::getSequenceNbr, sequenceNbrList);
            this.update(updateWrapper);
            for (String sequenceNbr : sequenceNbrList) {
                //更新工作台信息
                updateTodoTask(sequenceNbr);
            }
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void batchRepay(List<String> sequenceNbrList) {
        if (CollectionUtil.isNotEmpty(sequenceNbrList)) {
            LambdaUpdateWrapper<Repayment> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(Repayment::getRepayState, RepaymentRepayStateEnum.REPAY.getCode())
                    .set(Repayment::getRepayCommitTime, new Date())
                    .in(Repayment::getSequenceNbr, sequenceNbrList);
            this.update(updateWrapper);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void confirmMessageState(Long sequenceNbr) {
        LambdaUpdateWrapper<Repayment> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Repayment::getMessageState, RepaymentMessageStateEnum.CONFIRM.getCode())
                .set(Repayment::getCronSendState, RepaymentCronSendStateEnum.UN_SEND.getCode())
                .set(Repayment::getMessageCommitTime, new Date())
                .eq(Repayment::getSequenceNbr, sequenceNbr);
        this.update(updateWrapper);
        //更新工作台信息
        updateTodoTask(String.valueOf(sequenceNbr));
    }

    /**
     * 更新工作台信息
     * @param sequenceNbr
     */
    @Async
    private void updateTodoTask(String sequenceNbr) {
        Repayment repayment = repaymentMapper.selectById(sequenceNbr);
        ToDoTasks toDoTasks = new ToDoTasks(TaskTypeStationEnum.还款.getCode(), repayment.getSequenceNbr());
        toDoTasksService.completeToDoTasks(toDoTasks, "");
    }

    @Transactional(rollbackFor = Exception.class)
    public void repay(Long sequenceNbr) {
        LambdaUpdateWrapper<Repayment> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Repayment::getRepayState, RepaymentRepayStateEnum.REPAY.getCode())
                .set(Repayment::getRepayCommitTime, new Date())
                .eq(Repayment::getSequenceNbr, sequenceNbr);
        this.update(updateWrapper);
    }

    /**
     * 定时发送还款信息 15天发送、5天发送、当天发送
     */
    @Scheduled(cron = "${repaymentCron}")
    public void cronSendRepayMessage() {
        execute();
    }

    public void execute() {
        log.info("====================开始执行定时任务===========================");
        List<Repayment> repaymentList = list();
        if (CollectionUtil.isNotEmpty(repaymentList)) {
            for (Repayment repayment : repaymentList) {
                //此处拿出还款日期进行对比15天发送、5天发送、当天发送
                LocalDate repayDate = repayment.getRepayDate();
                LocalDate now = LocalDate.now();
                long betweenDay = ChronoUnit.DAYS.between(now, repayDate);
                Boolean dateSend = betweenDay == 15 || betweenDay == 5 || betweenDay == 0;
                if (dateSend && repayment.getCronSendState() == RepaymentCronSendStateEnum.SEND.getCode()) {
                    //生成待办
                    RouthPathVO routhPathVO = new RouthPathVO();
                    routhPathVO.setRouthPathId(String.valueOf(repayment.getSequenceNbr()));
                    String routhPath = toDoTasksService.getRouthPath(TaskTypeStationEnum.还款.getCode(),HygfRouthTypeEnum.HYGF_REPAY.getCode(), routhPathVO);
                    String wxRouthPath = toDoTasksService.getWxRouthPath(TaskTypeStationEnum.还款.getCode(),HygfRouthTypeEnum.HYGF_REPAY.getCode(), routhPathVO);
                    ToDoTasks toDoTasks = new ToDoTasks(TaskTypeStationEnum.还款.getCode(), repayment.getSequenceNbr(), createTaskName(repayment), repayment.getRegionalCompaniesCode(), routhPath,wxRouthPath);
                    String roleId = toDoTasksService.getRoleIdByName("户用光伏-投融");
                    List<String> todoUserIds = toDoTasksService.getTodoUserIds(repayment.getRegionalCompaniesCode(), roleId);
                    toDoTasksService.addToDoTasksByUserIds(todoUserIds, toDoTasks);
                    if (CollectionUtil.isNotEmpty(todoUserIds)){
                        List<String> todoTelephones = repaymentMapper.getTodoTelephones(todoUserIds);
                        HashMap<String, String> params = new HashMap<>(6);
                        for (String todoTelephone : todoTelephones) {
                            params.put("1",repayment.getCompanyName());
                            params.put("2",repayment.getRent().toString());
                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                            params.put("3", repayment.getRepayDate().format(formatter));
                            params.put("4",repayment.getPeriod());
                            params.put("mobile",todoTelephone);
                            params.put("smsCode", smsTempCode);
                            try {
                                Systemctl.smsClient.sendCommonSms(params).getResult();
                            } catch (Exception e) {
                                log.error(e.getMessage(),e);
                            }
                        }
                    }
                }
                if (betweenDay < 0) {
                    //关闭定时任务
                    LambdaUpdateWrapper<Repayment> updateWrapper = new LambdaUpdateWrapper<>();
                    updateWrapper.set(Repayment::getCronSendState, RepaymentCronSendStateEnum.UN_SEND.getCode())
                            .eq(Repayment::getSequenceNbr, repayment.getSequenceNbr());
                    this.update(updateWrapper);
                }
            }
        }
        log.info("==================定时任务执行完成====================");
    }

    /**
     * 处理Double类型输出为科学计数法
     * @param number
     * @return
     */
    private String dealDoubleToString(Double number) {
        if (!Objects.isNull(number)) {
            DecimalFormat df = new DecimalFormat("#.##");
            return df.format(number);
        }
        return "0.00";
    }

    private String createTaskName(Repayment repayment) {
        StringBuilder taskNameBuilder = new StringBuilder();
        taskNameBuilder.append("您于");
        taskNameBuilder.append(repayment.getRepayDate().getYear());
        taskNameBuilder.append("年");
        taskNameBuilder.append(repayment.getRepayDate().getMonthValue());
        taskNameBuilder.append("月");
        taskNameBuilder.append(repayment.getRepayDate().getDayOfMonth());
        taskNameBuilder.append("日");
        taskNameBuilder.append("之前需要对");
        taskNameBuilder.append(repayment.getCompanyName());
        taskNameBuilder.append("进行还款,");
        taskNameBuilder.append("金额为");
        taskNameBuilder.append(dealDoubleToString(repayment.getRent()));
        taskNameBuilder.append("元");
        return taskNameBuilder.toString();
    }

    @Transactional(rollbackFor = Exception.class)
    public void importRepaymentExcel(MultipartFile file) {
        //获取当前用户权限的公司
        try {
            List<DropDown> region = jpStationMapper.getRegion(null, null);
            if (CollectionUtil.isEmpty(region)) {
                throw new BadRequest("该用户没有公司");
            }
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
            List<List<Object>> read = reader.read(1);
            List<Repayment> repaymentList = new ArrayList<>();
            for (int i = 0; i < read.size(); i++) {
                List<Object> objects = read.get(i);
                checkExcel(objects, i);
                Repayment repayment = new Repayment();
                String companyName = String.valueOf(objects.get(0));
                for (DropDown dropDown : region) {
                    if (companyName.equals(dropDown.getName())) {
                        PrivilegeCompany privilegeCompany = repaymentMapper.getPrivilegeCompanyByOrgCode(dropDown.getOrgCode());
                        if (Objects.nonNull(privilegeCompany)) {
                            repayment.setRegionalCompaniesCode(dropDown.getOrgCode());
                            repayment.setCompanyId(privilegeCompany.getSequenceNbr());
                            repayment.setCompanyName(privilegeCompany.getCompanyName());
                        }
                    }
                }
                if (StrUtil.isEmpty(repayment.getCompanyName())) {
                    throw new BadRequest(companyName + ",该用户没有权限");
                }
                String period = String.valueOf(objects.get(1));
                String repayDate = String.valueOf(objects.get(2));
                String rent = String.valueOf(objects.get(3));
                String interest = String.valueOf(objects.get(4));
                String principal = String.valueOf(objects.get(5));
                String loanPeriod = String.valueOf(objects.get(6));
                repayment.setMessageState(RepaymentMessageStateEnum.UN_CONFIRM.getCode());
                repayment.setCronSendState(RepaymentCronSendStateEnum.SEND.getCode());
                repayment.setRepayState(RepaymentRepayStateEnum.UN_REPAY.getCode());
                repayment.setPeriod(period);
                repayment.setRepayDate(DateUtils.dateParse(repayDate).toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDate());
                repayment.setRent(Double.valueOf(rent));
                repayment.setInterest(Double.valueOf(interest));
                repayment.setPrincipal(Double.valueOf(principal));
                repayment.setLoanPeriod(loanPeriod);
                repayment.setCreateTime(new Date());
                repaymentList.add(repayment);
            }
            try {
                saveBatch(repaymentList);
            } catch (Exception exception) {
                throw new BadRequest("有重复数据请检查,区域公司、期次、还款日期、放款批次唯一");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BadRequest("导入失败,失败原因[" + e.getMessage() + "]");
        }
    }

    private void checkExcel(List<Object> objects, int i) {
        if (objects.size() != 7) {
            int col = i + 2;
            throw new BadRequest("第" + col + "行数据不符合规范,每一列需要有值");
        }
        String companyName = String.valueOf(objects.get(0));
        if (StrUtil.isEmpty(companyName)) {
            throw new BadRequest("区域公司不能为空");
        }

        String period = String.valueOf(objects.get(1));
        if (StrUtil.isEmpty(period)) {
            throw new BadRequest("期次不能为空");
        }
        String repayDate = String.valueOf(objects.get(2));
        if (StrUtil.isEmpty(repayDate)) {
            throw new BadRequest("还款日期不能为空");
        }
        Date date = DateUtils.dateParse(repayDate);
        if (Objects.isNull(date)) {
            throw new BadRequest("还款日期格式不对");
        }
        String rent = String.valueOf(objects.get(3));
        if (StrUtil.isEmpty(rent)) {
            throw new BadRequest("租金不能为空");
        }
        String interest = String.valueOf(objects.get(4));
        if (StrUtil.isEmpty(interest)) {
            throw new BadRequest("利息不能为空");
        }
        String principal = String.valueOf(objects.get(5));
        if (StrUtil.isEmpty(principal)) {
            throw new BadRequest("本金不能为空");
        }
        checkMoney(rent, interest, principal);
        String loanPeriod = String.valueOf(objects.get(6));
        if (StrUtil.isEmpty(loanPeriod)) {
            throw new BadRequest("放款批次不能为空");
        }
    }

    private void checkMoney(String rent, String interest, String principal) {
        // 定义要匹配的正则表达式
        String regex = "^([0-9]\\d{0,9}|0)([.]?|(\\.\\d{1,2})?)$";
        Pattern pattern = Pattern.compile(regex);
        Matcher rentMatcher = pattern.matcher(rent);
        if (!rentMatcher.find()) {
            throw new BadRequest("租金金额格式不对,小数点后最多保留2位");
        }
        Matcher interestMatcher = pattern.matcher(interest);
        if (!interestMatcher.find()) {
            throw new BadRequest("利息金额格式不对,小数点后最多保留2位");
        }
        Matcher principalMatcher = pattern.matcher(principal);
        if (!principalMatcher.find()) {
            throw new BadRequest("本金金额格式不对,小数点后最多保留2位");
        }
    }

    public void exportRepaymentExcel(List<String> sequenceNbrList, HttpServletResponse response) {
        LambdaQueryWrapper<Repayment> wrapper = new LambdaQueryWrapper<>();
        //如果是空值导出全部
        if (CollectionUtil.isNotEmpty(sequenceNbrList)) {
            wrapper.in(Repayment::getSequenceNbr, sequenceNbrList);
        }
        List<Repayment> repaymentList = list(wrapper);
        if (CollectionUtil.isNotEmpty(repaymentList)) {
            List<RepaymentExcelVO> dataList = new ArrayList<>();
            for (Repayment repayment : repaymentList) {
                RepaymentExcelVO repaymentExcelVO = BeanUtil.copyProperties(repayment, RepaymentExcelVO.class);
                repaymentExcelVO.setRent(dealDoubleToString(repayment.getRent()));
                repaymentExcelVO.setPrincipal(dealDoubleToString(repayment.getPrincipal()));
                repaymentExcelVO.setInterest(dealDoubleToString(repayment.getInterest()));
                dataList.add(repaymentExcelVO);
            }
            try {
                ExcelUtils.exportExcel(dataList, null, "还款管理", RepaymentExcelVO.class, "还款管理" + DateUtils.dateFormat(new Date(), "YYYY-MM-dd-HH-mm") + ".xlsx", response);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new BadRequest("导出失败,失败原因[" + e.getMessage() + "]");
            }
        }

    }


    @Transactional(rollbackFor = Exception.class)
    public void updateBySequenceNbr(Repayment model) {
        PrivilegeCompany privilegeCompany = repaymentMapper.getPrivilegeCompanyByOrgCode(model.getRegionalCompaniesCode());
        if (Objects.nonNull(privilegeCompany)) {
            model.setCompanyId(privilegeCompany.getSequenceNbr());
            model.setCompanyName(privilegeCompany.getCompanyName());
        }
        this.updateById(model);
    }

    @Transactional(rollbackFor = Exception.class)
    public void addOrUpdate(RepaymentDto model) {
        if (Objects.isNull(model.getSequenceNbr())) {
            model.setMessageState(RepaymentMessageStateEnum.UN_CONFIRM.getCode());
            model.setRepayState(RepaymentRepayStateEnum.UN_REPAY.getCode());
            model.setCreateTime(new Date());
        }
        model.setCronSendState(RepaymentCronSendStateEnum.SEND.getCode());
        PrivilegeCompany privilegeCompany = repaymentMapper.getPrivilegeCompanyByOrgCode(model.getRegionalCompaniesCode());
        if (Objects.nonNull(privilegeCompany)) {
            model.setCompanyId(privilegeCompany.getSequenceNbr());
            model.setCompanyName(privilegeCompany.getCompanyName());
        }
        Repayment repayment = BeanUtil.copyProperties(model, Repayment.class);
        try {
            this.saveOrUpdate(repayment);
        } catch (Exception exception) {
            throw new BadRequest("有重复数据请检查,区域公司、期次、还款日期、放款批次唯一");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(RepaymentBatchDto repaymentBatchDto) {
        List<String> sequenceNbrList = repaymentBatchDto.getSequenceNbrList();
        List<Long> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(sequenceNbrList)) {
            for (String s : sequenceNbrList) {
                list.add(Long.valueOf(s));
            }
        }
        deleteBatchSeq(list);
        //删除待办
        toDoTasksService.deleteByBusinessIds(sequenceNbrList);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteBySequenceNbr(Long sequenceNbr) {
        deleteBySeq(sequenceNbr);
        //删除待办
        List<String> sequenceNbrList=new ArrayList<>();
        sequenceNbrList.add(String.valueOf(sequenceNbr));
        toDoTasksService.deleteByBusinessIds(sequenceNbrList);
    }
}
