package com.pj.project.tb_business_car;

import java.io.File;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.EasyExcel;
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.current.config.PartConfig;
import com.pj.current.config.SystemObject;
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_item.TbBusinessItem;
import com.pj.project.tb_business_item.TbBusinessItemService;
import com.pj.project.tb_car.TbCar;
import com.pj.project.tb_car.TbCarService;
import com.pj.project.tb_car_no_color.TbCarNoColor;
import com.pj.project.tb_car_no_color.TbCarNoColorService;
import com.pj.project.tb_fee_details.TbFeeDetails;
import com.pj.project.tb_fee_details.TbFeeDetailsService;
import com.pj.project4sp.global.BusinessException;
import com.pj.project4sp.uploadfile.UploadConfig;
import com.pj.utils.so.SoMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

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

import javax.annotation.Resource;

/**
 * Service: tb_business_car --
 *
 * @author qzy
 */
@Service
@Transactional
public class TbBusinessCarService extends ServiceImpl<TbBusinessCarMapper, TbBusinessCar> implements IService<TbBusinessCar> {
    private final List<String> CAR_LIST = StrUtil.splitTrim("浙,粤,京,津,冀,晋,蒙,辽,黑,沪,吉,苏,皖,赣,鲁,豫,鄂,湘,桂,琼,渝,川,贵,云,藏, 陕, 甘, 青, 宁", ",");

    /**
     * 底层 Mapper 对象
     */
    @Autowired
    TbBusinessCarMapper tbBusinessCarMapper;
    @Resource
    @Lazy
    private TbBusinessService tbBusinessService;
    @Resource
    TbCarService tbCarService;
    @Resource
    @Lazy
    TbFeeDetailsService tbFeeDetailsService;

    @Resource
    private UploadConfig uploadConfig;

    @Resource
    private TbCarNoColorService tbCarNoColorService;
    @Resource
    @Lazy
    private RelationBusinessCarService relationBusinessCarService;


    /**
     * 查集合 - 根据条件（参数为空时代表忽略指定条件）
     */
    List<TbBusinessCar> getList(SoMap so) {
        List<TbBusinessCar> list = tbBusinessCarMapper.getList(so);
        for (TbBusinessCar tbBusinessCar : list) {
            Date inTime = tbBusinessCar.getRealInTime();
            Date outTime = tbBusinessCar.getRealOutTime();
            if (inTime != null && outTime != null) {
                LocalDateTime inDayTime = inTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                LocalDateTime outDayTime = outTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                long minutes = ChronoUnit.MINUTES.between(inDayTime, outDayTime);
                PartConfig partConfig = SpringUtil.getBean(PartConfig.class);
                if (minutes < partConfig.getFreeMinutes()) {
                    tbBusinessCar.setPayType("免费");
                    continue;
                }
            }
            String type = tbBusinessCar.getPay() == null || tbBusinessCar.getPay() == 0 ? "未支付" : "已支付";
            tbBusinessCar.setPayType(type);
        }
        return list;
    }

    public TbBusinessCar findByBusinessIdAndCarNo(String businessId, String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("business_id", businessId)
                .eq("car_no", carNo.toUpperCase());
        return getOne(ew);
    }

