package com.pj.project.tb_fee_statistics;

import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Month;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pj.api.wx.bo.PriceBO;
import com.pj.current.config.MyConfig;
import com.pj.current.config.PartConfig;
import com.pj.project.relation_business_car.RelationBusinessCar;
import com.pj.project.relation_business_car.RelationBusinessCarService;
import com.pj.project.tb_business.TbBusiness;
import com.pj.project.tb_business.TbBusinessService;
import com.pj.project.tb_business_car.TbBusinessCar;
import com.pj.project.tb_business_car.TbBusinessCarService;
import com.pj.project.tb_business_item.TbBusinessItem;
import com.pj.project.tb_fee_details.ExportFeeDetailDTO;
import com.pj.project.tb_fee_details.TbFeeDetails;
import com.pj.project.tb_fee_details.TbFeeDetailsService;
import com.pj.project.tb_goods.TbGoods;
import com.pj.project.tb_goods.TbGoodsService;
import com.pj.project.tb_item_type.TbItemType;
import com.pj.project.tb_item_type.TbItemTypeService;
import com.pj.project4sp.uploadfile.UploadUtil;
import com.pj.utils.so.SoMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.pj.utils.sg.*;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * Service: tb_fee_statistics -- 收费统计表
 *
 * @author lzm
 */
@Service
@Transactional
@Slf4j
public class TbFeeStatisticsService extends ServiceImpl<TbFeeStatisticsMapper, TbFeeStatistics> implements IService<TbFeeStatistics> {

    /**
     * 底层 Mapper 对象
     */
    @Autowired
    TbFeeStatisticsMapper tbFeeStatisticsMapper;
    @Resource
    TbBusinessCarService tbBusinessCarService;
    @Resource
    TbBusinessService tbBusinessService;
    @Resource
    TbFeeDetailsService tbFeeDetailsService;
    @Resource
    private PartConfig partConfig;
    @Resource
    private MyConfig myConfig;
    @Resource
    private TbItemTypeService tbItemTypeService;
    @Resource
    private RelationBusinessCarService relationBusinessCarService;
    @Resource
    private TbGoodsService tbGoodsService;

    /**
     * 增
     */
    int add(TbFeeStatistics t) {
        return tbFeeStatisticsMapper.add(t);
    }

    /**
     * 删
     */
    int delete(Long id) {
        return tbFeeStatisticsMapper.delete(id);
    }

    /**
     * 改
     */
    int update(TbFeeStatistics t) {
        return tbFeeStatisticsMapper.update(t);
    }

    /**
     * 查
     */
    TbFeeStatistics getById(Long id) {
        return tbFeeStatisticsMapper.getById(id);
    }

    /**
     * 查集合 - 根据条件（参数为空时代表忽略指定条件）
     */
    List<TbFeeStatistics> getList(SoMap so) {
        return tbFeeStatisticsMapper.getList(so);
    }

    List<TbFeeStatistics> getMonth(SoMap so) {
        List<TbFeeStatistics> monthFees = tbFeeStatisticsMapper.getMonth(so);

        return monthFees;
    }

    List<TbFeeStatistics> getYear(SoMap so) {
        List<TbFeeStatistics> yearFees = tbFeeStatisticsMapper.getYear(so);

        return yearFees;
    }

    public TbFeeStatistics getByDayAndFeeType(String day, Integer feeType) {
        QueryWrapper<TbFeeStatistics> qw = new QueryWrapper<>();
        qw.eq("day", day);
        qw.eq("fee_type", feeType);
        return getOne(qw);
    }


