package com.bonus.material.push.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.bonus.common.biz.config.DateTimeHelper;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.material.push.domain.IwsCostPushBean;
import com.bonus.material.push.mapper.IwsCostPushMapper;
import com.bonus.material.push.service.IwsCostPushService;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.vo.SltInfoVo;
import com.bonus.material.settlement.service.ISltAgreementInfoService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author : 阮世耀
 * @version : 1.0
 * @PackagePath: com.bonus.material.push.service.impl
 * @CreateTime: 2025-01-23  14:48
 * @Description: 描述
 */
@Service
public class IwsCostPushServiceImpl implements IwsCostPushService {

    @Resource
    private IwsCostPushMapper iwsCostPushMapper;

    @Resource
    private ISltAgreementInfoService sltAgreementInfoService;


    /**
     * 根据给定的条件查找协议信息
     *
     * @param o IwsCostPushBean对象，包含查找协议所需的条件
     * @return 返回一个IwsCostPushBean列表，包含符合查找条件的协议信息
     */
    @Override
    public List<IwsCostPushBean> findAgreement(IwsCostPushBean o) {
        List<IwsCostPushBean> pushBeanList = iwsCostPushMapper.findAgreement(o);

        // 使用流式操作过滤掉null值，并设置isMatch属性
        return pushBeanList.stream()
                .filter(Objects::nonNull)
                .peek(bean -> {
                    if (StringUtils.isNotBlank(bean.getProjectId()) && StringUtils.isNotBlank(bean.getProjectCode())) {
                        bean.setIsMatch((byte) 1);
                    }
                })
                .collect(Collectors.toList());
    }

    /**
     * 查询费用推送审核数据列表
     */
    @Override
    public List<IwsCostPushBean> getCostPushCheckList(IwsCostPushBean o) {
        return iwsCostPushMapper.getCostPushCheckList(o);
    }


    /**
     * 计算月结费用
     *
     * @param costDto IwsCostPushBean对象，包含月结费用计算的所需信息
     * @return 返回一个AjaxResult对象，包含月结费用计算的结果
     */
    @Override
    public AjaxResult computeTheMonthCost(IwsCostPushBean costDto) {
        // 获取当前年月 例如:2025-01
        String month = DateTimeHelper.getNowMonth();
        // 获取当月第一天的日期和最后一天日期
        LocalDateTime firstDayOfMonth = getFirstDayOfMonth(month);
        LocalDateTime lastDayOfMonth = getLastDayOfMonthZ(month);

        costDto.setMonth(month);

        // 清除旧的费用计算数据
        // cleanSameMonthOldRecords(record);

        // 插入新的当前月份,并生成记录id
        iwsCostPushMapper.insertCalcMonthRecord(costDto);
        // 查询协议信息列表
        List<IwsCostPushBean> agreementList = iwsCostPushMapper.findAgreement(costDto);
        // 处理数据,计算4项费用
        computeMonthInfoDetail(agreementList, month, firstDayOfMonth, lastDayOfMonth);
        // 存入数据库
        agreementList.forEach(item -> item.setMonth(month));
        int addProjectMonthCostsMapperResult = iwsCostPushMapper.insertProjectMonthCosts(agreementList);

        return 0 < addProjectMonthCostsMapperResult ? AjaxResult.success("月结费用计算成功") : AjaxResult.error("月结费用计算失败");
    }

