package com.ningdatech.carapi.car.vehicle.service.impl;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.exception.code.ExceptionCode;
import com.ningdatech.basic.function.VUtils;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.carapi.archives.enums.AuditStateEnum;
import com.ningdatech.carapi.car.datascreen.model.dto.DataScreenParam;
import com.ningdatech.carapi.car.datascreen.model.po.OverspeedStatisticsPO;
import com.ningdatech.carapi.car.equipment.mapper.NdVehicleEquipmentChannelsMapper;
import com.ningdatech.carapi.car.equipment.mapper.NdVehicleEquipmentInfoMapper;
import com.ningdatech.carapi.car.equipment.model.NdVehicleEquipmentChannels;
import com.ningdatech.carapi.car.equipment.model.NdVehicleEquipmentInfo;
import com.ningdatech.carapi.car.vehicle.mapper.VehicleBaseInfoMapper;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfo;
import com.ningdatech.carapi.car.vehicle.model.dto.VehicleAuditDTO;
import com.ningdatech.carapi.car.vehicle.model.dto.VehicleSaveDTO;
import com.ningdatech.carapi.car.vehicle.model.dto.VehicleUpdateDTO;
import com.ningdatech.carapi.car.vehicle.model.po.VehicleCompanyPO;
import com.ningdatech.carapi.car.vehicle.service.IVehicleBaseInfoService;
import com.ningdatech.carapi.car.vehicle.utils.VinUtils;
import com.ningdatech.carapi.common.constant.UserRoleTypeEnum;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.datascope.aop.XmlDataScope;
import com.ningdatech.carapi.scheduler.enums.DataTypeEnum;
import com.ningdatech.carapi.scheduler.enums.OperatorTypeEnum;
import com.ningdatech.carapi.sys.entity.UserRole;
import com.ningdatech.carapi.sys.entity.dto.CompanyDTO;
import com.ningdatech.carapi.sys.helper.CompaniesCacheHelper;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;
import com.ningdatech.carapi.sys.helper.VehiclesCacheHelper;
import com.ningdatech.carapi.sys.service.UserRoleService;
import com.ningdatech.carapi.user.util.LoginUserUtil;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author PoffyZhang
 * @since 2022-10-09
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class VehicleBaseInfoServiceImpl extends ServiceImpl<VehicleBaseInfoMapper, NdVehicleBaseInfo>
        implements IVehicleBaseInfoService {

    private final VehicleBaseInfoMapper vehicleBaseInfoMapper;
    private final NdVehicleEquipmentInfoMapper vehicleEquipmentInfoMapper;
    private final NdVehicleEquipmentChannelsMapper vehicleEquipmentChannelsMapper;

    private final VehiclesCacheHelper vehiclesCacheHelper;

    private final CompaniesCacheHelper companiesCacheHelper;

    private final ApplicationContext applicationContext;
    private final RegionsCacheHelper regionCacheHelper;
    private final UserRoleService userRoleService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeByIdWithCache(List<Long> ids) {
        if (ids.isEmpty()) {
            return true;
        }
        AtomicReference<Boolean> removeFlag = new AtomicReference<>(true);
        //只能删除 审核不成功的车辆
        ids.forEach(id -> {
            NdVehicleBaseInfo vehicle = getById(id);
            VUtils.isTrueFormat(Objects.isNull(vehicle))
                    .throwMessage(ExceptionCode.OPERATION_EX.getCode(), "该id车辆不存在 {}", id);

            VUtils.isTrueFormat(!AuditStateEnum.AUDIT_FAIL.getCode().equals(vehicle.getAuditState()))
                    .throwMessage(ExceptionCode.OPERATION_EX.getCode(), "只有审核失败的车辆才能删除");

            if (!removeById(id)) {
                removeFlag.set(false);
                return;
            }

            //删除 车辆下的设备
            List<NdVehicleEquipmentInfo> ndVehicleEquipmentInfos = vehicleEquipmentInfoMapper.selectList(
                    Wrappers.lambdaQuery(NdVehicleEquipmentInfo.class).eq(NdVehicleEquipmentInfo::getVehicleId, id));
            if (CollUtil.isNotEmpty(ndVehicleEquipmentInfos)) {
                ndVehicleEquipmentInfos.forEach(equipment -> {
                    vehicleEquipmentChannelsMapper.delete(Wrappers.lambdaQuery(NdVehicleEquipmentChannels.class).eq(NdVehicleEquipmentChannels::getEquipmentId, equipment.getId()));
                    vehicleEquipmentInfoMapper.deleteById(equipment);
                });
            }
            //更新车辆缓存
            vehiclesCacheHelper.refresh(id);
        });
        return removeFlag.get();
    }

    /**
     * 车辆审核
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean audit(VehicleAuditDTO data, Long userId) {
        VUtils.isTrue(!AuditStateEnum.AUDIT_SUCCESS.getCode().equals(data.getAuditState()) &&
                !AuditStateEnum.AUDIT_FAIL.getCode().equals(data.getAuditState())).throwMessage("请确认传入的审核code正确");
        NdVehicleBaseInfo vehicleBaseInfo = vehicleBaseInfoMapper.selectById(data.getId());
        VUtils.isTrueFormat(Objects.isNull(vehicleBaseInfo))
                .throwMessage(ExceptionCode.OPERATION_EX.getCode(), "修改车辆 {} 为空", data.getId());
        VUtils.isTrueFormat(!AuditStateEnum.AUDIT.getCode().equals(vehicleBaseInfo.getAuditState()))
                .throwMessage(ExceptionCode.OPERATION_EX.getCode(), "只能审核待审核的车辆 {}", data.getId());

        vehicleBaseInfo.setAuditState(data.getAuditState());
        vehicleBaseInfo.setAuditBy(userId);
        vehicleBaseInfo.setAuditTime(LocalDateTime.now());
        vehicleBaseInfo.setAuditMind(data.getAuditMind());
        //最后修改人
        vehicleBaseInfo.setUpdateBy(userId);
        vehicleBaseInfo.setUpdateOn(LocalDateTime.now());
        if(updateById(vehicleBaseInfo)){
            // 如果审核通过
            if (AuditStateEnum.AUDIT_SUCCESS.getCode().equals(data.getAuditState())) {
                return Boolean.TRUE;
            } else if (AuditStateEnum.AUDIT_FAIL.getCode().equals(data.getAuditState())) {
                // 审核不通过，车辆的所属企业和使用企业要恢复到提交审核前的状态，
                // 防止使用企业和所属企业更新后，原企业无法重新提交
                NdVehicleBaseInfo baseInfo = vehicleBaseInfoMapper.selectById(data.getId());
                Long beforeCompanyId = baseInfo.getBeforeCompanyId();
                Long beforeUseCompanyId = baseInfo.getBeforeUseCompanyId();

                baseInfo.setCompanyId(beforeCompanyId);
                baseInfo.setUseCompanyId(beforeUseCompanyId);

                if (Objects.nonNull(beforeUseCompanyId)) {
                    CompanyDTO companyDto = companiesCacheHelper.getById(beforeUseCompanyId);
                    baseInfo.setUseCompanyName(companyDto.getName());
                }
                vehicleBaseInfoMapper.updateById(baseInfo);
            }
        }
        return Boolean.FALSE;
    }


    /**
     * 保存车辆
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public NdVehicleBaseInfo save(VehicleSaveDTO data, Long userId) {
        Long companyId = LoginUserUtil.loginUserDetail().getCompanyId();
        checkNewCar(data.getCarPlate());
        //VUtils.isTrue(!VinUtils.isLegal(data.getChassisNo())).throwMessage("底盘号错误 请重新填入!");
        NdVehicleBaseInfo vehicleBaseInfo = BeanUtil.toBean(data, NdVehicleBaseInfo.class);
        //创建人 最后修改人
        vehicleBaseInfo.setCreateBy(userId);
        vehicleBaseInfo.setUpdateBy(userId);
        vehicleBaseInfo.setAuditState(AuditStateEnum.AUDIT.getCode());
        vehicleBaseInfo.setCompanyId(companyId);
        CompanyDTO company = companiesCacheHelper.getById(companyId);
        VUtils.isTrue(Objects.isNull(company)).throwMessage("获取不到当前操作人的公司信息!");
        vehicleBaseInfo.setRegionId(company.getRegionId());
        CodeUtil.searchRegionName(vehicleBaseInfo);
        if(save(vehicleBaseInfo)){
            vehiclesCacheHelper.refresh(vehicleBaseInfo.getId());
        }
        return vehicleBaseInfo;
    }

    private void checkCar(String oldCarPlate, String carPlate) {
        // 如果提交的车牌号已经在审核中
        VUtils.isTrue(count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                        .eq(NdVehicleBaseInfo::getCarPlate, carPlate)
                        .eq(NdVehicleBaseInfo::getAuditState,AuditStateEnum.AUDIT.getCode())) > 0)
                .throwMessage("该车牌车辆待审核中 请勿重新提交!");
        // 如果修改信息修改了车牌号，校验车牌号是否已经存在
        // 如果提交的车牌号已经审核通过存在于系统中
        NdVehicleBaseInfo baseInfo = vehicleBaseInfoMapper.selectOne(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                .eq(NdVehicleBaseInfo::getCarPlate, carPlate));
        String companyName = null;
        if (Objects.nonNull(baseInfo)) {
            Long companyId = baseInfo.getCompanyId();
            if (Objects.nonNull(companyId)) {
                companyName = companiesCacheHelper.getNameById(companyId);
            }
        }
        if (!carPlate.equals(oldCarPlate)) {
            VUtils.isTrue(count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                            .eq(NdVehicleBaseInfo::getCarPlate, carPlate)
                            .eq(NdVehicleBaseInfo::getAuditState, AuditStateEnum.AUDIT_SUCCESS.getCode())) > 0)
                    .throwMessage("系统企业:" + companyName + "已有该车牌车辆 不能重复添加!");
        }
    }

    private void checkNewCar(String carPlate) {
        // 如果提交的车牌号已经在审核中
        VUtils.isTrue(count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                        .eq(NdVehicleBaseInfo::getCarPlate, carPlate)
                        .eq(NdVehicleBaseInfo::getAuditState,AuditStateEnum.AUDIT.getCode())) > 0)
                .throwMessage("该车牌车辆待审核中 请勿重新提交!");
        // 如果提交的车牌号审核不通过
        VUtils.isTrue(count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                        .eq(NdVehicleBaseInfo::getCarPlate, carPlate)
                        .eq(NdVehicleBaseInfo::getAuditState,AuditStateEnum.AUDIT_FAIL.getCode())) > 0)
                .throwMessage("该车牌车辆审核不通过 请到车辆新增界面修改信息后重新提交!");
        NdVehicleBaseInfo baseInfo = vehicleBaseInfoMapper.selectOne(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                .eq(NdVehicleBaseInfo::getCarPlate, carPlate));
        String companyName = null;
        if (Objects.nonNull(baseInfo)) {
            Long companyId = baseInfo.getCompanyId();
            if (Objects.nonNull(companyId)) {
                companyName = companiesCacheHelper.getNameById(companyId);
            }
        }
        // 如果提交的车牌号已经审核通过存在于系统中
        VUtils.isTrue(count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class).eq(NdVehicleBaseInfo::getCarPlate, carPlate)
                .eq(NdVehicleBaseInfo::getAuditState, AuditStateEnum.AUDIT_SUCCESS.getCode())) > 0)
            .throwMessage("系统企业:" + companyName + "已有该车牌车辆 不能重复添加!");
        
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public NdVehicleBaseInfo update(VehicleUpdateDTO data, Long userId) {
        NdVehicleBaseInfo vehicleBaseInfo = vehicleBaseInfoMapper.selectById(data.getId());
        VUtils.isTrueFormat(Objects.isNull(vehicleBaseInfo))
                .throwMessage(ExceptionCode.OPERATION_EX.getCode(), String.format("修改车辆%d 为空", data.getId()));

        VUtils.isTrueFormat(!AuditStateEnum.AUDIT_FAIL.getCode().equals(vehicleBaseInfo.getAuditState()))
                .throwMessage(ExceptionCode.OPERATION_EX.getCode(), String.format("车辆状态为审核失败才能修改", data.getId()));

        VUtils.isTrue(StringUtils.isNotBlank(data.getCarPlate()) && !vehicleBaseInfo.getCarPlate().equals(data.getCarPlate()) &&
                count(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                        .eq(NdVehicleBaseInfo::getCarPlate, data.getCarPlate())) > 0)
                .throwMessage("此公司已经有该车牌车辆 不能重复添加!");

        if(StringUtils.isNotBlank(data.getChassisNo())){
            VUtils.isTrue(!VinUtils.isLegal(data.getChassisNo())).throwMessage("底盘号错误 请重新填入!");
        }

        NdVehicleBaseInfo vehicle = BeanUtil.toBean(data, NdVehicleBaseInfo.class);

        //最后修改人 置为审核中
        vehicle.setAuditState(AuditStateEnum.AUDIT.getCode());
        vehicle.setUpdateBy(userId);
        if(updateById(vehicle)){
            vehiclesCacheHelper.refresh(vehicle.getId());
        }
        return vehicle;
    }

    /**
     * 根据企业ids 获取车牌号
     *
     * @param companyIds
     * @return
     */
    @Override
    public List<VehicleCompanyPO> getCarPlatesByCompanyIds(List<String> companyIds) {
        return vehicleBaseInfoMapper.getCarPlatesByCompanyIds(companyIds);
    }

    /**
     * 根据企业ID获取所属企业和使用企业为该企业的车牌号
     *
     * @param companyId
     * @return
     */
    @Override
    public List<VehicleCompanyPO> getCarPlatesByUseCompanyId(Long companyId) {
        return vehicleBaseInfoMapper.getCarPlatesByUseCompanyId(companyId);
    }

    @Override
    @XmlDataScope
    public List<OverspeedStatisticsPO> getZj(DataScreenParam param) {
        return vehicleBaseInfoMapper.getZj(param);
    }

    @Override
    @XmlDataScope
    public List<OverspeedStatisticsPO> getCity(DataScreenParam param) {
        return vehicleBaseInfoMapper.getCity(param);
    }

    @Override
    @XmlDataScope
    public List<OverspeedStatisticsPO> getDistrict(DataScreenParam param) {
        return vehicleBaseInfoMapper.getDistrict(param);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public NdVehicleBaseInfo modifyAndUpdate(VehicleUpdateDTO data, Long userId) {
        UserRole userRole = userRoleService.getOne(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getUserId, userId));
        if (Objects.isNull(userRole)){
            throw new BizException("当前登录用户角色不存在!");
        }
        Long roleId = userRole.getRoleId();

        NdVehicleBaseInfo vehicleBaseInfo = vehicleBaseInfoMapper.selectById(data.getId());
        String oldCarPlate = vehicleBaseInfo.getCarPlate();
        VUtils.isTrueFormat(Objects.isNull(vehicleBaseInfo))
                .throwMessage(ExceptionCode.OPERATION_EX.getCode(), String.format("修改车辆%d 为空", data.getId()));

        //if(StringUtils.isNotBlank(data.getChassisNo())){
        //    VUtils.isTrue(!VinUtils.isLegal(data.getChassisNo())).throwMessage("底盘号错误 请重新填入!");
        //}

        //修改车牌号，原车辆的违法违章记录不变，以原车牌号为准，不用更新

        // 先保存旧的所属企业和使用企业，防止填写错误，审核不通过，回显错误
        vehicleBaseInfo.setBeforeCompanyId(vehicleBaseInfo.getCompanyId());
        vehicleBaseInfo.setBeforeUseCompanyId(vehicleBaseInfo.getUseCompanyId());
        vehicleBaseInfoMapper.updateById(vehicleBaseInfo);

        // 更新编辑过之后的车辆信息
        NdVehicleBaseInfo vehicle = BeanUtil.toBean(data, NdVehicleBaseInfo.class);
        // 如果修改了所属企业，车辆所属区域也要更新
        Long oldCompanyId = vehicleBaseInfo.getCompanyId();
        Long newCompanyId = vehicle.getCompanyId();
        if (Objects.isNull(oldCompanyId) || !newCompanyId.equals(oldCompanyId)){
            CompanyDTO companyDto = companiesCacheHelper.getById(newCompanyId);
            if (Objects.nonNull(companyDto)) {
                Long regionId = companyDto.getRegionId();
                if (Objects.nonNull(regionId)) {
                    vehicle.setRegionId(regionId);
                    vehicle.setRegionName(regionCacheHelper.getDisplayName(regionId));
                }
            }
        }

        String carPlate = vehicle.getCarPlate();
        checkCar(oldCarPlate,carPlate);
        // 最后修改人
        // 修改后，车辆审核状态变更为待审核
        // 如果登录用户为超管、市、区县散办不需要更改车辆状态为待审核
        if (UserRoleTypeEnum.COMPANY_INFO_OFFICER_ROLE_ID.getId().equals(roleId) ||
        UserRoleTypeEnum.DRIVER_ROLE_ID.getId().equals(roleId)) {
            vehicle.setAuditState(AuditStateEnum.AUDIT.getCode());
        }else if(Objects.nonNull(vehicleBaseInfo.getAuditState()) &&
                AuditStateEnum.AUDIT_FAIL.getCode().equals(vehicleBaseInfo.getAuditState())){
            //另外一种情况 不满足以上情况 但是审核失败需要重新提交的
            vehicle.setAuditState(AuditStateEnum.AUDIT.getCode());
        }
        vehicle.setUpdateBy(userId);
        if(updateById(vehicle)){
            vehiclesCacheHelper.refresh(vehicle.getId());
        }
        return vehicle;
    }

    @Override
    public Boolean updateOfflineNotInCarPlates(List<String> carsOnline) {
        return vehicleBaseInfoMapper.updateOfflineNotInCarPlates(carsOnline);
    }

    @Override
    public boolean batchUpdate(List<NdVehicleBaseInfo> batchSaveCars,Integer batchSize) {
        if (CollectionUtils.isEmpty(batchSaveCars)) {
            return false;
        }
        List<NdVehicleBaseInfo> infos = Lists.newArrayList();
        for (NdVehicleBaseInfo batchSaveCar : batchSaveCars) {
            NdVehicleBaseInfo car = new NdVehicleBaseInfo();
            car.setId(batchSaveCar.getId());
            car.setCarPlate(batchSaveCar.getCarPlate());
            car.setStatus(batchSaveCar.getStatus());
            car.setLastOnlineTime(batchSaveCar.getLastOnlineTime());
            car.setUpdateOn(batchSaveCar.getUpdateOn());
            infos.add(car);
            // 每当达到批次大小时就进行一次更新
            if (infos.size() == batchSize) {
                updateBatchById(infos);
                infos.clear();
            }
        }
        // 处理剩余不足批次大小的数据
        if (!infos.isEmpty()) {
            updateBatchById(infos);
            return true;
        }
        return false;
    }

    @Override
    public Map<String, NdVehicleBaseInfo> listByCarNo(Collection<String> carNos) {
        if (CollectionUtils.isEmpty(carNos)) {
            return Collections.emptyMap();
        }
        LambdaQueryWrapper<NdVehicleBaseInfo> query = Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                .in(NdVehicleBaseInfo::getCarPlate, carNos)
                .eq(NdVehicleBaseInfo::getAuditState, 2);
        List<NdVehicleBaseInfo> list = this.list(query);
        return CollUtils.listToMap(this.list(query), NdVehicleBaseInfo::getCarPlate);
    }

}
