package com.pj.project.tb_fee_details;

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

import cn.hutool.core.date.DateUtil;
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.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.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_statistics.TbFeeStatistics;
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.context.annotation.Lazy;
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_details -- 收费明细表
 *
 * @author lzm
 */
@Service
@Transactional
@Slf4j
public class TbFeeDetailsService extends ServiceImpl<TbFeeDetailsMapper, TbFeeDetails> implements IService<TbFeeDetails> {

    /**
     * 底层 Mapper 对象
     */
    @Autowired
    TbFeeDetailsMapper tbFeeDetailsMapper;
    @Resource
    TbBusinessCarService tbBusinessCarService;
    @Resource
    @Lazy
    TbBusinessService tbBusinessService;
    @Resource
    private MyConfig myConfig;
    @Resource
    private PartConfig partConfig;

    /**
     * 增
     */
    int add(TbFeeDetails t) {
        return tbFeeDetailsMapper.add(t);
    }

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

    /**
     * 改
     */
    int update(TbFeeDetails t) {
        return tbFeeDetailsMapper.update(t);
    }

    /**
     * 查
     */
    TbFeeDetails getById(Long id) {
        return tbFeeDetailsMapper.getById(id);
    }

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

    public TbFeeDetails getByBusinessItemId(String businessItemId) {
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_item_id", businessItemId);
        return getOne(qw);
    }

    public TbFeeDetails getByBusinessIdAndCarNoAndFeeType(String businessId, String carNo, Integer feeType) {
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_id", businessId);
        qw.eq("car_no", carNo);
        qw.eq("fee_type", feeType);
        return getOne(qw);
    }
    public List<TbFeeDetails> getByBusinessIdAndCarNo(String businessId, String carNo){
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_id", businessId);
        qw.eq("car_no", carNo);
        return list(qw);
    }
    public TbFeeDetails getByBusinessIdAndCarNoAndItemType(String businessId, String carNo, String itemTypeId) {
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_id", businessId);
        qw.eq("car_no", carNo);
        qw.eq("item_type_id", itemTypeId);
        return getOne(qw);
    }

    public List<TbFeeDetails> getByBusinessCarIdAndCarNoAndFeeType(String businessCarId, String carNo, Integer feeType){
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_car_id", businessCarId);
        qw.eq("car_no", carNo);
        qw.eq("fee_type", feeType);
        return list(qw);
    }

    public TbFeeDetails findBuCarIdAndCarNoAndFeeTypeAndItemTypeName(String businessCarId, String carNo, Integer feeType, String itemTypeName){
        QueryWrapper<TbFeeDetails> qw = new QueryWrapper<>();
        qw.eq("business_car_id", businessCarId);
        qw.eq("car_no", carNo);
        qw.eq("fee_type", feeType);
        qw.eq("item_type_name", itemTypeName);
        return getOne(qw);
    }


    public void chargeParkFee(List<PriceBO> cars, String transactionId, String outTradeNo) {
        log.info("进入计算停车费:{}", JSONUtil.toJsonStr(cars));
        for (PriceBO bo1 : cars) {
            if(bo1.getP().compareTo(BigDecimal.valueOf(0)) == 0){
                continue;
            }
            TbBusinessCar car = tbBusinessCarService.getById(bo1.getId());

            BigDecimal paidMoney  = car.getMoney();

            List<TbFeeDetails> oldFeeList = getByBusinessCarIdAndCarNoAndFeeType(car.getId(), car.getCarNo(), TbFeeDetails.fee.PARK_FEE.getCode());
            TbFeeDetails nightParkFee = new TbFeeDetails();
            TbFeeDetails dayParkFee = new TbFeeDetails();
            String transactionIdStr = "";
            String outTradeNoStr = "";
            for (TbFeeDetails oldFee : oldFeeList) {
                if(!StrUtil.isEmpty(oldFee.getTransactionId())){
                    transactionIdStr = oldFee.getTransactionId();
                    outTradeNoStr = oldFee.getOutTradeNo();
                }
                if(StrUtil.equals(oldFee.getItemTypeName(), "白天停车")){
                    dayParkFee = oldFee;
                }
                if(StrUtil.equals(oldFee.getItemTypeName(), "夜间停车")){
                    nightParkFee = oldFee;
                }
            }
            transactionIdStr = transactionIdStr.equals("") ? transactionId : transactionIdStr + "," + transactionId;
            outTradeNoStr = outTradeNoStr.equals("") ? outTradeNo : outTradeNoStr + "," + outTradeNo;
            BigDecimal baseNightPrice = partConfig.getBasePrice().add(partConfig.getExtraPrice());
            BigDecimal[] qr = paidMoney.divideAndRemainder(baseNightPrice);//商和余数的数组，商为夜间停车次数，余数为白天停车费用。
            BigDecimal nightFeeNum = qr[0];
            if(nightFeeNum.compareTo(BigDecimal.valueOf(0)) > 0){
                    nightParkFee.setNum(nightFeeNum.intValue()).setItemTypeName("夜间停车")
                            .setUnitPrice(baseNightPrice).setItemPrice(baseNightPrice.multiply(nightFeeNum));
                    setFee(nightParkFee, car, transactionIdStr, outTradeNoStr);
                    saveOrUpdate(nightParkFee);
                    if(qr[1].compareTo(BigDecimal.valueOf(0)) == 0 && dayParkFee.getId() != null){
                        delete(Long.valueOf(dayParkFee.getId()));
                    }
            }
            if(qr[1].compareTo(BigDecimal.valueOf(0)) > 0){
                dayParkFee.setNum(1).setItemTypeName("白天停车").setUnitPrice(qr[1]).setItemPrice(qr[1]);
                setFee(dayParkFee, car, transactionIdStr, outTradeNoStr);
                saveOrUpdate(dayParkFee);
            }

        }
    }