    public void countParkFee(List<PriceBO> cars) {
        Date now = new Date();
        String toDay = DateUtil.format(now, "yyyy-MM-dd");
        String thisYear = StrUtil.sub(toDay, 0, 4);
        String thisMonth = StrUtil.sub(toDay, 0, 7);
        TbFeeStatistics parkFee = this.getByDayAndFeeType(toDay, TbFeeStatistics.FeeTypeEnum.PARK_FEE.getCode());
        if (parkFee == null) {
            parkFee = new TbFeeStatistics();
            parkFee.setTaxRate(BigDecimal.valueOf(TbFeeStatistics.taxRate.PARK.getValue()));
        }
        BigDecimal totalPrice = BigDecimal.valueOf(0);
        Integer totalNum = parkFee.getNum();

        for (PriceBO bo1 : cars) {
            BigDecimal price = bo1.getP();
            if (price.compareTo(BigDecimal.valueOf(0)) != 0) {
                totalPrice = totalPrice.add(price);
                TbBusinessCar car = tbBusinessCarService.getById(bo1.getId());
                Integer addNum = calcuAddNum(car.getMoney(), price);
                totalNum += addNum;
            }
        }
        if (totalPrice.compareTo(BigDecimal.valueOf(0)) == 0) {
            return;
        }
        parkFee.setNum(totalNum).setTaxMoney(parkFee.getTaxMoney().add(totalPrice));
        BigDecimal taxes = totalPrice
                .divide(BigDecimal.valueOf(1).add(parkFee.getTaxRate()), 2, BigDecimal.ROUND_HALF_UP)
                .multiply(parkFee.getTaxRate());
        taxes = taxes.setScale(2, BigDecimal.ROUND_HALF_UP);
        BigDecimal noTaxMoney = totalPrice.subtract(taxes);
        noTaxMoney = noTaxMoney.setScale(2, BigDecimal.ROUND_HALF_UP);
        parkFee.setNoTaxMoney(parkFee.getNoTaxMoney().add(noTaxMoney))
                .setTaxes(parkFee.getTaxes().add(taxes));

        parkFee.setDayTime(now).setMonth(thisMonth).setYear(thisYear).setDay(toDay)
                .setFeeType(TbFeeStatistics.FeeTypeEnum.PARK_FEE.getCode()).setItemTypeName(TbFeeStatistics.FeeTypeEnum.PARK_FEE.getDesc())
                .setPayType(3);
        this.saveOrUpdate(parkFee);
    }

    public Integer calcuAddNum(BigDecimal money, BigDecimal p) {
        BigDecimal baseNightPrice = partConfig.getBasePrice().add(partConfig.getExtraPrice());
        BigDecimal[] qrOld = money.divideAndRemainder(baseNightPrice);
        Integer oldNightNum = qrOld[0].intValue();
        if (qrOld[1].compareTo(BigDecimal.valueOf(0)) > 0) {
            oldNightNum++;
        }
        BigDecimal newMoney = money.add(p);
        BigDecimal[] qrNew = newMoney.divideAndRemainder(baseNightPrice);
        Integer newNightNum = qrNew[0].intValue();
        if (qrNew[1].compareTo(BigDecimal.valueOf(0)) > 0) {
            newNightNum++;
        }
        return newNightNum - oldNightNum;
    }