    public TbBusinessCar findInAndNoBusinessCar(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo.toUpperCase())
                .isNotNull("real_in_time")
                .isNull("business_id")
                .isNull("real_out_time");
        return getOne(ew);
    }


    public List<TbBusinessCar> findInAndNoBusinessCar() {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.isNotNull("real_in_time")
                .isNull("real_out_time");
        return list(ew);
    }

    public void removeByBusinessId(String businessId) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("business_id", businessId);
        List<TbBusinessCar> list = list(ew);
        list.forEach(tbBusinessCar -> {
            if (tbBusinessCar.getRealInTime() == null) {
                this.removeById(tbBusinessCar.getId());
            } else {
                tbBusinessCarMapper.removeBusinessId(tbBusinessCar.getId());
            }
        });
    }


    public List<TbBusinessCar> searchPartCar(String carNo) {
        return tbBusinessCarMapper.searchPartCar(carNo.toUpperCase());
    }


    public TbBusinessCar findNotInCar(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo.toUpperCase())
                .isNull("real_in_time").orderByDesc("id");
        List<TbBusinessCar> list = list(ew);
        return list.isEmpty() ? null : list.get(0);
    }

    public List<TbBusinessCar> findNotOutCar(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo.toUpperCase())
                .isNull("real_out_time")
                .isNotNull("real_in_time");
        return list(ew);
    }

    public TbBusinessCar check(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo.toUpperCase())
                .and(i -> i.isNull("real_in_time").or().isNull("real_out_time"));
        return getOne(ew);
    }

    public void add(TbBusinessCar t) throws Exception {
        String carNo = t.getCarNo().toUpperCase();
        TbBusinessCar db = this.check(carNo);
        String businessId = t.getBusinessId();
        if (db != null) {
            throw new Exception("该车有未完成业务");
        }
        TbBusinessCar businessCar = this.findByBusinessIdAndCarNo(businessId, carNo);
        if (businessCar != null) {
            throw new Exception("车辆已存在");
        }
        TbBusiness tbBusiness = tbBusinessService.getById(businessId);
        if (!CAR_LIST.contains(carNo.substring(0, 1))) {
            tbBusiness.setCardNo(carNo);
            tbBusiness.setCardSize(t.getCarSize());
        }
        tbBusinessService.updateById(tbBusiness);
        t.setNo(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")) + RandomUtil.randomNumbers(4));
        this.save(t);
    }

    public AjaxJson deleteCar(String id) {
        TbBusinessCar db = this.getById(id);
        String checkStr = db.getCarNo().substring(0, 1);
        if (!CAR_LIST.contains(checkStr)) {
            return AjaxJson.getError("越南车不能删除");
        }
        this.removeById(id);
        return AjaxJson.getSuccess();
    }


    public TbBusinessCar findTheLastRecord(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo);
        ew.orderByDesc("id");
        List<TbBusinessCar> list = this.list(ew);
        return list.isEmpty() ? null : list.get(0);
    }

    public void addCarRecord(TbBusinessCar t) throws Exception {
        String carNo = t.getCarNo().toUpperCase();
        TbBusinessCar db = this.check(carNo);
        if (db != null) {
            throw new Exception("该车有未完成业务");
        }
        t.setCarNo(carNo).setPay(0).setIsLock(0).setBasePartMoney(new BigDecimal("0")).setTimeUpdate(new Date())
                .setNo(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")) + RandomUtil.randomNumbers(4));
        this.save(t);
    }

    public void updateCarRecord(TbBusinessCar t) throws Exception {
        TbBusinessCar db = this.getById(t.getId());
        String oldCarNo = db.getCarNo();
        String newCarNo = t.getCarNo();
        db.setRealInTime(t.getRealInTime()).setColor(t.getColor())
                .setRealOutTime(t.getRealOutTime()).setInImage(t.getInImage())
                .setOutImage(t.getOutImage())
                .setCarSize(t.getCarSize())
                .setDriverName(t.getDriverName())
                .setDriverPhone(t.getDriverPhone()).setTimeUpdate(new Date());
        //车牌没改
        if (StrUtil.equals(oldCarNo, newCarNo)) {
            this.updateById(db);
            return;
        }
        //改了车牌，看下新车牌是否被占用
        TbBusinessCar lastRecord = this.findTheLastRecord(newCarNo);
        //未离场，不能改
        if (lastRecord != null && lastRecord.getRealInTime() != null && lastRecord.getRealOutTime() == null) {
            throw new Exception("该车" + newCarNo + "已入场但未离场");
        }
        if (lastRecord != null && lastRecord.getRealInTime() == null) {
            throw new Exception("车辆已存在");
        }
        db.setCarNo(newCarNo);
        this.updateById(t);
        List<TbBusiness> businessList = tbBusinessService.findOtherBusinessByCarId(t.getId());
        String carStr = StrUtil.sub(newCarNo, 0, 1);
        for (TbBusiness tbBusiness : businessList) {
            if (!CAR_LIST.contains(carStr)) {
                tbBusiness.setCardNo(newCarNo);
            } else {
                List<TbBusinessCar> cars = this.findOtherBusinessCar(t.getBusinessId());
                String chinaCarNo = cars.stream().filter(obj -> CAR_LIST.contains(obj.getCarNo().substring(0, 1))).map(TbBusinessCar::getCarNo).collect(Collectors.joining("、"));
                tbBusiness.setChinaCarNo(chinaCarNo);
            }
            tbBusinessService.updateById(tbBusiness);

            List<TbFeeDetails> feeDetailsLIst = tbFeeDetailsService.getByBusinessIdAndCarNo(tbBusiness.getId(), oldCarNo);
            feeDetailsLIst.forEach(fee -> fee.setCarNo(newCarNo));
            tbFeeDetailsService.updateBatchById(feeDetailsLIst);
        }
        if (businessList.isEmpty()) {
            List<TbFeeDetails> feeDetailsList = tbFeeDetailsService.getByBusinessCarIdAndCarNoAndFeeType(t.getId(), oldCarNo, TbFeeDetails.fee.PARK_FEE.getCode());
            for (TbFeeDetails feeDetail : feeDetailsList) {
                if (feeDetail != null) {
                    feeDetail.setCarNo(newCarNo);
                    tbFeeDetailsService.updateById(feeDetail);
                }
            }

        }

    }

    public List<TbBusinessCar> findTheNoBusinessCar(String carNo) {
        return tbBusinessCarMapper.findTheNoBusinessCar(carNo);
    }

    public String export(SoMap soMap) {
        List<TbBusinessCar> cars = this.getList(soMap);
        List<ExportTbBusinessDTO> list = new ArrayList<>(cars.size());
        cars.forEach(tbBusinessCar -> {
            ExportTbBusinessDTO dto = new ExportTbBusinessDTO();
            BeanUtil.copyProperties(tbBusinessCar, dto);
            Double carSize = tbBusinessCar.getCarSize();
            dto.setSize(carSize + "");
            if (carSize == null || carSize == 0) {
                dto.setSize("-");
            }
            list.add(dto);
        });
        String flieTypeFolder = "/export/";
        String currDateFolder = DateUtil.today();
        String fileName = "car-record-" + RandomUtil.randomNumbers(6) + ".xlsx";
        String fileFolder = new File(uploadConfig.rootFolder).getAbsolutePath() +
                uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/";
        if (!FileUtil.exist(fileFolder)) {
            FileUtil.mkdir(fileFolder);
        }
        EasyExcel.write(fileFolder + fileName, ExportTbBusinessDTO.class).sheet("车辆放行记录")
                .doWrite(() -> list);
        return SystemObject.config.getDomain() + uploadConfig.httpPrefix + flieTypeFolder + currDateFolder + "/" + fileName;
    }

    public List<TbBusinessCar> findByCarNo(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.eq("car_no", carNo);
        return list(ew);
    }

    public AjaxJson deleteRecord(String id) {
        List<TbFeeDetails> tbFeeDetails = tbFeeDetailsService.findByBusinessCarId(id, TbFeeDetails.fee.PARK_FEE.getCode());
        if (!tbFeeDetails.isEmpty()) {
            return AjaxJson.getError("该车有付款记录，不能删除");
        }
        List<RelationBusinessCar> relationBusinessCars = relationBusinessCarService.findByBusinessCarId(id);
        if (!relationBusinessCars.isEmpty()) {
            return AjaxJson.getError("该车有业务，不能删除");
        }
        this.removeById(id);
        return AjaxJson.getSuccess();
    }

    public List<TbBusinessCar> findInCarByDay(String day) {
        QueryWrapper<TbBusinessCar> qw = new QueryWrapper();
        qw.like("real_in_time", day);
        List<TbBusinessCar> list = this.list(qw);
        return list;
    }

    public List<TbBusinessCar> findOutCarByDay(String day) {
        QueryWrapper<TbBusinessCar> qw = new QueryWrapper();
        qw.like("real_out_time", day);
        List<TbBusinessCar> list = this.list(qw);
        return list;
    }

    public TbBusinessCar findInCar(String carNo) {
        return null;
    }

    public List<TbBusinessCar> searchOtherBusinessCar(String carNo) {
        return tbBusinessCarMapper.searchOtherBusinessCar(carNo);
    }

    public List<TbBusinessCar> findTodayRecord(String carNo, String itemId) {
        return tbBusinessCarMapper.findTodayRecord(carNo, itemId);
    }

    public int checkCarBusinessType(String typeId, String carNo, String timeEnd) {
        return tbBusinessCarMapper.checkCarBusinessType(typeId, carNo, timeEnd);
    }

    public List<TbBusinessCar> findNotOutCarLike(String carNo) {
        if (StrUtil.isEmpty(carNo) || StrUtil.length(carNo) < 3) {
            return Collections.emptyList();
        }
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.select("car_no,car_size");
        ew.like("car_no", carNo.trim().toUpperCase());
        ew.isNull("real_out_time")
                .isNotNull("real_in_time");
        List<TbBusinessCar> list = list(ew);
        return list.stream().distinct().collect(Collectors.toList());
    }

    public TbBusinessCar searchOtherBusinessCarNotOut(String carNo) {
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.isNull("real_out_time")
                .isNull("business_id")
                .eq("car_no", carNo.trim().toUpperCase())
                .orderByDesc("time_update");
        List<TbBusinessCar> list = list(ew);
        return list.isEmpty() ? null : list.get(0);
    }

    public List<TbBusinessCar> searchByNo(String carNo) {
        if (StrUtil.length(carNo) < 3) {
            return Collections.emptyList();
        }
        QueryWrapper<TbBusinessCar> ew = new QueryWrapper<>();
        ew.select("car_no");
        ew.like("car_no", carNo);
        List<TbBusinessCar> list = list(ew);
        return list.stream().distinct().collect(Collectors.toList());
    }

    public List<TbBusinessCar> findOtherBusinessCar(String businessId) {
        return tbBusinessCarMapper.findOtherBusinessCar(businessId);
    }


    public List<TbBusinessCar> searchForPayCar(String carNo) {
        return tbBusinessCarMapper.searchForPayCar(carNo);
    }

    public void outCar(String id, String outChannel, Date realOutTime) {
        Date now=new Date();
        TbBusinessCar db=  this.getById(id);
        db.setOutChannel(outChannel).setRealOutTime(realOutTime).setTimeUpdate(now);
        this.updateById(db);
    }

    public void inCar(String id, String inChannel, Date realInTime) {
        TbBusinessCar db=  this.getById(id);
        Date now=new Date();
        db.setInChannel(inChannel).setRealInTime(realInTime).setTimeUpdate(now);
        this.updateById(db);
    }
}
