package com.fjhx.service.contract.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.contract.Contract;
import com.fjhx.entity.contract.ContractPay;
import com.fjhx.enums.ContractPayStatusEnum;
import com.fjhx.enums.ContractPayTypeEnum;
import com.fjhx.enums.ContractStatusEnum;
import com.fjhx.mapper.contract.ContractPayMapper;
import com.fjhx.params.contract.ContractPayVo;
import com.fjhx.params.contract.DateStatisticsResult;
import com.fjhx.params.contract.SupplierInfo;
import com.fjhx.params.contract.SupplierStatisticsResult;
import com.fjhx.service.contract.ContractPayService;
import com.fjhx.service.contract.ContractService;
import com.fjhx.utils.BigDecimalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 合同付款 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-29
 */
@Service
public class ContractPayServiceImpl extends ServiceImpl<ContractPayMapper, ContractPay> implements ContractPayService {
    // 上半月
    private static final String THE_FIRST_HALF_MONTH = "1";
    // 下半月
    private static final String THE_SECOND_HALF_MONTH = "2";

    // 历史账期
    private static final Integer HISTORICAL_ACCOUNTING_PERIOD = 1;
    // 当前账期
    private static final Integer CURRENT_ACCOUNTING_PERIOD = 2;
    // 未来账期
    private static final Integer FUTURE_ACCOUNTING_PERIOD = 3;

    @Autowired
    private ContractService contractService;

    @Override
    public void add(ContractPayVo contractPayVo) {
        save(contractPayVo);
    }

    @Override
    public void edit(ContractPayVo contractPayVo) {
        updateById(contractPayVo);
    }

    @Override
    public void delete(ContractPayVo contractPayVo) {
        removeById(contractPayVo.getId());
    }

    @Override
    public Map<Integer, DateStatisticsResult> dateStatistics() {

        Date date = new Date();

        // 初始化返回结果
        Map<Integer, DateStatisticsResult> result = initDateStatisticsResult(date);

        // 获取合同完成但未付款的数据
        List<Contract> contractList = contractService.list(Wrappers.<Contract>lambdaQuery()
                .select(Contract::getAdjustAmount, Contract::getBackAmount, Contract::getHadPayAmount,
                        Contract::getExpressAmount, Contract::getCompleteTime, Contract::getAccountDate)
                .lt(Contract::getPayStatus, ContractPayStatusEnum.COMPLETED.getType())
                .and(q -> q.eq(Contract::getAccountDate, 0)
                        .or(w -> w.gt(Contract::getAccountDate, 0)
                                .eq(Contract::getStatus, ContractStatusEnum.COMPLETED.getType()))));

        // 赋值合同未付款
        for (Contract contract : contractList) {

            // 未付金额 = 质检金额 - 退货金额 - 已付金额 - 运费
            BigDecimal notHadPayAmount = contract.getAdjustAmount().subtract(contract.getBackAmount())
                    .subtract(contract.getHadPayAmount()).subtract(contract.getExpressAmount());

            // 付款日期
            Date paymentDate = DateUtil.offsetDay(contract.getCompleteTime(), contract.getAccountDate());

            for (DateStatisticsResult dateStatisticsResult : result.values()) {
                if (paymentDate.compareTo(dateStatisticsResult.getEndTime()) < 0) {

                    // 如果统计历史账期未付款,顺延到当前账期之中
                    DateStatisticsResult item = HISTORICAL_ACCOUNTING_PERIOD.equals(dateStatisticsResult.getType())
                            ? result.get(2) : dateStatisticsResult;

                    // 未付有账期
                    if (contract.getAccountDate() > 0) {
                        item.setUnpaidAccountingPeriod(item.getUnpaidAccountingPeriod().add(notHadPayAmount));
                    }

                    break;
                }
            }
        }

        // 获取本月付款记录
        List<ContractPay> contractPayList = list(Wrappers.<ContractPay>lambdaQuery()
                .select(StorageBaseEntity::getCreateTime, ContractPay::getType, ContractPay::getPayAmount)
                .between(StorageBaseEntity::getCreateTime, DateUtil.beginOfMonth(date), DateUtil.endOfMonth(date)));

        // 分别统计出上半月和下班月的付款
        Map<String, Map<Integer, BigDecimal>> collect = contractPayList.stream().collect(Collectors.groupingBy(
                contractPay -> DateUtil.betweenDay(contractPay.getCreateTime(), date, true) < 15 ? THE_FIRST_HALF_MONTH : THE_SECOND_HALF_MONTH,
                Collectors.toMap(ContractPay::getType, ContractPay::getPayAmount, BigDecimal::add)
        ));

        // 赋值上半月付款
        Map<Integer, BigDecimal> theFirstHalfMonthMap = collect.get(THE_FIRST_HALF_MONTH);
        if (theFirstHalfMonthMap != null) {
            DateStatisticsResult dateStatisticsResult = result.get(1);
            dateStatisticsResult.setPaidPayInAdvance(theFirstHalfMonthMap.get(ContractPayTypeEnum.ADVANCE_CHARGE.getType()));
            dateStatisticsResult.setPaidAccountingPeriod(theFirstHalfMonthMap.get(ContractPayTypeEnum.ACCOUNTING_PERIOD.getType()));
        }

        // 赋值下半月付款
        Map<Integer, BigDecimal> theSecondHalfMonthMap = collect.get(THE_SECOND_HALF_MONTH);
        if (theSecondHalfMonthMap != null) {
            DateStatisticsResult dateStatisticsResult = result.get(2);
            dateStatisticsResult.setPaidPayInAdvance(theSecondHalfMonthMap.get(ContractPayTypeEnum.ADVANCE_CHARGE.getType()));
            dateStatisticsResult.setPaidAccountingPeriod(theSecondHalfMonthMap.get(ContractPayTypeEnum.ACCOUNTING_PERIOD.getType()));
        }

        return result;
    }

