package com.njzscloud.supervisory.order.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import com.njzscloud.common.security.support.UserDetail;
import com.njzscloud.common.security.util.SecurityUtil;
import com.njzscloud.common.sn.support.SnUtil;
import com.njzscloud.supervisory.biz.contant.AuditStatus;
import com.njzscloud.supervisory.biz.contant.BizObj;
import com.njzscloud.supervisory.biz.pojo.entity.BizAuditConfigEntity;
import com.njzscloud.supervisory.biz.service.BizAuditConfigService;
import com.njzscloud.supervisory.order.contant.CheckStatus;
import com.njzscloud.supervisory.order.contant.OrderCategory;
import com.njzscloud.supervisory.order.contant.OrderStatus;
import com.njzscloud.supervisory.order.mapper.OrderInfoMapper;
import com.njzscloud.supervisory.order.pojo.entity.OrderGoodsEntity;
import com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity;
import com.njzscloud.supervisory.order.pojo.param.*;
import com.njzscloud.supervisory.order.pojo.result.ObtainWeighBillResult;
import com.njzscloud.supervisory.order.pojo.result.OrderCertificateResult;
import com.njzscloud.supervisory.order.pojo.result.OrderPagingResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 订单信息
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEntity> implements IService<OrderInfoEntity> {

    private final BizAuditConfigService bizAuditConfigService;
    private final OrderGoodsService orderGoodsService;
    private final OrderCargoPlaceService orderCargoPlaceService;
    private final OrderCarInOutService orderCarInOutService;

    /**
     * 新增
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(AddOrderInfoParam addOrderInfoParam) {
        AddOrderCargoPlaceParam cargoPlace = addOrderInfoParam.getCargoPlace();
        long cargoPlaceId = orderCargoPlaceService.add(cargoPlace);
        Long userId = SecurityUtil.currentUserId();
        String area = cargoPlace.getArea();
        BizAuditConfigEntity bizAuditConfigEntity = bizAuditConfigService.getOne(Wrappers.lambdaQuery(BizAuditConfigEntity.class).eq(BizAuditConfigEntity::getArea, area));

        AuditStatus auditStatus = AuditStatus.DaiShenHe;
        if (bizAuditConfigEntity != null) {
            String areaRole = bizAuditConfigEntity.getAreaRole();
            String cityRole = bizAuditConfigEntity.getCityRole();
            if (areaRole != null && cityRole != null) {
                auditStatus = AuditStatus.QuDaiShenHe;
            } else if (areaRole == null && cityRole != null) {
                auditStatus = AuditStatus.ShiDaiShenHe;
            }
        }

        Long transCompanyId = addOrderInfoParam.getTransCompanyId();
        OrderStatus orderStatus = transCompanyId == null ? OrderStatus.YiYuYue : OrderStatus.DaiPaiDan;

        Long goodsId = addOrderInfoParam.getGoodsId();

        Long orderGoodsId = orderGoodsService.add(goodsId);

        OrderInfoEntity orderInfoEntity = BeanUtil.copyProperties(addOrderInfoParam, OrderInfoEntity.class)
                .setSn(SnUtil.next())
                .setCargoPlaceId(cargoPlaceId)
                .setUserId(userId)
                .setAuditStatus(auditStatus)
                .setOrderStatus(orderStatus)
                .setGoodsId(orderGoodsId)
                .setCheckStatus(CheckStatus.WeiKanLiao);
        this.save(orderInfoEntity);

        Long orderId = orderInfoEntity.getId();
        OrderCategory orderCategory = addOrderInfoParam.getOrderCategory();
        if (orderCategory == OrderCategory.DuanBoChu) {
            this.add(addOrderInfoParam.setOrderCategory(OrderCategory.DuanBoRu)
                    .setTargetOrderId(orderId)
                    .setTargetStationId(addOrderInfoParam.getStationId())
                    .setStationId(addOrderInfoParam.getTargetStationId())
            );

        } else if (orderCategory == OrderCategory.DuanBoRu) {
            this.updateById(new OrderInfoEntity().setId(addOrderInfoParam.getTargetOrderId())
                    .setTargetOrderId(orderId));
        }
    }

    /**
     * 修改
     */
    public void modify(OrderInfoEntity orderInfoEntity) {
        this.updateById(orderInfoEntity);
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(List<Long> ids) {
        this.removeBatchByIds(ids);
    }

    /**
     * 详情
     */
    public OrderPagingResult detail(Long id) {
        return baseMapper.detail(Wrappers.<OrderPagingResult>query()
                .in("a.id", id));
    }

    /**
     * 分页查询
     */
    public PageResult<OrderPagingResult> paging(PageParam pageParam, OrderPagingSearchParam orderPagingSearchParam) {
        AuditStatus auditStatus = orderPagingSearchParam.getAuditStatus();
        String orderStatus = orderPagingSearchParam.getOrderStatus();
        List<String> orderStatusList = null;
        if (StrUtil.isNotBlank(orderStatus)) {
            orderStatusList = Arrays.stream(orderStatus.split(",")).collect(Collectors.toList());
        }
        CheckStatus checkStatus = orderPagingSearchParam.getCheckStatus();
        String sn = orderPagingSearchParam.getSn();
        String licensePlate = orderPagingSearchParam.getLicensePlate();
        String nickname = orderPagingSearchParam.getNickname();
        String phone = orderPagingSearchParam.getPhone();
        LocalDateTime startTime = orderPagingSearchParam.getStartTime();
        LocalDateTime endTime = orderPagingSearchParam.getEndTime();
        Page<OrderPagingResult> page = pageParam.toPage();
        OrderItem orderItem = new OrderItem();
        orderItem.setColumn("a.create_time");
        orderItem.setAsc(false);
        page.addOrder(orderItem);
        return PageResult.of(baseMapper.paging(page, Wrappers.<OrderPagingResult>query()
                .like(StrUtil.isNotBlank(sn), "a.sn", sn)
                .like(StrUtil.isNotBlank(licensePlate), "a.license_plate", licensePlate)
                .like(StrUtil.isNotBlank(nickname), "h.nickname", nickname)
                .like(StrUtil.isNotBlank(phone), "h.phone", phone)
                .eq(auditStatus != null, "a.audit_status", auditStatus)
                .in(CollUtil.isNotEmpty(orderStatusList), "a.order_status", orderStatusList)
                .eq(checkStatus != null, "a.check_status", checkStatus)
                .ge(startTime != null, "a.create_time", startTime)
                .le(endTime != null, "a.create_time", endTime)
        ));
    }

    public PageResult<OrderPagingResult> auditPaging(PageParam pageParam, OrderPagingSearchParam orderPagingSearchParam) {
        Set<String> roles = orderPagingSearchParam.getRoles();
        List<String> areas = bizAuditConfigService.list(Wrappers.<BizAuditConfigEntity>query()
                .select("DISTINCT area")
                .in("area_role", roles)
                .or().in("city_role", roles)
        ).stream().map(BizAuditConfigEntity::getArea).collect(Collectors.toList());
        if (CollUtil.isEmpty(areas)) {
            return PageResult.of(pageParam.toPage());
        }
        QueryWrapper<OrderPagingResult> ew = Wrappers.<OrderPagingResult>query()
                .in("b.area", areas);
        AuditStatus auditStatus = orderPagingSearchParam.getAuditStatus();

        if (auditStatus != null) {
            if (auditStatus == AuditStatus.DaiShenHe) {
                ew.eq("a.order_status", OrderStatus.YiJieDan)
                        .in("a.audit_status", Arrays.asList(AuditStatus.QuDaiShenHe.getVal(), AuditStatus.ShiDaiShenHe.getVal()));
            } else {
                ew.in("a.audit_status", Collections.singletonList(auditStatus.getVal()));
            }
        }

        return PageResult.of(baseMapper.paging(pageParam.toPage(), ew));
    }

    public void audit(AuditOrderParam auditOrderParam) {
        OrderPagingResult detail = detail(auditOrderParam.getId());
        Assert.notNull(detail, () -> Exceptions.clierr("订单不存在"));
        AuditStatus auditStatus = detail.getAuditStatus();
        Assert.isTrue(auditStatus == AuditStatus.QuDaiShenHe
                        || auditStatus == AuditStatus.ShiDaiShenHe,
                () -> Exceptions.clierr("订单已审核"));

        AuditStatus newAuditStatus = auditOrderParam.getAuditStatus();

        Assert.isTrue(newAuditStatus == AuditStatus.TongGuo || newAuditStatus == AuditStatus.BoHui, () -> Exceptions.clierr("未指明审核结果"));

        Set<String> roles = SecurityUtil.loginUser().getRoles();
        BizAuditConfigEntity config = bizAuditConfigService.getOne(Wrappers.<BizAuditConfigEntity>query()
                .eq("area", detail.getArea()));
        String areaRole = config.getAreaRole();
        String cityRole = config.getCityRole();
        if (!roles.contains(areaRole) && !roles.contains(cityRole)) {
            throw Exceptions.clierr("您没有权限审核该订单");
        }

        if (auditStatus == AuditStatus.QuDaiShenHe) {
            if (StrUtil.isNotBlank(areaRole) && roles.contains(areaRole)) {
                auditStatus = newAuditStatus == AuditStatus.TongGuo ? AuditStatus.ShiDaiShenHe : newAuditStatus;
            } else {
                throw Exceptions.clierr("您没有权限审核该订单");
            }
        } else {
            if (StrUtil.isNotBlank(cityRole) && roles.contains(cityRole)) {
                auditStatus = newAuditStatus;
            } else {
                throw Exceptions.clierr("您没有权限审核该订单");
            }
        }

        detail.setAuditStatus(newAuditStatus);
        detail.setAuditMemo(auditOrderParam.getAuditMemo());
        this.updateById(new OrderInfoEntity()
                .setId(detail.getId())
                .setAuditStatus(auditStatus)
                .setAuditMemo(auditOrderParam.getAuditMemo())
        );
    }

    @Transactional(rollbackFor = Exception.class)
    public void check(CheckGoodsParam checkGoodsParam) {
        Long orderId = checkGoodsParam.getOrderId();
        OrderInfoEntity oldOrder = this.getById(orderId);
        Assert.notNull(oldOrder, () -> Exceptions.clierr("订单不存在"));
        CheckStatus checkStatus = oldOrder.getCheckStatus();
        Assert.isFalse(checkStatus == null, () -> Exceptions.clierr("当前订单无需看料"));
        Assert.isFalse(CheckStatus.YiKanLiao == checkStatus, () -> Exceptions.clierr("当前订单已看料"));

        this.updateById(new OrderInfoEntity()
                .setId(orderId)
                .setCheckPhoto(checkGoodsParam.getCheckPhoto())
                .setCheckStatus(CheckStatus.YiKanLiao)
                .setCheckerId(SecurityUtil.currentUserId())
        );
    }

    public void assignment(AssignmentOrderParam assignmentOrderParam) {
        OrderInfoEntity oldOrder = this.getById(assignmentOrderParam.getOrderId());
        Assert.notNull(oldOrder, () -> Exceptions.clierr("订单不存在"));
        boolean assignmented = assignmentTrafficCompany(assignmentOrderParam, oldOrder);
        if (assignmented) {
            oldOrder.setOrderStatus(OrderStatus.DaiPaiDan);
        }
        assignmentDriver(assignmentOrderParam, oldOrder);
    }

    private void assignmentDriver(AssignmentOrderParam assignmentOrderParam, OrderInfoEntity orderInfo) {
        Long driverId = assignmentOrderParam.getDriverId();
        if (driverId == null) return;
        Assert.isNull(orderInfo.getDriverId(), () -> Exceptions.clierr("当前订单已分配司机，无需重复分配"));
        Assert.isTrue(orderInfo.getOrderStatus() == OrderStatus.DaiPaiDan, () -> Exceptions.clierr("当前订单状态，无法分配司机"));

        this.updateById(new OrderInfoEntity()
                .setId(orderInfo.getId())
                .setDriverId(driverId)
                .setOrderStatus(OrderStatus.DaiJieDan)
        );
    }

    private boolean assignmentTrafficCompany(AssignmentOrderParam assignmentOrderParam, OrderInfoEntity orderInfo) {
        Long transCompanyId = assignmentOrderParam.getTransCompanyId();
        if (transCompanyId == null) return false;
        Assert.isNull(orderInfo.getTransCompanyId(), () -> Exceptions.clierr("当前订单已分配清运公司，无需重复分配"));
        Assert.isTrue(orderInfo.getOrderStatus() == OrderStatus.YiYuYue, () -> Exceptions.clierr("当前订单状态，无法分配清运公司"));

        this.updateById(new OrderInfoEntity()
                .setId(orderInfo.getId())
                .setTransCompanyId(transCompanyId)
                .setOrderStatus(OrderStatus.DaiPaiDan)
        );
        return true;
    }

    public void confirm(Long orderId, Long truckId) {
        OrderInfoEntity orderInfo = this.getById(orderId);
        Assert.notNull(orderInfo, () -> Exceptions.clierr("订单不存在"));
        Assert.isTrue(orderInfo.getOrderStatus() == OrderStatus.DaiJieDan, () -> Exceptions.clierr("当前订单状态，无法确认接单"));
        this.updateById(new OrderInfoEntity()
                .setId(orderInfo.getId())
                .setTruckId(truckId)
                .setOrderStatus(OrderStatus.YiJieDan)
        );
    }

    @Transactional(rollbackFor = Exception.class)
    public void startTrans(StartTransportOrderParam startTransportOrderParam) {
        OrderInfoEntity orderInfo = this.getById(startTransportOrderParam.getOrderId());
        Assert.notNull(orderInfo, () -> Exceptions.clierr("订单不存在"));
        Assert.isTrue(orderInfo.getOrderStatus() == OrderStatus.YiJieDan, () -> Exceptions.clierr("当前订单状态，无法开始运输"));
        this.updateById(new OrderInfoEntity()
                .setId(orderInfo.getId())
                .setCargoPhoto(startTransportOrderParam.getCargoPhoto())
                .setOrderStatus(OrderStatus.QingYunZhong)
                .setTransTime(LocalDateTime.now())
        );
        // TODO 获取 GPS
    }

    public void cancel(Long orderId) {
        OrderInfoEntity orderInfo = this.getById(orderId);
        Assert.notNull(orderInfo, () -> Exceptions.clierr("订单不存在"));
        OrderStatus orderStatus = orderInfo.getOrderStatus();
        Assert.isTrue(orderStatus == OrderStatus.YiYuYue
                        || orderStatus == OrderStatus.DaiPaiDan
                        || orderStatus == OrderStatus.DaiJieDan
                , () -> Exceptions.clierr("当前订单状态，无法取消"));
        this.updateById(new OrderInfoEntity()
                .setId(orderInfo.getId())
                .setOrderStatus(OrderStatus.YiQuXiao)
        );
        // TODO 关闭 GPS
    }

    @Transactional(rollbackFor = Exception.class)
    public void modifyGoods(ModifyOrderGoodsParam modifyOrderGoodsParam) {
        OrderInfoEntity orderInfoEntity = this.getById(modifyOrderGoodsParam.getOrderId());
        Assert.notNull(orderInfoEntity, () -> Exceptions.clierr("订单不存在"));
        OrderStatus orderStatus = orderInfoEntity.getOrderStatus();
        Assert.isTrue(orderStatus != OrderStatus.YiChuChang
                        && orderStatus != OrderStatus.YiWanCheng
                        && orderStatus != OrderStatus.YiQuXiao
                , () -> Exceptions.clierr("当前订单状态，无法修改商品"));
        orderGoodsService.modify(new OrderGoodsEntity()
                .setId(orderInfoEntity.getGoodsId())
                .setGoodsCategoryId(modifyOrderGoodsParam.getGoodsCategoryId())
                .setOriginGoodsId(modifyOrderGoodsParam.getOriginGoodsId())
        );
    }

    /**
     * 车辆进场
     */
    @Transactional(rollbackFor = Exception.class)
    public void truckComing(TruckComingOrderParam truckComingOrderParam) {
        OrderInfoEntity orderInfoEntity = this.getById(truckComingOrderParam.getOrderId());
        Assert.notNull(orderInfoEntity, () -> Exceptions.clierr("订单不存在"));
        OrderStatus orderStatus = orderInfoEntity.getOrderStatus();
        Assert.isTrue(orderStatus == OrderStatus.QingYunZhong, () -> Exceptions.clierr("当前订单状态，无法进场"));

        OrderCategory orderCategory = orderInfoEntity.getOrderCategory();
        long carInOutId = orderCarInOutService.truckComing(truckComingOrderParam, orderCategory);

        this.updateById(new OrderInfoEntity()
                .setId(orderInfoEntity.getId())
                .setCarInOutId(carInOutId)
                .setOrderStatus(OrderStatus.YiJinChang)
        );
    }

    /**
     * 车辆出场
     */
    @Transactional(rollbackFor = Exception.class)
    public void truckLeaving(TruckLeavingOrderParam truckLeavingOrderParam) {
        OrderInfoEntity orderInfoEntity = this.getById(truckLeavingOrderParam.getOrderId());
        Assert.notNull(orderInfoEntity, () -> Exceptions.clierr("订单不存在"));
        OrderStatus orderStatus = orderInfoEntity.getOrderStatus();
        Assert.isTrue(orderStatus == OrderStatus.YiJinChang, () -> Exceptions.clierr("当前订单状态，无法出场"));
        this.updateById(new OrderInfoEntity()
                .setId(orderInfoEntity.getId())
                .setOrderStatus(OrderStatus.YiWanCheng)
        );
        OrderCategory orderCategory = orderInfoEntity.getOrderCategory();
        Long truckId = orderInfoEntity.getTruckId();
        Integer settleWeight = orderCarInOutService.truckOut(orderInfoEntity.getCarInOutId(), truckLeavingOrderParam, orderCategory, truckId);
        orderGoodsService.updateById(new OrderGoodsEntity()
                .setId(orderInfoEntity.getGoodsId())
                .setQuantity(settleWeight));
        // TODO 关闭 GPS

    }

    public OrderPagingResult pendingOrder(String licensePlate) {
        return baseMapper.pendingOrder(Wrappers.query()
                .eq("e.license_plate", licensePlate)
                .in("a.order_status", Arrays.asList(OrderStatus.QingYunZhong, OrderStatus.YiJinChang))
                .orderByDesc("a.trans_time")
        );
    }

    public PageResult<OrderPagingResult> dispatchPaging(PageParam pageParam, OrderPagingSearchParam orderPagingSearchParam) {
        AuditStatus auditStatus = orderPagingSearchParam.getAuditStatus();
/*         String orderStatus = orderPagingSearchParam.getOrderStatus();
        List<String> orderStatusList = null;
        if (StrUtil.isNotBlank(orderStatus)) {
            orderStatusList = Arrays.stream(orderStatus.split(",")).collect(Collectors.toList());
        } */
        CheckStatus checkStatus = orderPagingSearchParam.getCheckStatus();
        String sn = orderPagingSearchParam.getSn();
        String licensePlate = orderPagingSearchParam.getLicensePlate();
        String nickname = orderPagingSearchParam.getNickname();
        String phone = orderPagingSearchParam.getPhone();
        LocalDateTime startTime = orderPagingSearchParam.getStartTime();
        LocalDateTime endTime = orderPagingSearchParam.getEndTime();
        Page<OrderPagingResult> page = pageParam.toPage();
        OrderItem orderItem = new OrderItem();
        orderItem.setColumn("a.create_time");
        orderItem.setAsc(false);
        page.addOrder(orderItem);
        QueryWrapper<OrderPagingResult> ew = Wrappers.<OrderPagingResult>query()
                .like(StrUtil.isNotBlank(sn), "a.sn", sn)
                .like(StrUtil.isNotBlank(licensePlate), "a.license_plate", licensePlate)
                .like(StrUtil.isNotBlank(nickname), "h.nickname", nickname)
                .like(StrUtil.isNotBlank(phone), "h.phone", phone)
                .eq(auditStatus != null, "a.audit_status", auditStatus)
                // .in(CollUtil.isNotEmpty(orderStatusList), "a.order_status", orderStatusList)
                .eq(checkStatus != null, "a.check_status", checkStatus)
                .ge(startTime != null, "a.create_time", startTime)
                .le(endTime != null, "a.create_time", endTime);
        UserDetail userDetail = SecurityUtil.loginUser();
        BizObj bizObj = Dict.parse(userDetail.getBizObj(), BizObj.values());
        if (bizObj != null) {
            switch (bizObj) {
                case QiYe:
                case GeTi:
                    ew.eq("a.order_status", OrderStatus.DaiPaiDan)
                            .isNotNull("a.trans_company_id");
                    break;
                default:
                    ew.isNull("a.trans_company_id");
            }
        } else {
            ew.isNull("a.trans_company_id");
        }
        return PageResult.of(baseMapper.paging(page, ew));
    }

    public OrderCertificateResult certificate(OrderCertificateParam orderCertificateParam) {
        Integer width = orderCertificateParam.getWidth();
        Integer height = orderCertificateParam.getHeight();
        if (width == null) {
            width = 300;
        }
        if (height == null) {
            height = 300;
        }
        String imgType = orderCertificateParam.getImgType();
        if (imgType == null) {
            imgType = ImgUtil.IMAGE_TYPE_JPG;
        }

        String qrcode = QrCodeUtil.generateAsBase64(orderCertificateParam.getLink(), new QrConfig(width, height), imgType);

        return new OrderCertificateResult()
                .setOrderDetail(this.detail(orderCertificateParam.getOrderId()))
                .setCertificateQrcode(qrcode);
    }

    public ObtainWeighBillResult obtainWeighBill(Long orderId) {
        return baseMapper.obtainWeighBill(orderId);
    }
}
