package com.ruoyi.bst.order.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ruoyi.bst.area.domain.AreaVO;
import com.ruoyi.bst.area.domain.enums.AreaStatus;
import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.utils.DeviceUtil;
import com.ruoyi.bst.model.domain.ModelVO;
import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.bo.OrderDeductBO;
import com.ruoyi.bst.order.domain.bo.OrderEndBO;
import com.ruoyi.bst.order.domain.bo.OrderPayRideFeeBO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.dto.OrderDeductDTO;
import com.ruoyi.bst.order.domain.dto.OrderEndDTO;
import com.ruoyi.bst.order.domain.dto.OrderPayRideFeeDTO;
import com.ruoyi.bst.order.domain.enums.OrderStatus;
import com.ruoyi.bst.order.domain.vo.OrderCalcRideFeeVO;
import com.ruoyi.bst.order.domain.vo.OrderInParkingVO;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.order.mapper.OrderMapper;
import com.ruoyi.bst.order.service.OrderValidator;
import com.ruoyi.bst.order.utils.OrderUtil;
import com.ruoyi.bst.orderDevice.domain.OrderDeviceVO;
import com.ruoyi.bst.pay.service.PayValidator;
import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.bst.vipUser.domain.VipUserVO;
import com.ruoyi.bst.vipUser.utils.VipUserUtil;
import com.ruoyi.common.constants.ServiceCode;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.system.user.domain.enums.UserAuthType;
import com.ruoyi.system.user.utils.UserUtil;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class OrderValidatorImpl implements OrderValidator{

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private PayValidator payValidator;

    @Override
    public void validate(OrderCreateBO bo) {
        ServiceUtil.assertion(bo == null, "参数错误");

        OrderCreateDTO dto = bo.getDto();
        ServiceUtil.assertion(dto == null, "参数错误");

        // 设备
        DeviceVO device = bo.getDevice();
        this.canUseDevice(device, null);

        // 套餐
        SuitVO suit = bo.getSuit();
        ServiceUtil.assertion(suit == null, "ID为%s的套餐不存在", dto.getSuitId());

        // 车型
        ModelVO model = bo.getModel();
        ServiceUtil.assertion(model == null, "ID为%s的车型不存在", device.getModelId());
        ServiceUtil.assertion(CollectionUtils.isEmptyElement(model.getSuitIds()) || !model.getSuitIds().contains(suit.getId()), "ID为%s的套餐不可在ID为%s的车辆使用", dto.getSuitId(), dto.getDeviceId());

        // 用户
        UserVO user = bo.getUser();
        ServiceUtil.assertion(user == null, "ID为%s的用户不存在", dto.getUserId());
        if (device.getAreaAuthentication() != null) {
            for (String authType : device.getAreaAuthentication()) {
                boolean hasAuth = UserUtil.hasAuth(user, authType);
                if (!hasAuth) {
                    UserAuthType authTypeEnum = UserAuthType.getByCode(authType);
                    if (authTypeEnum == null) {
                        throw new ServiceException(String.format("运营区%s认证类型错误", device.getAreaName()));
                    }
                    throw new ServiceException(String.format("用户【%s】未认证【%s】", user.getNickName(), authTypeEnum.getName()), authTypeEnum.getErrorCode());
                }
            }
        }

        // 用户未支付的订单数量
        ServiceUtil.assertion(bo.getUnpaidCount() != null && bo.getUnpaidCount() > 0, "您有未支付的骑行费，请支付后下单");

        // 用户进行中的订单设备
        OrderDeviceVO userOrderDevice = bo.getUserOrderDevice();
        if (userOrderDevice != null) {
            throw new ServiceException(String.format("用户%s当前有订单在用车，请先结束订单。订单编号：%s", userOrderDevice.getOrderUserName(), userOrderDevice.getOrderNo()));
        }

        // 价格
        OrderPrePriceVO prePrice = bo.getPrePrice();
        ServiceUtil.assertion(prePrice == null, "价格计算失败");
        // 是否和前端传输的价格一致
        boolean priceEquals = OrderUtil.isEquals(prePrice, dto.getPrice());
        ServiceUtil.assertion(!priceEquals, "当前价格已发生变化，请刷新后重新下单");

        // 支付校验
        payValidator.checkPay(bo.getApp(), bo.getChannel(), bo.getUserApp(), bo.getChannelPayList(), bo.getCreditUser());
    }


    @Override
    public boolean canPay(OrderVO order, Long userId) {
        return isUser(order, userId);
    }

    @Override
    public boolean canEnd(OrderVO order, Long userId) {
        return isUser(order, userId);
    }

    // 是否是下单用户
    @Override
    public boolean isUser(OrderVO order, Long userId) {
        return order != null && userId != null && Objects.equals(order.getUserId(), userId);
    }

    @Override
    public void validate(OrderEndBO bo, boolean ignoreInParking) {
        OrderVO order = bo.getOrder();
        ServiceUtil.assertion(order == null, "参数错误，订单不存在");
        ServiceUtil.assertion(!OrderStatus.canEnd().contains(order.getStatus()), "ID为%s的订单状态不允许结束", order.getId());

        DeviceVO device = bo.getDevice();
        ServiceUtil.assertion(device == null, "参数错误，设备不存在");

        AreaVO area = bo.getArea();
        ServiceUtil.assertion(area == null, "参数错误，运营区不存在");

        // 还车校验
        OrderInParkingVO inParkingVO = bo.getInParkingVO();
        if (!ignoreInParking) {
            // 还车图片
            OrderEndDTO dto = bo.getDto();
            this.checkReturnPicture(order, dto.getPicture());
            // 停车区校验
            this.checkInParking(area, inParkingVO);
        }

    }

    @Override
    public boolean canOpenDevice(OrderVO order, Long userId) {
        return isUser(order, userId);
    }

    @Override
    public boolean canCloseDevice(OrderVO order, Long userId) {
        return isUser(order, userId);
    }

    @Override
    public boolean canChangeDevice(OrderVO order, Long userId) {
        return isUser(order, userId);
    }

    @Override
    public void checkInParking(AreaVO area, OrderInParkingVO inParkingVO) {
        log.info("【还车定位校验】运营区：{},定位数据：{}", area.getName(), inParkingVO);
        ServiceUtil.assertion(inParkingVO == null, "参数错误，停车点信息不存在");
        ServiceUtil.assertion(area == null, "参数错误，运营区信息不存在");

        if (inParkingVO.getParkingCount() != null && inParkingVO.getParkingCount() == 0) {
            log.warn("运营区{}的停车区数量为0，无需校验停车区", area.getName());
            return;
        }

        boolean mustInParking = area.getParkingReturn() != null && area.getParkingReturn();
        boolean inParking = inParkingVO.getInParking() != null && inParkingVO.getInParking();
        ServiceUtil.assertion(mustInParking && !inParking, "请将车辆停放在停车点内", ServiceCode.RETURN_MUST_IN_PARKING);

        boolean isInNoParking = inParkingVO.getInNoParking() != null && inParkingVO.getInNoParking();
        ServiceUtil.assertion(isInNoParking, "请勿将车辆停放在禁停区内", ServiceCode.RETURN_IN_NO_PARKING);

        boolean mustInArea = area.getAreaOutReturn() != null && !area.getAreaOutReturn();
        boolean inArea = inParkingVO.getInArea() != null && inParkingVO.getInArea();
        ServiceUtil.assertion(mustInArea && !inArea, "请将车辆停放在运营区内", ServiceCode.RETURN_MUST_IN_AREA);
    }

    @Override
    public void canUseDevice(DeviceVO device, OrderVO order) {
        // 设备本身的状态是否允许使用
        ServiceUtil.assertion(device == null || device.getId() == null, "车辆不存在");
        ServiceUtil.assertion(!DeviceStatus.canUse().contains(device.getStatus()), "ID为%s的车辆当前状态不可使用", device.getId());
        ServiceUtil.assertion(MathUtils.smallerThan(device.getRemainingPower(), device.getAreaUndercharge()), "ID为%s的车辆电量不足%s%%，暂时无法使用", device.getId(), device.getAreaUndercharge());
        ServiceUtil.assertion(DeviceUtil.isOrderUsing(device), "ID为%s的车辆当前有正在进行的订单，无法使用", device.getId());
        ServiceUtil.assertion(device.getAreaId() == null, "当前车辆未绑定运营区，无法使用");
        ServiceUtil.assertion(AreaStatus.DISABLED.getCode().equals(device.getAreaStatus()), "运营区“%s”已停运，无法使用该车辆", device.getAreaName());
        ServiceUtil.assertion(!DateUtils.isBetween(LocalTime.now(), device.getAreaTimeStart(), device.getAreaTimeEnd()), "运营区“%s”当前不在营业时间，无法使用该车辆", device.getAreaName());

        // 设备能否在该订单上使用（换车）
        if (order != null) {
            DeviceQuery query = OrderUtil.toChangeDeviceQuery(order);
            List<DeviceVO> list = deviceService.selectDeviceList(query);
            ServiceUtil.assertion(CollectionUtils.isEmptyElement(list), "当前订单无法使用ID为%s的车辆，请换一辆车或者重新下单。", device.getId());
            ServiceUtil.assertion(!CollectionUtils.map(list, DeviceVO::getId).contains(device.getId()), "当前订单无法使用ID为%s的车辆，请换一辆车或者重新下单。", device.getId());
        }
    }

    @Override
    public void checkReturnPicture(OrderVO order, String picture) {
        ServiceUtil.assertion(order == null, "参数错误，订单不存在");
        ServiceUtil.assertion(order != null
            && order.getAreaReturnVerify() != null
            && order.getAreaReturnVerify()
            && StringUtils.isBlank(picture), "请上传还车视频");
    }

    @Override
    public boolean canOperate(Long orderId) {
        return hasPermission(Collections.singletonList(orderId), AreaJoinPermission.ORDER_EDIT.getCode());
    }

    private boolean hasPermission(List<Long> orderIds, String permission) {
        if (CollectionUtils.isEmptyElement(orderIds)) {
            return true;
        }

        OrderQuery query = new OrderQuery();
        query.setScope(true);
        query.setIds(orderIds);
        query.addAreaPermission(permission);
        List<Long> list = orderMapper.selectIdByQuery(query);
        return new HashSet<>(list).containsAll(orderIds);
    }

    @Override
    public boolean isTimeout(OrderVO order) {
        return order != null && order.getMaxTime() != null && order.getMaxTime().isBefore(LocalDateTime.now());
    }

    @Override
    public boolean isUser(Long orderId, Long userId) {
        OrderVO order = orderMapper.selectOrderById(orderId);
        return isUser(order, userId);
    }

    @Override
    public void validate(OrderPayRideFeeBO bo) {
        ServiceUtil.assertion(bo == null, "参数错误");

        OrderVO order = bo.getOrder();
        OrderPayRideFeeDTO dto = bo.getDto();

        // 订单
        ServiceUtil.assertion(order == null, "ID为%s的订单不存在", dto.getOrderId());
        ServiceUtil.assertion(!OrderStatus.canPayRideFee().contains(order.getStatus()), "ID为%s的订单当前状态不允许支付骑行费", dto.getOrderId());

        // 校验费用
        OrderCalcRideFeeVO fee = bo.getFee();
        ServiceUtil.assertion(fee == null, "ID为%s的订单当前费用不存在", dto.getOrderId());
        ServiceUtil.assertion(!OrderUtil.isEquals(fee, dto.getFee()), "ID为%s的订单当前费用已发生变化，请刷新后重新支付", dto.getOrderId());

        // 用户
        UserVO user = bo.getUser();
        ServiceUtil.assertion(user == null, "ID为%s的用户不存在", dto.getUserId());
        ServiceUtil.assertion(!Objects.equals(order.getUserId(), user.getUserId()), "ID为%s的用户无权限支付ID为%s的订单", dto.getUserId(), dto.getOrderId());

        // 优惠券
        VipUserVO vipUser = bo.getVipUser();
        if (vipUser != null) {
            this.validateVipUser(vipUser, order, user, fee);
        }

        // 支付校验
        ChannelVO channel = bo.getChannel();
        payValidator.checkPay(bo.getApp(), channel, bo.getUserApp(), bo.getChannelPayList(), bo.getCreditUser());
    }

    private void validateVipUser(VipUserVO vipUser, OrderVO order, UserVO user, OrderCalcRideFeeVO fee) {
        ServiceUtil.assertion(!VipUserUtil.isValidTime(vipUser), "ID为%s的卡券不在可用期限内", vipUser.getId());
        ServiceUtil.assertion(vipUser.getSurplusTotal() != null && vipUser.getSurplusTotal() <= 0, "ID为%s的卡券剩余次数不足", vipUser.getId());
        ServiceUtil.assertion(MathUtils.smallerThan(fee.getOriginalAmount(), vipUser.getMinAmount()), "未达到卡券的使用门槛 %s 元", vipUser.getMinAmount());
        if (vipUser.getEnableLimit() != null && vipUser.getEnableLimit()) {
            int roundCount = MathUtils.dv(vipUser.getRoundCount());
            int limitCount = MathUtils.dv(vipUser.getLimitCount());
            ServiceUtil.assertion(roundCount >= limitCount, "ID为%s的卡券已达到本周期使用次数上限", vipUser.getId());
        }
        ServiceUtil.assertion(!Objects.equals(order.getAreaId(), vipUser.getAreaId()), "ID为%s的卡券不可在ID为%s的运营区使用", vipUser.getId(), order.getAreaId());
        ServiceUtil.assertion(!Objects.equals(vipUser.getUserId(), user.getUserId()), "ID为%s的用户无权使用ID为%s的卡券", user.getUserId(), vipUser.getId());
    }

    @Override
    public void validate(OrderDeductBO bo) {
        ServiceUtil.assertion(bo == null, "参数错误");

        OrderDeductDTO dto = bo.getDto();

        // 订单
        OrderVO order = bo.getOrder();
        ServiceUtil.assertion(order == null || order.getId() == null, "参数错误，订单不存在");
        ServiceUtil.assertion(!OrderStatus.canDeduct().contains(order.getStatus()), "ID为%s的订单当前状态不允许抵扣", order.getId());

        boolean depositDeduction = order.getSuitDepositDeduction() != null && order.getSuitDepositDeduction();
        ServiceUtil.assertion(!depositDeduction && !dto.getIsAdmin(), "ID为%s的订单不允许用户进行押金抵扣", order.getId());

        boolean isCredit = order.getIsCredit() != null && order.getIsCredit();
        ServiceUtil.assertion(isCredit, "ID为%s的订单为挂账订单，无法进行押金抵扣", order.getId());

        // 用户
        UserVO user = bo.getUser();
        ServiceUtil.assertion(user == null, "参数错误，用户不存在");

        // 费用
        OrderCalcRideFeeVO fee = bo.getFee();
        ServiceUtil.assertion(fee == null, "参数错误，费用不存在");

        // 卡券
        if (dto.getVipUserId() != null) {
            this.validateVipUser(bo.getVipUser(), order, user, fee);
        }

    }
}