    @Override
    public List<SupplierStatisticsResult> supplierStatistics(Condition condition) {
        // 初始化返回值
        List<SupplierStatisticsResult> result = initSupplierStatisticsResult();

        // 历史账期没有待付款记录
        if (HISTORICAL_ACCOUNTING_PERIOD.equals(condition.getType())) {
            return result;
        }

        QueryWrapper<Object> wrapper = Wrappers.query();

        // 合同筛选
        contractScreen(wrapper);
        // 账期筛选
        accountDateScreen(wrapper, condition);

        wrapper.groupBy("c.supplier_id");

        List<SupplierInfo> supplierInfoList = baseMapper.getSupplierInfoList(wrapper);

        if (supplierInfoList.size() == 0) {
            return result;
        }

        Map<Integer, List<SupplierInfo>> collect = supplierInfoList.stream().collect(Collectors.groupingBy(
                supplierInfo -> {
                    BigDecimal totalAmount = supplierInfo.getTotalAmount();
                    if (totalAmount.compareTo(new BigDecimal("500000")) > 0) {
                        return 1;
                    }
                    if (totalAmount.compareTo(new BigDecimal("200000")) > 0) {
                        return 2;
                    }
                    if (totalAmount.compareTo(new BigDecimal("100000")) > 0) {
                        return 3;
                    }
                    if (totalAmount.compareTo(new BigDecimal("50000")) > 0) {
                        return 4;
                    }
                    return 5;
                }
        ));

        result.get(0).setSupplierInfoList(supplierInfoList);
        collect.forEach((k, v) -> result.get(k).setSupplierInfoList(v));

        return result;
    }

    @Override
    public Map<String, Object> statusStatistics(Condition condition) {
        // 历史账期没有待付款记录
        if (HISTORICAL_ACCOUNTING_PERIOD.equals(condition.getType())) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("canLaunch", 0);
            map.put("needSupplemented", 0);
            map.put("total", 0);
            return map;
        }

        QueryWrapper<Object> wrapper = Wrappers.query();

        // 合同筛选
        contractScreen(wrapper);
        // 账期筛选
        accountDateScreen(wrapper, condition);
        // 供应商筛选
        supplierIdScreen(wrapper, condition);