    public void countBusinessFee(List<TbBusinessItem> items, String transactionId, String outTradeNo) {
        Date now = new Date();
        String nowStr = DateUtil.format(now, "yyyy-MM-dd HH:mm:ss");
        String toDay = DateUtil.format(now, "yyyy-MM-dd");
        String thisYear = StrUtil.sub(toDay, 0, 4);
        String thisMonth = StrUtil.sub(toDay, 0, 7);
        for (TbBusinessItem item : items) {
            Integer feeType = item.getPayType();
            String itemType = item.getItemTypeName();
            if (StrUtil.isNotEmpty(itemType)) {
                if (itemType.contains("核酸")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.NUCLEIC_FEE.getCode();
                } else if (itemType.contains("消毒") || itemType.contains("消杀")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.DISINFECT_FEE.getCode();
                } else if (itemType.contains("装卸")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.STEVEDORE_FEE.getCode();
                } else if (StrUtil.equals(itemType, "特殊车辆") || StrUtil.equals(itemType, "汽车吊")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.STEVEDORE_FEE.getCode();
                } else if (itemType.contains("入场管理")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.MANAGE_FEE.getCode();
                } else if (StrUtil.equals(itemType, "充电打冷")) {
                    feeType = TbFeeStatistics.FeeTypeEnum.CHARGE_FEE.getCode();
                }
            }

            Double taxRate = item.getTaxRate();

            TbFeeStatistics businessFee = this.getByDayAndFeeType(toDay, feeType);
            if (businessFee == null) {
                businessFee = new TbFeeStatistics();
                businessFee.setTaxRate(BigDecimal.valueOf(taxRate));
            }
            businessFee.setTaxMoney(businessFee.getTaxMoney().add(item.getTotal()));//含税收入
            businessFee.setNum(businessFee.getNum() + Integer.valueOf(item.getNum()));
            BigDecimal taxes = item.getTotal()
                    .divide(BigDecimal.valueOf(1).add(businessFee.getTaxRate()), 2, BigDecimal.ROUND_HALF_UP)
                    .multiply(businessFee.getTaxRate());
            taxes = taxes.setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal noTaxMoney = item.getTotal().subtract(taxes);
            noTaxMoney = noTaxMoney.setScale(2, BigDecimal.ROUND_HALF_UP);

            businessFee.setNoTaxMoney(businessFee.getNoTaxMoney().add(noTaxMoney))
                    .setTaxes(businessFee.getTaxes().add(taxes));
            businessFee.setDayTime(now).setMonth(thisMonth).setYear(thisYear).setDay(toDay)
                    .setFeeType(feeType)
                    //.setItemTypeName(itemType)
                    .setPayType(3);
            this.saveOrUpdate(businessFee);
            /*-----------------分割线------------------*/
            //添加此item的收费明细
            TbBusiness business = tbBusinessService.getById(item.getBusinessId());
            TbFeeDetails businessFeeDetail = new TbFeeDetails();
            businessFeeDetail.setTaxRate(BigDecimal.valueOf(taxRate));
            BigDecimal taxPrice = item.getTotal()
                    .divide(BigDecimal.valueOf(1).add(businessFeeDetail.getTaxRate()), 2, BigDecimal.ROUND_HALF_UP)
                    .multiply(businessFeeDetail.getTaxRate());
            taxPrice = taxPrice.setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal noTaxPrice = item.getTotal().subtract(taxPrice);
            noTaxPrice = noTaxPrice.setScale(2, BigDecimal.ROUND_HALF_UP);

            String carNo = business.getCardNo();
            if (StrUtil.isEmpty(business.getCardNo())) {
                carNo = business.getChinaCarNo();
            }
            TbGoods businessCfg = tbGoodsService.getById(business.getGoodsId());
            if((TbGoods.LeaveEnum.BUSINESS_MONEY.getCode()==businessCfg.getChinaCarLeave() || TbGoods.LeaveEnum.APART_BUSINESS.getCode()==businessCfg.getChinaCarLeave())
            && TbGoods.LeaveEnum.BUSINESS_MONEY.getCode()!=businessCfg.getVietnamCarLeave() && TbGoods.LeaveEnum.APART_BUSINESS.getCode()!=businessCfg.getVietnamCarLeave()){
                carNo = business.getChinaCarNo();
            }
            String businessCarIds = "";
            String businessCarNos = "";
            List<RelationBusinessCar> relation = relationBusinessCarService.findByBusinessId(item.getBusinessId());
            for (RelationBusinessCar rela : relation) {
                TbBusinessCar bt = tbBusinessCarService.getById(rela.getBusinessCarId());
                if(StrUtil.contains(carNo, bt.getCarNo())){
                    businessCarIds += bt.getId() + ",";
                    businessCarNos += bt.getCarNo() + ",";
                }
            }
            businessCarIds = StrUtil.sub(businessCarIds, 0, businessCarIds.length()-1);
            businessCarNos = StrUtil.sub(businessCarNos, 0, businessCarNos.length()-1);
            businessFeeDetail.setBusinessId(item.getBusinessId()).setBusinessNo(business.getNo())
                    .setBusinessItemNo(item.getNo()).setBusinessCarId(businessCarIds).setBusinessCarNo(businessCarNos)
                    .setCarNo(carNo)
                    .setItemPrice(item.getTotal()).setUnitPrice(item.getItemPrice()).setNoTaxPrice(noTaxPrice).setTaxPrice(taxPrice)
                    .setFeeType(feeType)
                    .setItemTypeId(item.getItemTypeId()).setItemTypeName(item.getItemTypeName())
                    .setItemId(item.getItemId()).setItemName(item.getItemName())
                    .setPayDay(toDay).setPayType(3).setCreateTime(now)
                    .setBusinessItemId(item.getId() + "")
                    .setPickCustomerName(item.getPickCustomerName())
                    .setIsSettle(1).setPayMode(1).setPayTime(nowStr).setWeight(business.getNetWeight())
                    .setTransactionId(transactionId).setOutTradeNo(outTradeNo)
                    .setNum(Integer.valueOf(item.getNum()));
            tbFeeDetailsService.saveOrUpdate(businessFeeDetail);
        }
    }

    public TbFeeStatistics updateParkNum(String day) {
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.like("create_time", day);
        List<TbFeeDetails> list = tbFeeDetailsService.list(qw);
        Integer totalNum = 0;
        for (TbFeeDetails feeDetail : list) {
            if (feeDetail.getFeeType() == TbFeeStatistics.FeeTypeEnum.PARK_FEE.getCode()) {
                totalNum = totalNum + feeDetail.getNum();
            }
        }
        TbFeeStatistics feeStatistics = getByDayAndFeeType(day, TbFeeStatistics.FeeTypeEnum.PARK_FEE.getCode());
        feeStatistics.setNum(totalNum);
        this.updateById(feeStatistics);
        return feeStatistics;
    }