    /**
     * 根据协议ID及月份查询当月租赁费用明细
     *
     * @param record 传入查询的年月：yyyy-MM 及 协议ID
     */
    @Override
    public List<IwsCostPushBean> getLeaseCostsByAgreementIdAndMonth(IwsCostPushBean record) {
        if (Objects.isNull(record) || Objects.isNull(record.getMonth()) || Objects.isNull(record.getAgreementId())) {
            System.err.println("查询信息为空 = " + record);
            return Collections.emptyList();
        }

        // 获取前端传入年月对应的第一天与最后一天 LocalDateTime对象
        LocalDateTime queryStartTime = getFirstDayOfMonth(record.getMonth());
        LocalDateTime queryEndTime = getLastDayOfMonthZ(record.getMonth());

        List<IwsCostPushBean> theMonthAgreementLeaseList = iwsCostPushMapper.getPaidSltBaseInfo(record);
        if (CollectionUtils.isEmpty(theMonthAgreementLeaseList)) {
            return Collections.emptyList();
        } else {
            // 过滤异常数据
            theMonthAgreementLeaseList.removeIf(item ->
                    Objects.isNull(item) ||
                    Objects.isNull(item.getTypeId()) ||
                    Objects.isNull(item.getLeaseNum()) ||
                    Objects.isNull(item.getLeasePrice())
            );
            for (IwsCostPushBean leaseCostInfo : theMonthAgreementLeaseList) {
                if (null == leaseCostInfo.getLeaseDate()) { continue; }
                if (null == leaseCostInfo.getBackDate()) { leaseCostInfo.setBackDate(queryEndTime); }

                // 校准计算后的日期
                if (leaseCostInfo.getLeaseDate().isBefore(queryStartTime)) {
                    leaseCostInfo.setLeaseDate(queryStartTime);
                } else if (leaseCostInfo.getLeaseDate().isAfter(queryEndTime)) {
                    leaseCostInfo.setLeaseDays(0);
                    leaseCostInfo.setDelFlag(true);
                    continue;
                }
                if (leaseCostInfo.getBackDate().isAfter(queryEndTime)) {
                    leaseCostInfo.setBackDate(queryEndTime);
                } else if (leaseCostInfo.getBackDate().isBefore(queryStartTime)) {
                    leaseCostInfo.setLeaseDays(0);
                    leaseCostInfo.setDelFlag(true);
                    continue;
                }

                // 计算天数
                leaseCostInfo.setLeaseDays(Math.toIntExact(calculateDaysInclusive(leaseCostInfo.getLeaseDate(), leaseCostInfo.getBackDate())));
                // 计算费用
                leaseCostInfo.setLeaseMoney(new BigDecimal(leaseCostInfo.getLeaseDays())
                        .multiply(leaseCostInfo.getLeasePrice())
                        .multiply(leaseCostInfo.getLeaseNum()).setScale(2, RoundingMode.HALF_UP)
                );

            }

            // 删除标记的非期间段数据
            theMonthAgreementLeaseList.removeIf(IwsCostPushBean::isDelFlag);
            return theMonthAgreementLeaseList;
        }
    }

    /**
     * 计算月结费用详情
     * @param agreementList 协议集合
     * @param firstDay 月份第一天
     * @param lastDay 月份最后一天
     */
    private void computeMonthInfoDetail(List<IwsCostPushBean> agreementList, String month, LocalDateTime firstDay, LocalDateTime lastDay) {
        if (null == agreementList || agreementList.isEmpty()) {
            System.err.println("---------agreementList为空----------");
        } else {
            System.out.println("agreementList数据不为空");

            // 过滤异常数据
            agreementList.removeIf(item ->
                            Objects.isNull(item) ||
                            Objects.isNull(item.getAgreementId()) ||
                            Objects.isNull(item.getSignTime()) ||
                            Objects.isNull(item.getIsSettlement()) ||
                            item.getSignTime().isAfter(lastDay) || item.getSettlementTime().isBefore(firstDay)
            );

            for (IwsCostPushBean agreement : agreementList) {
                // 协议id
                String agreementId = agreement.getAgreementId();
                // 更新协议推送状态
                updatePushStatus(agreement);

                // 对已结算、未结算分开处理
                if (0 == agreement.getIsSettlement()) {
                    // 未结算时，无丢失、维修、报废费用
                    agreement.setLostMoney(BigDecimal.ZERO);
                    agreement.setRepairMoney(BigDecimal.ZERO);
                    agreement.setScrapMoney(BigDecimal.ZERO);

                    // 查询领料费用
                    List<IwsCostPushBean> leaseCostsByAgreementIdAndMonth = this.getLeaseCostsByAgreementIdAndMonth(new IwsCostPushBean()
                            .setAgreementId(agreementId)
                            .setMonth(month)
                    );
                    if (CollectionUtils.isEmpty(leaseCostsByAgreementIdAndMonth)) {
                        agreement.setLeaseMoney(BigDecimal.ZERO);
                    } else {
                        leaseCostsByAgreementIdAndMonth.removeIf(item -> Objects.isNull(item) || Objects.isNull(item.getLeaseMoney()));
                        // 合并计算领料费用
                        agreement.setLeaseMoney(leaseCostsByAgreementIdAndMonth
                                .stream()
                                .map(IwsCostPushBean::getLeaseMoney)
                                .reduce(BigDecimal.ZERO, BigDecimal::add)
                        );
                    }
                } else if (1 == agreement.getIsSettlement()) {
                    // 已结算费用计算
                    SltInfoVo sltServiceResult = sltAgreementInfoService.getSltInfo(new SltAgreementInfo().setAgreementId(Long.valueOf(agreementId)));
                    if (Objects.isNull(sltServiceResult)) {
                        System.err.println("sltServiceResult为空----null");
                        agreement.setLostMoney(BigDecimal.ZERO);
                        agreement.setRepairMoney(BigDecimal.ZERO);
                        agreement.setScrapMoney(BigDecimal.ZERO);
                        agreement.setLeaseMoney(BigDecimal.ZERO);
                    } else {
                        // 已经结算时，要统计领料、丢失、维修、报废费用
                        agreement.setLostMoney(Optional.ofNullable(sltServiceResult.getLoseCost()).orElse(BigDecimal.ZERO));
                        agreement.setRepairMoney(Optional.ofNullable(sltServiceResult.getRepairCost()).orElse(BigDecimal.ZERO));
                        agreement.setScrapMoney(Optional.ofNullable(sltServiceResult.getScrapCost()).orElse(BigDecimal.ZERO));
                        agreement.setLeaseMoney(Optional.ofNullable(sltServiceResult.getLeaseCost()).orElse(BigDecimal.ZERO));
                    }
                }

                // 合并统计费用
                agreement.setTotalMoney(agreement.getLeaseMoney()
                        .add(agreement.getLostMoney())
                        .add(agreement.getRepairMoney())
                        .add(agreement.getScrapMoney())
                );
            }
        }
    }


