package common.service.impl;

import common.model.*;
import common.repository.AuthRepository;
import common.repository.ContractBodyRepository;
import common.repository.ContractChangeRepository;
import common.repository.ContractRepository;
import common.service.ShareIncomeService;
import dic.ContractStatusEnum;
import dic.RoleEnum;
import net.sf.json.JSONArray;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ShareIncomeServiceImpl implements ShareIncomeService {

    private final Logger logger = LoggerFactory.getLogger(ShareIncomeServiceImpl.class);

    @Autowired
    private ContractRepository contractRepository;
    @Autowired
    private AuthRepository authRepository;
    @Autowired
    private ContractBodyRepository contractBodyRepository;
    @Autowired
    private ContractChangeRepository contractChangeRepository;

    @Override
    public List<Contract> shareIncomeList(User loginAccount, String startDate, String endDate, String platform, String bodyCode, String serchName) {

        List<Contract> contracts = new ArrayList<>();

        if ("all".equals(bodyCode)) {
            bodyCode = null;
        }

        List<String> financeBodies = null;
        if (RoleEnum.FINANCE.getKey().equals(loginAccount.getRole())) {
            Auth auth = authRepository.findByUser(loginAccount.getId());
            financeBodies = JSONArray.fromObject(auth.getAuthExtend());
        }

        if (!StringUtils.isEmpty(bodyCode) && !StringUtils.isEmpty(serchName)) {

            if (financeBodies != null && !financeBodies.contains(bodyCode)) {
                //沒有权限查看
                return contracts;
            } else {
                contracts = contractRepository.findShareContranctByDate1(startDate, endDate, platform, bodyCode, serchName);
            }

        } else if (!StringUtils.isEmpty(bodyCode)) {
            if (financeBodies != null && !financeBodies.contains(bodyCode)) {
                //沒有权限查看
                return contracts;
            } else {
                contracts = contractRepository.findShareContranctByDate2(startDate, endDate, platform, bodyCode);
            }
        } else if (!StringUtils.isEmpty(serchName)) {
            if (financeBodies != null) {
                contracts = contractRepository.findShareContranctByDateSercheFinace(startDate, endDate, platform, serchName, financeBodies);
            } else {
                contracts = contractRepository.findShareContranctByDateSerche(startDate, endDate, platform, serchName);
            }
        } else {
            if (financeBodies != null) {
                contracts = contractRepository.findShareContranctByDateFinace(startDate, endDate, platform, financeBodies);
            } else {
                contracts = contractRepository.findShareContranctByDate3(startDate, endDate, platform);
            }
        }

        /*contracts = new ArrayList<>();
        contracts.add(contractRepository.findOne(1355L));*/

        List<ContractBody> bodies = contractBodyRepository.findAll();
        Map<String, String> bodiesNameMap = bodies.stream().collect(
                Collectors.toMap(ContractBody::getCode, ContractBody::getName, (v1, v2) -> v1));

        DateTime start = new DateTime(startDate);
        DateTime end = new DateTime(endDate);

        for (Contract contract : contracts) {

//            this.caculateShareIncome(contract, start, end);
            this.shareIncome4Contract(contract, start, end);
            contract.setMyBodyName(bodiesNameMap.get(contract.getMyBodyCode()));
        }

        return contracts;
    }

    public void shareIncome4Contract(Contract contract, DateTime start, DateTime end) {

        DateTime[] selected = new DateTime[]{start, end};
        DateTime[] contractPart = new DateTime[]{
                new DateTime(contract.getStartDate()),
                new DateTime(contract.getEndDate())};//合同开始结束 时间
        DateTime[] usePart = new DateTime[]{
                selected[0].compareTo(contractPart[0]) <= 0 ? contractPart[0] : selected[0],
                selected[1].compareTo(contractPart[1]) >= 0 ? contractPart[1] : selected[1]
        };

        /*DateTime[][] mainTimes = new DateTime[][]{selected, contractPart, usePart};*/

        contract.setIntervalUseDays(getDayRange(contractPart[0], usePart[1]) + 1);

        Long excludTax = contract.getMoney() * 94;//不含税收入*100
        contract.setIncomeExcludingTax(excludTax);
        int contractAllDay = getDayRange(contractPart[0], contractPart[1]) + 1;//合同总天数

        //处理精度
        Long dayShareIncome = new BigDecimal(contract.getMoney() * 94 * 1.0 / contractAllDay)
                .setScale(0, BigDecimal.ROUND_HALF_UP).longValue();//每日分摊收入(值扩大100倍)

        //作废合同处理
        Contract cancleContract = this.cancledShare(contract, contractAllDay, dayShareIncome, contractPart, usePart, selected);
        if (cancleContract != null) {
            return;
        }

        //中止合同处理
        Contract suspendContract = this.suspendShare(contract, contractAllDay, dayShareIncome, contractPart, usePart, selected);
        if (suspendContract != null) {
            return;
        }

        //晚录合同处理
        DateTime create = new DateTime(new DateTime(contract.getCreateTime()).toString("yyyy-MM-dd")); //录入时间点
        DateTime[] creatPoints = new DateTime[]{
                create, //录入日
                create.dayOfMonth().withMinimumValue() //录入月1日
        };

        this.afterContract(contract, contractAllDay, dayShareIncome, contractPart, usePart, selected, creatPoints);


    }

    private void afterContract(Contract contract, int contractAllDay, Long dayShareIncome,
                               DateTime[] contractPart, DateTime[] usePart, DateTime[] selected, DateTime[] creatPoints) {

        int betweenMonth = creatPoints[0].getMonthOfYear() - contractPart[0].getMonthOfYear();
        //时间范围内用于计算分摊金额的天数
        int daysIncom = getDayRange(usePart[0], usePart[1]) + 1;
        //区间分摊总收入
        contract.setIntervaIncomeShare(dayShareIncome * daysIncom);

        Long adjustmentFund = 0L;//调整金

        boolean isLateContract = false; // 是否为晚录合同（为了兼容历史数据 此处做冗余判断）
        if (ContractStatusEnum.LATE.getKey().equals(contract.getStatus())) {
            isLateContract = true;
        } else if (checkLateContract(contractPart[0], creatPoints[0])) {
            isLateContract = true;
        }

        if (isLateContract) {
            contract.setStatus(ContractStatusEnum.LATE.getKey());
        }

        if (!isLateContract || betweenMonth < 1) {
            //非合同晚录
            contract.setAdjustmentFund(0L);
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());

        } else if (selected[1].isBefore(creatPoints[1].plusDays(betweenMonth > 1 ? -1 : 0))) {
            //录入月1号之前 调整金为 0
            contract.setIntervaIncomeShare(0L);
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());
            contract.setStatus(ContractStatusEnum.LATE.getKey());
        } else {
            contract.setStatus(ContractStatusEnum.LATE.getKey());
            //合同晚录
            //所选时间范围内的分摊收入(录入月1号 即creatPoints[1] 开始计算)
            DateTime useStart = creatPoints[1].compareTo(selected[0]) >= 0 ? creatPoints[1] : selected[0];
            daysIncom = getDayRange(useStart, usePart[1]) + 1;
            contract.setIntervaIncomeShare(dayShareIncome * daysIncom);//时间范围内分摊金

            adjustmentFund = (getDayRange(contractPart[0], creatPoints[1].plusDays(-1)) + 1) * dayShareIncome;

            if (checkTwoTime(selected[0], creatPoints[1]) && checkTwoTime(creatPoints[1], selected[1])) {
                //所选时间范围包含 录入月 1 号 显示统计的调整金
                contract.setAdjustmentFund(adjustmentFund);
            } else {
                contract.setAdjustmentFund(0L);
            }
        }

        if (checkTwoTime(contractPart[1], selected[1])) {
            //最后一日分摊金计算处理
            Long lastDay = contract.getIncomeExcludingTax() - adjustmentFund
                    - dayShareIncome * getDayRange(creatPoints[1], contractPart[1]);
            //最后一日 或 包含最后一日 时
            contract.setIntervaIncomeShare(contract.getIntervaIncomeShare() - dayShareIncome + lastDay);

        }

        contract.setIncomeShareAll(contract.getIntervaIncomeShare() + contract.getAdjustmentFund());

    }

    private Contract suspendShare(Contract contract, int contractAllDay, Long dayShareIncome,
                                  DateTime[] contractPart, DateTime[] usePart, DateTime[] selected) {
        if (!ContractStatusEnum.SUSPEND.getKey().equals(contract.getStatus())) {
            return null;
        }

        ContractChange contractChange = contractChangeRepository.findByContentCode(ContractStatusEnum.SUSPEND.getValue(),
                contract.getContractCode());

        if (contractChange == null) {
            contract.setAdjustmentFund(0L);
            contract.setIntervaIncomeShare(0L);
            contract.setIncomeShareAll(0L);
            return contract;
        }
        // 合同终止日 或 作废日
        DateTime cancelDate = new DateTime(contractChange.getDs());

        //中止操作入库时间
        DateTime suspendDate = new DateTime(new DateTime(contractChange.getCreateTime()).toString("yyyy-MM-dd"));

        Long adjustmentFund = 0L;
        if (checkTwoTime(suspendDate, cancelDate)) {
            adjustmentFund = 0L;
        } else {
          /*  adjustmentFund = new BigDecimal((getDayRange(cancelDate, suspendDate) + 1)
                    * contract.getIncomeExcludingTax() * 1.0 / contractAllDay)
                    .setScale(0, BigDecimal.ROUND_HALF_UP).longValue() * -1;*/

            adjustmentFund = (getDayRange(cancelDate, suspendDate) + 1) * dayShareIncome * (-1);
        }

        DateTime usedEnd = usePart[1].compareTo(cancelDate) <= 0 ? usePart[1] : cancelDate;
        int daysIncom = Days.daysBetween(usePart[0], usedEnd).getDays() + 1;
        daysIncom = daysIncom < 0 ? 0 : daysIncom;

        //区间分摊总收入
        contract.setIntervaIncomeShare(dayShareIncome * daysIncom);

        if (checkTwoTime(selected[0], cancelDate) && checkTwoTime(cancelDate, selected[1])) {
            contract.setAdjustmentFund(adjustmentFund);
        } else {
            contract.setAdjustmentFund(0L);
        }

        contract.setIncomeShareAll(contract.getIntervaIncomeShare() + contract.getAdjustmentFund());

        return contract;
    }

    private Contract cancledShare(Contract contract, int contractAllDay, Long dayShareIncome,
                                  DateTime[] contractPart, DateTime[] usePart, DateTime[] selected) {

        if (!ContractStatusEnum.CANCEL.getKey().equals(contract.getStatus())) {
            return null;
        }

        ContractChange contractChange = contractChangeRepository.findByContentCode(ContractStatusEnum.CANCEL.getValue(), contract.getContractCode());

        if (contractChange == null) {
            contract.setAdjustmentFund(0L);
            contract.setIntervaIncomeShare(0L);
            contract.setIncomeShareAll(0L);
            return contract;
        }

        // 合同终止日 或 作废日
        DateTime cancelDate = new DateTime(contractChange.getDs());

        Long adjustmentFund = (getDayRange(contractPart[0], cancelDate) + 1) * dayShareIncome * (-1);

        DateTime usedEnd = usePart[1].compareTo(cancelDate) <= 0 ? usePart[1] : cancelDate;

        int daysIncom = Days.daysBetween(usePart[0], usedEnd).getDays() + 1;
        daysIncom = daysIncom < 0 ? 0 : daysIncom;
        //区间分摊总收入
        contract.setIntervaIncomeShare(dayShareIncome * daysIncom);

        boolean fullRange = getDayRange(selected[0], selected[1])
                >= Days.daysBetween(contractPart[0], cancelDate).getDays() ? true : false;

        if (fullRange) {
            contract.setAdjustmentFund(-1 * contract.getIntervaIncomeShare());
        } else if (checkTwoTime(selected[0], cancelDate) && checkTwoTime(cancelDate, selected[1])) {
            contract.setAdjustmentFund(adjustmentFund);
        } else {
            contract.setAdjustmentFund(0L);
        }

        contract.setIncomeShareAll(contract.getIntervaIncomeShare() + contract.getAdjustmentFund());
        return contract;
    }


    @Override
    public boolean checkLateContract(DateTime dateTime, DateTime creatTime) {
        int aferDays = dateTime.monthOfYear().get() == 12 ? 29 : 9;
        return dateTime.dayOfMonth().withMaximumValue().plusDays(aferDays).isBefore(creatTime);

    }

    private int getDayRange(DateTime range1, DateTime range2) {
        return Days.daysBetween(range1, range2).getDays();
    }

    public boolean checkTwoTime(DateTime dateTime1, DateTime dateTime2) {
        return dateTime1.isBefore(dateTime2) || dateTime1.isEqual(dateTime2);
    }

    @Deprecated
    private void caculateShareIncome(Contract contract, DateTime start, DateTime end) {

        DateTime create = new DateTime(new DateTime(contract.getCreateTime()).toString("yyyy-MM-dd"));
        DateTime contractStart = new DateTime(contract.getStartDate());
        DateTime contractEnd = new DateTime(contract.getEndDate());

        // 时间范围是否包含合同结束日期
        boolean containEnd = end.compareTo(contractEnd) >= 0;
        // 时间范围是否包含合同开始日期
        boolean containStart = start.compareTo(contractStart) <= 0;
        //所选结束时间包含合同结束时间 取合同结束时间 反之 取所选结束时间
        DateTime usedEnd = containEnd ? contractEnd : end;
        //所选开始时间包含合同开始时间 则取合同开始时间 反之 取所选开始时间
        DateTime usedStart = containStart ? contractStart : start;

        contract.setIntervalUseDays(Days.daysBetween(contractStart, usedEnd).getDays() + 1);//区间使用天数

        int allDay = Days.daysBetween(contractStart, contractEnd).getDays() + 1;
        Long excludTax = contract.getMoney() * 94;//不含税收入*100 ：contract.getMoney()*100 - contract.getMoney()*0.06*100 =  contract.getMoney()*94
        contract.setIncomeExcludingTax(excludTax);

        //每天的分摊收入
        //Long shareIncomDay = contract.getMoney() * 94 / allDay;
        //处理精度
        Long shareIncomDay = new BigDecimal(contract.getMoney() * 94 * 1.0 / allDay)
                .setScale(0, BigDecimal.ROUND_HALF_UP).longValue();

        DateTime creatMonth = create.dayOfMonth().withMinimumValue();//合同录入月1号

        int aferDays = 9;
        if (contractStart.monthOfYear().get() == 12) {
            // 12月份的合同 次月30号（包含）
            aferDays = 29;
        }
        DateTime nextMonth = null;

        Long adjustmentFund = 0L;

        int betweenMonth = create.getMonthOfYear() - contractStart.getMonthOfYear();

        if (ContractStatusEnum.CANCEL.getKey().equals(contract.getStatus()) || ContractStatusEnum.SUSPEND.getKey().equals(contract.getStatus())) {
            //合同作废

            boolean isCancel = ContractStatusEnum.CANCEL.getKey().equals(contract.getStatus()) ? true : false;
            String contentKey = isCancel ? ContractStatusEnum.CANCEL.getValue() : ContractStatusEnum.SUSPEND.getValue();
            ContractChange contractChange = contractChangeRepository.findByContentCode(contentKey, contract.getContractCode());

            if (contractChange == null) {
                contract.setAdjustmentFund(0L);
                contract.setIntervaIncomeShare(0L);
                contract.setIncomeShareAll(0L);
                return;
            }

            // 合同终止日 或 作废日
            DateTime cancelDate = new DateTime(contractChange.getDs());

            if (isCancel) {

                adjustmentFund = new BigDecimal((Days.daysBetween(contractStart, cancelDate).getDays() + 1) * excludTax * 1.0 / allDay)
                        .setScale(0, BigDecimal.ROUND_HALF_UP).longValue() * -1;

                usedEnd = usedEnd.compareTo(cancelDate) <= 0 ? usedEnd : cancelDate;
                int daysIncom = Days.daysBetween(usedStart, usedEnd).getDays() + 1;
                daysIncom = daysIncom < 0 ? 0 : daysIncom;
                //区间分摊总收入
                contract.setIntervaIncomeShare(shareIncomDay * daysIncom);

                boolean fullRange = Days.daysBetween(start, end).getDays()
                        >= Days.daysBetween(contractStart, cancelDate).getDays() ? true : false;

                if (fullRange) {
                    contract.setAdjustmentFund(-1 * contract.getIntervaIncomeShare());
                } else if (checkTwoTime(start, cancelDate) && checkTwoTime(cancelDate, end)) {
                    contract.setAdjustmentFund(adjustmentFund);
                } else {
                    contract.setAdjustmentFund(0L);
                }

                contract.setIncomeShareAll(contract.getIntervaIncomeShare() + contract.getAdjustmentFund());

            } else {
                //中止操作入库时间
                DateTime suspendDate = new DateTime(new DateTime(contractChange.getCreateTime()).toString("yyyy-MM-dd"));

                if (checkTwoTime(suspendDate, cancelDate)) {
                    adjustmentFund = 0L;
                } else {
                    adjustmentFund = new BigDecimal((Days.daysBetween(cancelDate, suspendDate).getDays() + 1) * excludTax * 1.0 / allDay)
                            .setScale(0, BigDecimal.ROUND_HALF_UP).longValue() * -1;
                }

                usedEnd = usedEnd.compareTo(cancelDate) <= 0 ? usedEnd : cancelDate;
                int daysIncom = Days.daysBetween(usedStart, usedEnd).getDays() + 1;
                daysIncom = daysIncom < 0 ? 0 : daysIncom;

                //区间分摊总收入
                contract.setIntervaIncomeShare(shareIncomDay * daysIncom);

                if (checkTwoTime(start, cancelDate) && checkTwoTime(cancelDate, end)) {
                    contract.setAdjustmentFund(adjustmentFund);
                } else {
                    contract.setAdjustmentFund(0L);
                }

                contract.setIncomeShareAll(contract.getIntervaIncomeShare() + contract.getAdjustmentFund());

            }

            //中止 取消 合同逻辑结束
            return;


        } else if (contractStart.dayOfMonth().withMaximumValue().plusDays(aferDays).isBefore(create)) {

            //录入时间在次月10以后 或次月30日以后
            if (betweenMonth == 1) {
                //相差一月
                nextMonth = contractStart.dayOfMonth().withMaximumValue().plusDays(1);//合同开始次月1号 与调整金对应 = 合同录入月1号
            } else if (betweenMonth > 1) {
                //相差多月
                nextMonth = creatMonth;//合同录入月1号 与调整金对应
            } else {
                //相差小于1月
                nextMonth = null;
            }

            if (nextMonth != null && end.isBefore(nextMonth.plusDays(betweenMonth > 1 ? -1 : 0))) {
                //当月（上月）及之前 无分摊收入
                contract.setIntervaIncomeShare(0L);
            } else if (nextMonth == null || (checkTwoTime(nextMonth, end))) {
                if (nextMonth == null) {
                    //执行期内的分摊金额开始时间
                    nextMonth = usedStart;
                }

                //筛选结束时间包含合同开始次月1号 则计算分摊收入
                //时间范围内用于计算分摊金额的天数
                DateTime shareStart = start.compareTo(nextMonth) <= 0 ? nextMonth : start;
                int daysIncom = Days.daysBetween(shareStart, usedEnd).getDays() + 1;
                //区间分摊收入
                contract.setIntervaIncomeShare(shareIncomDay * daysIncom);
            } else {
                contract.setIntervaIncomeShare(0L);
            }

            //调证金   筛选时间包含录入月1号 则计算调证金
            //调证金：（合同开始月最后一日 - 合同开始日期 + 1）* （不含税收入 / 合同总天数）

            if (betweenMonth == 1) {

                adjustmentFund = nextMonth == null ? 0 : new BigDecimal((contractStart.dayOfMonth().withMaximumValue().dayOfMonth().get()
                        - contractStart.getDayOfMonth() + 1) * excludTax * 1.0 / allDay)
                        .setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
            } else {
                adjustmentFund = nextMonth == null ? 0 :
                        new BigDecimal((Days.daysBetween(contractStart, creatMonth.plusDays(-1)).getDays() + 1) * 1.0 * excludTax / allDay)
                                .setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
            }


            if (nextMonth != null && checkTwoTime(start, nextMonth) && checkTwoTime(nextMonth, end)) {
                contract.setAdjustmentFund(adjustmentFund);
                //筛选开始时间包含次月1号，则分摊金需加上调证金
                contract.setIntervaIncomeShare(contract.getIntervaIncomeShare() + adjustmentFund);
                contract.setIncomeShareAll(contract.getIntervaIncomeShare());
            } else {
                contract.setAdjustmentFund(0L);
            }

        } else {

            //时间范围内用于计算分摊金额的天数
            int daysIncom = Days.daysBetween(start, usedEnd).getDays() + 1;
            //区间分摊总收入
            contract.setIntervaIncomeShare(shareIncomDay * daysIncom);
            contract.setAdjustmentFund(0L);
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());
            adjustmentFund = 0L;


        }

        DateTime dayShareStartDate;

        dayShareStartDate = nextMonth == null ? contractStart : nextMonth;

        //最后一日  包含最后一日 时 分摊金处理
        //adjustmentFund = adjustmentFund < 0 ? 0 : adjustmentFund;

        Long lastDay = excludTax - adjustmentFund - shareIncomDay * (Days.daysBetween(dayShareStartDate, contractEnd).getDays());
        if (start.isEqual(end) && start.isEqual(contractEnd)) {
            //最后一日分摊金处理
            contract.setIntervaIncomeShare(lastDay);
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());
        } else if (checkTwoTime(contractEnd, end)) {
            //包含最后一日 时
            contract.setIntervaIncomeShare(contract.getIntervaIncomeShare() - shareIncomDay + lastDay);
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());
        } else {
            contract.setIncomeShareAll(contract.getIntervaIncomeShare());
        }

    }


    public static void main(String[] args) {
        DateTime contractStart = new DateTime("2020-05-15");
        DateTime create = new DateTime("2020-06-15");
        System.out.println(contractStart.monthOfYear().getDateTime());
        System.out.println(contractStart.dayOfMonth().withMaximumValue().plusDays(1));
        System.out.println(contractStart.dayOfMonth().withMaximumValue().plusDays(9).isBefore(create));
        System.out.println(contractStart.compareTo(create));

        long ss = new BigDecimal(94000 * 1.0 / 52)
                .setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
        System.out.println(ss);

        DateTime[][] times = new DateTime[][]{
                {new DateTime(), new DateTime()},
                {new DateTime(), new DateTime()}
        };

        System.out.println(times[1][0].toString());

    }
}