    private void setFee(TbFeeDetails parkFee, TbBusinessCar car, 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");
        parkFee.setTaxRate(partConfig.getTaxRate());
        BigDecimal taxPrice = parkFee.getItemPrice().divide(BigDecimal.valueOf(1).add(parkFee.getTaxRate()),2, BigDecimal.ROUND_HALF_UP).multiply(parkFee.getTaxRate());
        taxPrice = taxPrice.setScale(2, BigDecimal.ROUND_HALF_UP);
        BigDecimal noTaxPrice = parkFee.getItemPrice().subtract(taxPrice);
        noTaxPrice = noTaxPrice.setScale(2, BigDecimal.ROUND_HALF_UP);
        parkFee.setBusinessId(car.getBusinessId()).setBusinessCarId(car.getId())
                .setCarNo(car.getCarNo())
                .setNoTaxPrice(noTaxPrice).setTaxPrice(taxPrice)
                .setFeeType(TbFeeDetails.fee.PARK_FEE.getCode())
                .setPayDay(toDay).setPayType(3).setCreateTime(now)
                .setIsSettle(1).setPayMode(1).setPayTime(nowStr)
                .setTransactionId(transactionId).setOutTradeNo(outTradeNo)
                .setBusinessCarNo(car.getNo());
//        if (business!=null){
//            parkFee.setWeight(business.getNetWeight()).setBusinessNo(business.getNo());
//        }
    }