    /**
     * 更新协议推送状态
     *
     * @param bean IwsCostPushBean对象，包含推送状态更新的信息
     */
    private void updatePushStatus(IwsCostPushBean bean) {
        String isPush = checkPush(bean) ? "0" : "1";
        bean.setIsPush(isPush);
        iwsCostPushMapper.updateAgreementIsPush(bean);
    }

    /**
     * 检查是否包含大件及机械化，项目id为空的
     * return true 包含大件、机械化 或 工程id为空,  false 不包含
     */
    private boolean checkPush(IwsCostPushBean o) {
        return (o.getUnitName().contains("大件") || o.getUnitName().contains("机械化") || StringUtils.isEmpty(o.getProjectId()));
    }

    /**
     * 根据字符串形式的年月获取当月的第一天
     *
     * @param yearMonth 字符串形式的年月，格式为 "yyyy-MM" 例如 2024-09
     * @return 当月的第一天的 LocalDateTime 对象，时间部分为 00:00:00
     */
    public static LocalDateTime getFirstDayOfMonth(String yearMonth) {
        // 解析字符串为 LocalDate
        LocalDate date = LocalDate.parse(yearMonth + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 获取当月的第一天
        LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        // 转换为 LocalDateTime，时间部分为 00:00:00
        return firstDay.atStartOfDay();
    }

    /**
     * 根据字符串形式的年月获取当月的最后一天
     *
     * @param yearMonth 字符串形式的年月，格式为 "yyyy-MM" 例如 2024-09
     * @return 当月的最后一天的 LocalDateTime 对象，时间部分为 23:59:59
     */
    public static LocalDateTime getLastDayOfMonthZ(String yearMonth) {
        // 解析字符串为 LocalDate
        LocalDate date = LocalDate.parse(yearMonth + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 获取当月的最后一天
        LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        // 转换为 LocalDateTime，时间部分为 23:59:59
        return lastDay.atTime(23, 59, 59);
    }

    /**
     * 计算两个 LocalDateTime 之间的天数（当天开始和当天结束的情况也算1天）
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 天数差（包括当天）
     */
    public static long calculateDaysInclusive(LocalDateTime start, LocalDateTime end) {
        // 如果结束时间早于开始时间，交换两者
        if (end.isBefore(start)) {
            LocalDateTime temp = start;
            start = end;
            end = temp;
        }
        // 计算完整天数差
        long daysBetween = ChronoUnit.DAYS.between(start.toLocalDate(), end.toLocalDate());
        // 如果结束时间的时间部分晚于或等于开始时间的时间部分，则加 1 天
        if (!end.toLocalTime().isBefore(start.toLocalTime())) {daysBetween += 1;}

        return daysBetween;
    }
}