    public String exportMonthStatistics(SoMap so) throws Exception {
        String monthStr = so.getString("exportMonth");
        so.put("month", monthStr);
        Date month = DateUtil.parse(monthStr, "yyyy-MM");
        Month monthEnum = DateUtil.monthEnum(month);
        boolean leap = DateUtil.isLeapYear(DateUtil.year(month));
        Integer lastDay = monthEnum.getLastDay(leap);
        Map<String, ExportMonthDataDTO> dataMap = new TreeMap<>();
        for (int i = 1; i <= lastDay; i++) {
            ExportMonthDataDTO data = new ExportMonthDataDTO();
            String index = i < 10 ? "0" + i : "" + i;
            String day = monthStr + "-" + index;
            data.setIndex(index).setDay(day);
            dataMap.put(index, data);
        }
        List<TbFeeStatistics> list = this.getList(so);
        Integer totalIn = 0;
        Integer totalOut = 0;
        Integer totalAdd = 0;
        BigDecimal totalMoney = BigDecimal.valueOf(0);
        for (TbFeeStatistics statistics : list) {
            String day = statistics.getDay();
            String key = StrUtil.sub(day, 8, 10);
            ExportMonthDataDTO data = dataMap.get(key);
            if (data.getDayMoney() == null) {
                data.setDayMoney(BigDecimal.valueOf(0));
            }
            data.setDayMoney(data.getDayMoney().add(statistics.getTaxMoney()));
            totalMoney = totalMoney.add(statistics.getTaxMoney());
        }
        List<ExportMonthDataDTO> exportList = new ArrayList<>(dataMap.values());
        for (ExportMonthDataDTO dto : exportList) {
            String day = dto.getDay();
            List<TbBusinessCar> inCarList = tbBusinessCarService.findInCarByDay(day);
            List<TbBusinessCar> outCarList = tbBusinessCarService.findOutCarByDay(day);
            Integer inNum = inCarList != null ? inCarList.size() : 0;
            Integer outNum = outCarList != null ? outCarList.size() : 0;
            Integer addNum = inNum - outNum;
            if (inNum != 0) {
                dto.setInNum(inNum);
                totalIn += inNum;
            }
            if (outNum != 0) {
                dto.setOutNum(outNum);
                totalOut += outNum;
            }
            if (addNum != 0) {
                dto.setAddNum(addNum);
                totalAdd += addNum;
            }
        }
        Map<String, String> head = new HashMap<>();
        Integer exportMonth = Integer.valueOf(StrUtil.sub(monthStr, 5, 7));
        String today = DateUtil.today();
        head.put("exportMonth", exportMonth + "");
        head.put("exportDay", today);
        head.put("totalIn", totalIn + "");
        head.put("totalOut", totalOut + "");
        head.put("totalAdd", totalAdd + "");
        head.put("totalMoney", totalMoney.toString());

        String separator = File.separator;

        String rootPath = UploadUtil.uploadConfig.rootFolder + separator + UploadUtil.uploadConfig.httpPrefix;
        String prefix = myConfig.getDomain() + UploadUtil.uploadConfig.httpPrefix;
        String extPath = "feeStatistics_month" + separator + today + separator;
        String fileName = "收费月统计表_" + monthStr + ".xlsx";
        String savePath = rootPath + separator + extPath;
        File saveFIle = new File(savePath);
        if (!saveFIle.exists()) {
            saveFIle.mkdirs();
        }

        ClassPathResource classPathResource = new ClassPathResource("static/month-fee.xlsx");
        InputStream tempInputStream = classPathResource.getInputStream();
        ExcelWriter excelWriter = EasyExcel.write(savePath + fileName, ExportFeeDetailDTO.class)
                .withTemplate(tempInputStream).build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();//换行
        excelWriter.fill(head, writeSheet);
        excelWriter.fill(exportList, fillConfig, writeSheet);
        excelWriter.finish();
        return prefix + "/feeStatistics_month/" + today + "/收费月统计表_" + monthStr + ".xlsx";
    }

    public void calcuAgain() {
        List<TbFeeStatistics> list = this.list();
        for (TbFeeStatistics statistic : list) {
            String day = statistic.getDay();
            Integer feeType = statistic.getFeeType();
            QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
            qw.like("create_time", day).eq("fee_type", feeType);
            List<TbFeeDetails> detailsList = tbFeeDetailsService.list(qw);
            BigDecimal totalPrice = new BigDecimal(0);
            BigDecimal totalTaxes = new BigDecimal(0);
            BigDecimal totalNoTaxPrice = new BigDecimal(0);
            for (TbFeeDetails detail : detailsList) {
                totalPrice = totalPrice.add(detail.getItemPrice());
                totalTaxes = totalTaxes.add(detail.getTaxPrice());
                totalNoTaxPrice = totalNoTaxPrice.add(detail.getNoTaxPrice());
            }
            statistic.setTaxMoney(totalPrice).setTaxes(totalTaxes).setNoTaxMoney(totalNoTaxPrice);
        }
        this.updateBatchById(list);
    }
}