    public String export(SoMap so) throws Exception{
        Date now = new Date();
        String nowStr = DateUtil.format(now, "yyyy-MM-dd HH:mm");
        String beginTime = so.getString("beginTime");
        String endTime = so.getString("endTime");
        String time = beginTime + "至" + endTime;
        Map<String, String> head = new HashMap<>();
        head.put("time", time);
        head.put("exportTime", nowStr);
        String inCarNum = "";
        String outPayCarNum = "";
        String notOutCarNum = "";
        boolean isIn = checkTimeRange(beginTime, endTime, now);
        if(isIn){
            inCarNum = getInCarNum(now) + "";
            outPayCarNum = getOutAndPayCarNum(now) + "";
            notOutCarNum = getNotOutCarNum(now) + "";
        }
        head.put("inCarNum", inCarNum);
        head.put("outPayCarNum", outPayCarNum);
        head.put("notOutCarNum", notOutCarNum);

        BigDecimal toDayFee = new BigDecimal(0);
        BigDecimal allDayFee = new BigDecimal(0);
        BigDecimal allParkFee = new BigDecimal(0);
        BigDecimal allDayTaxes = new BigDecimal(0);
        BigDecimal allDayNoTaxFee = new BigDecimal(0);
	    List<ExportFeeDetailDTO> exportList = new ArrayList<>();
        List<TbFeeDetails> list = this.getList(so);
        Integer index = 1;
        for (TbFeeDetails feeDetails : list) {
            String feeType = TbFeeDetails.fee.getDesc(feeDetails.getFeeType());
            ExportFeeDetailDTO detailDTO = new ExportFeeDetailDTO();
            detailDTO.setBusinessNo(feeDetails.getBusinessNo()).setCarNo(feeDetails.getCarNo())
                    .setFeeType(feeType).setItemTypeName(feeDetails.getItemTypeName())
                    .setItemName(feeDetails.getItemName()).setItemPrice(feeDetails.getItemPrice())
                    .setPayType("微信支付").setIndex(index++)
                    .setPayMode("直接收款")
                    .setWeight(feeDetails.getWeight()).setNum(feeDetails.getNum()).setUnitPrice(feeDetails.getUnitPrice())
                    .setSettle("已结算").setTaxRate(feeDetails.getTaxRate().multiply(new BigDecimal(100)))
                    .setTaxPrice(feeDetails.getTaxPrice()).setNoTaxPrice(feeDetails.getNoTaxPrice()).setPayTime(feeDetails.getPayTime())
                    .setPickCustomerName(feeDetails.getPickCustomerName());
            if(!StrUtil.isEmpty(feeDetails.getBusinessId())){
                detailDTO.setBusinessNo(feeDetails.getBusinessNo());
            }else {
                detailDTO.setBusinessNo(feeDetails.getBusinessCarNo());
            }
            exportList.add(detailDTO);
            if(checkTimeRange(nowStr, nowStr, feeDetails.getCreateTime()) && feeDetails.getFeeType()==TbFeeDetails.fee.PARK_FEE.getCode()){
                toDayFee = toDayFee.add(feeDetails.getItemPrice());
            }
            if(feeDetails.getFeeType()==TbFeeDetails.fee.PARK_FEE.getCode()){
                allParkFee = allParkFee.add(feeDetails.getItemPrice());
            }
            allDayFee = allDayFee.add(feeDetails.getItemPrice());
            allDayTaxes = allDayTaxes.add(feeDetails.getTaxPrice());
            allDayNoTaxFee = allDayNoTaxFee.add(feeDetails.getNoTaxPrice());
        }

        head.put("toDayFee", toDayFee.toString());
        head.put("allParkFee", allParkFee.toString());
        head.put("allDayFee", allDayFee.toString());
        head.put("allDayTaxes", allDayTaxes.toString());
        head.put("allDayNoTaxFee", allDayNoTaxFee.toString());

        String separator = File.separator;
        String today = DateUtil.today();
        String rootPath = UploadUtil.uploadConfig.rootFolder + separator + UploadUtil.uploadConfig.httpPrefix;
        String prefix = myConfig.getDomain() + UploadUtil.uploadConfig.httpPrefix;
        String extPath = "feeDetails" + separator + today + separator;
        String fileName = "收费明细表_" + time + ".xlsx";
        String savePath = rootPath + separator + extPath;
        File saveFIle = new File(savePath);
        if (!saveFIle.exists()) {
            saveFIle.mkdirs();
        }

        ClassPathResource classPathResource = new ClassPathResource("static/day-fee-new.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 + "/feeDetails/" + today + "/收费明细表_" + time + ".xlsx";
    }

    private boolean checkTimeRange(String begin, String end, Date now) {
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(begin));
        Date endTime = DateUtil.endOfDay(DateUtil.parse(end));
        if(now.getTime() >= beginTime.getTime() && now.getTime() <= endTime.getTime()){
            return true;
        }
        return false;
    }

    private Integer getInCarNum(Date now){
        Date beginOfDay = DateUtil.beginOfDay(now);
        Date endOfDay = DateUtil.endOfDay(now);
        QueryWrapper<TbBusinessCar> qw = new QueryWrapper();
        qw.isNotNull("real_in_time")
                .ge("real_in_time", beginOfDay)
                .le("real_in_time", endOfDay);
        List<TbBusinessCar> list = tbBusinessCarService.list(qw);
        return list != null ? list.size() : 0;
    }

    private Integer getOutAndPayCarNum(Date now){
        Date beginOfDay = DateUtil.beginOfDay(now);
        Date endOfDay = DateUtil.endOfDay(now);
        QueryWrapper<TbBusinessCar> qw = new QueryWrapper();
        qw.eq("pay", 1)
                .isNotNull("real_in_time").isNotNull("real_out_time")
                .ge("real_out_time", beginOfDay)
                .le("real_out_time", endOfDay);
        List<TbBusinessCar> list = tbBusinessCarService.list(qw);
        return list != null ? list.size() : 0;
    }

    private Integer getNotOutCarNum(Date now){
        Date beginOfDay = DateUtil.beginOfDay(now);
        Date endOfDay = DateUtil.endOfDay(now);
        QueryWrapper<TbBusinessCar> qw = new QueryWrapper();
        qw.isNotNull("real_in_time")
                .ge("real_in_time", beginOfDay)
                .le("real_in_time", endOfDay)
                .isNull("real_out_time");
        List<TbBusinessCar> list = tbBusinessCarService.list(qw);
        return list != null ? list.size() : 0;
    }

    public List<TbFeeDetails> findByBusinessCarId(String businessCarId, int feeType) {
        QueryWrapper<TbFeeDetails>ew=new QueryWrapper<>();
        ew.eq("business_car_id",businessCarId);
        ew.eq("fee_type",feeType);
        return list(ew);
    }
}