        return baseMapper.statusStatistics(wrapper);
    }

    @Override
    public Map<String, Object> cycleStatistics(Condition condition) {

        QueryWrapper<Object> wrapper = Wrappers.query();

        // 合同筛选
        contractScreen(wrapper);
        // 账期筛选
        accountDateScreen(wrapper, condition);
        // 供应商筛选
        supplierIdScreen(wrapper, condition);
        // 合同状态筛选
        statusScreen(wrapper, condition);

        Date date = new Date();

        Date currentAccountBeginTime = getCurrentAccountBeginTime(date);
        Date currentAccountEndTime = getCurrentAccountEndTime(date);

        return baseMapper.cycleStatistics(wrapper, currentAccountBeginTime, currentAccountEndTime);
    }

    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {

        // 历史账期没有待付款记录
        if (HISTORICAL_ACCOUNTING_PERIOD.equals(condition.getType())) {
            return new Page<>();
        }

        Date date = new Date();

        QueryWrapper<Object> wrapper = Wrappers.query();

        // 合同筛选
        contractScreen(wrapper);
        // 账期筛选
        accountDateScreen(wrapper, condition);
        // 供应商筛选
        supplierIdScreen(wrapper, condition);
        // 合同状态筛选
        statusScreen(wrapper, condition);
        // 周期筛选
        cycleScreen(wrapper, condition, date);

        // 关键字查询
        String keyword = condition.getKeyword();
        wrapper.and(ObjectUtil.isNotEmpty(keyword), q -> q
                .like("c.code", keyword).or()
                .like("s.name", keyword).or()
                .like("m.name", keyword)
        );

        Page<Map<String, Object>> page = baseMapper.getPage(condition.getPage(), wrapper);

        for (Map<String, Object> record : page.getRecords()) {
            // 赋值付款期限
            setTermsPayment(date, record);
            // 质检是否完成
            record.put("qualityTestingComplete", ObjectUtil.equals(record.get("batchCount"), record.get("qualityTestingCount")));
            // 合同是否完成
            record.put("contractComplete", Convert.toInt(record.get("contractFileCount")) > 0);
            // 发票是否完成
            record.put("invoiceComplete", BigDecimalUtil.ge(record.get("invoiceAmount"), record.get("adjustAmount")));
        }

        return page;
    }

    /**
     * 合同付款时间段统计返回值初始化
     *
     * @param date 当前日期
     */
    private Map<Integer, DateStatisticsResult> initDateStatisticsResult(Date date) {

        Map<Integer, DateStatisticsResult> result = new LinkedHashMap<>();

        // 本月1号
        Date beginOfMonth = DateUtil.beginOfMonth(date);
        // 本月15号
        Date midOfMonth = DateUtil.offsetDay(beginOfMonth, 14);
        // 下月1号
        Date nextBeginOfMonth = DateUtil.offsetMonth(beginOfMonth, 1);
        // 下月15号
        Date nextMidOfMonth = DateUtil.offsetMonth(midOfMonth, 1);
        // 下下月1号
        Date nextTwoBeginOfMonth = DateUtil.offsetMonth(beginOfMonth, 2);
        // 下下月15号
        Date nextTwoMidOfMonth = DateUtil.offsetMonth(midOfMonth, 2);

        // 距离月初有几天
        long day = DateUtil.betweenDay(date, beginOfMonth, true);

        // 当前月和未来2个月一共6个账期
        result.put(1, getDateStatisticsResult(day > 15 ? HISTORICAL_ACCOUNTING_PERIOD : CURRENT_ACCOUNTING_PERIOD,
                beginOfMonth, DateUtil.endOfDay(midOfMonth)));

        result.put(2, getDateStatisticsResult(day > 15 ? CURRENT_ACCOUNTING_PERIOD : FUTURE_ACCOUNTING_PERIOD,
                DateUtil.offsetDay(midOfMonth, 1), DateUtil.endOfMonth(date)));

        result.put(3, getDateStatisticsResult(FUTURE_ACCOUNTING_PERIOD,
                nextBeginOfMonth, DateUtil.endOfDay(nextMidOfMonth)));

        result.put(4, getDateStatisticsResult(FUTURE_ACCOUNTING_PERIOD,
                DateUtil.offsetDay(nextMidOfMonth, 1), DateUtil.endOfMonth(nextBeginOfMonth)));

        result.put(5, getDateStatisticsResult(FUTURE_ACCOUNTING_PERIOD,
                nextTwoBeginOfMonth, DateUtil.endOfDay(nextTwoMidOfMonth)));

        result.put(6, getDateStatisticsResult(FUTURE_ACCOUNTING_PERIOD,
                DateUtil.offsetDay(nextTwoMidOfMonth, 1), DateUtil.endOfMonth(nextTwoBeginOfMonth)));

        return result;
    }

    /**
     * 初始化供应商统计结果集
     */
    private List<SupplierStatisticsResult> initSupplierStatisticsResult() {
        List<SupplierStatisticsResult> result = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            SupplierStatisticsResult supplierStatisticsResult = new SupplierStatisticsResult();
            supplierStatisticsResult.setTitleType(i);
            result.add(supplierStatisticsResult);
        }
        return result;
    }

    /**
     * 生成返回结果集
     */
    private DateStatisticsResult getDateStatisticsResult(Integer type, Date beginTime, Date endTime) {
        DateStatisticsResult dateStatisticsResult = new DateStatisticsResult();
        dateStatisticsResult.setType(type);
        dateStatisticsResult.setShowDate(DateUtil.format(endTime, "MM月dd日"));
        dateStatisticsResult.setBeginTime(beginTime);
        dateStatisticsResult.setEndTime(endTime);
        return dateStatisticsResult;
    }

    /**
     * 赋值付款期限
     */
    private void setTermsPayment(Date date, Map<String, Object> record) {
        // 完成时间
        Date completeTime = Convert.toDate(record.get("completeTime"));
        // 账期
        Integer accountDate = Convert.toInt(record.get("accountDate"));

        long day = completeTime == null ? 0 : DateUtil.betweenDay(completeTime, date, true);

        // 赋值付钱期限(距离账期还要多少天,负值为超过付款日期多少天)
        record.put("termsPayment", accountDate - day);
    }

    // 付款时间：最后入库时间 + 账期
    private static final String whereTime = "date_add( c.complete_time , interval c.account_date day)";

    /**
     * 合同筛选
     */
    private void contractScreen(QueryWrapper<Object> wrapper) {
        wrapper.eq("c.del_flag", StatusConstant.NOT_DELETED)
                .lt("c.pay_status", ContractPayStatusEnum.COMPLETED.getType())
                .and(q -> q.eq("c.account_date", 0)
                        .or(w -> w.gt("c.account_date", 0)
                                .eq("c.status", ContractStatusEnum.COMPLETED.getType())));
    }

    /**
     * 账期筛选
     */
    private void accountDateScreen(QueryWrapper<Object> wrapper, Condition condition) {

        Integer type = condition.getType();
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

        wrapper.func(q -> {
            if (CURRENT_ACCOUNTING_PERIOD.equals(type)) {
                q.le(whereTime, endTime);
            } else if (FUTURE_ACCOUNTING_PERIOD.equals(type)) {
                q.between(whereTime, beginTime, endTime);
            }
        });
    }

    /**
     * 供应商筛选
     */
    private void supplierIdScreen(QueryWrapper<Object> wrapper, Condition condition) {
        Long supplierId = condition.getLong("supplierId");
        wrapper.eq(ObjectUtil.isNotEmpty(supplierId), "c.supplier_id", supplierId);
    }

    /**
     * 合同状态筛选
     */
    private void statusScreen(QueryWrapper<Object> wrapper, Condition condition) {
        Integer status = condition.getStatus();
        wrapper.func(q -> {
            // 可发起
            if (ObjectUtil.equals(status, 1)) {
                q.apply("c.apply_pay_amount = 0 " +
                        "and t.batchCount = t.qualityTestingCount " +
                        "and t.contractFileCount > 0 " +
                        "and c.invoice_amount >= c.adjust_amount");
            }
            // 待补齐
            else if (ObjectUtil.equals(status, 2)) {
                q.apply("t.batchCount != t.qualityTestingCount " +
                        "or t.contractFileCount <= 0 " +
                        "or c.invoice_amount < c.adjust_amount");
            }
        });
    }

    /**
     * 周期筛选
     */
    private void cycleScreen(QueryWrapper<Object> wrapper, Condition condition, Date date) {

        Integer cycleType = condition.getInt("cycleType");

        wrapper.func(q -> {
            // 本周期
            if (ObjectUtil.equals(cycleType, 1)) {
                q.between(whereTime, getCurrentAccountBeginTime(date), getCurrentAccountEndTime(date));
            }
            // 上周期顺延
            else if (ObjectUtil.equals(cycleType, 2)) {
                q.lt(whereTime, getCurrentAccountBeginTime(date));
            }
            // 其他(未来账期)
            else if (ObjectUtil.equals(cycleType, 3)) {
                q.gt(whereTime, getCurrentAccountEndTime(date));
            }
        });
    }


    /**
     * 获取当前账期开始时间
     */
    private Date getCurrentAccountBeginTime(Date date) {
        // 本月1号
        DateTime beginOfMonth = DateUtil.beginOfMonth(date);
        // 距离月初有几天
        long day = DateUtil.betweenDay(date, beginOfMonth, true);

        return day > 15 ? DateUtil.offsetDay(beginOfMonth, 15) : beginOfMonth;
    }

    /**
     * 获取当前账期结束时间
     */
    private Date getCurrentAccountEndTime(Date date) {
        // 本月1号
        DateTime beginOfMonth = DateUtil.beginOfMonth(date);
        // 距离月初有几天
        long day = DateUtil.betweenDay(date, beginOfMonth, true);

        return day > 15 ? DateUtil.endOfMonth(date) : DateUtil.endOfDay(DateUtil.offsetDay(beginOfMonth, 14));
    }


}
