package com.ruoyi.app.order;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.app.order.dto.OrderCreateInput;
import com.ruoyi.app.order.dto.OrderCreatItem;
import com.ruoyi.app.order.dto.OrderPushBodyDto;
import com.ruoyi.app.pay.LinePayRefundService;
import com.ruoyi.app.utils.DateUtil;
import com.ruoyi.app.utils.OperatingUtil;
import com.ruoyi.app.utils.PayPush;
import com.ruoyi.app.utils.event.PushEventService;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.PosOrderPromotionMapper;
import com.ruoyi.system.service.*;
import com.ruoyi.system.utils.Auth;
import com.ruoyi.system.utils.GetArea;
import com.ruoyi.system.utils.JwtUtil;
import com.ruoyi.system.utils.OrderLogHelper;
import com.ruoyi.system.dto.PromotionCalcResponse;
import com.ruoyi.system.domain.dto.ApplyInvoiceDto;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.TimerTask;
import java.util.stream.Collectors;

/**
 * 用户订单Controller
 *
 * @author ruoyi
 * @date 2020-04-01
 */
@RestController
@RequestMapping("/system/userOrder")
public class UserOrderController extends BaseController {
    @Autowired
    private IOrderParentService orderParentService;
    @Autowired
    private IPosOrderService posOrderService;
    @Autowired
    private IPosStoreService posStoreService;
    @Autowired
    private IInfoAddressService infoAddressService;
    @Autowired
    private IOperatingHoursService operatingHoursService;
    @Autowired
    private IUserFootprintService userFootprintService;
    @Autowired
    private IInfoUserService infoUserService;
    @Autowired
    private PushEventService pushEventService;
    @Autowired
    private OrderLogHelper orderLogHelper;
    @Autowired
    private OrderPromotionHelper orderPromotionHelper;
    @Autowired
    private PosOrderPromotionMapper posOrderPromotionMapper;
    @Autowired
    private OrderInvoiceService orderInvoiceService;
    @Autowired
    private IInfoInvoiceService infoInvoiceService;
    @Autowired
    private IPosOrderLinePaymentService linePaymentService;
    @Autowired
    private LinePayRefundService linePayRefundService;
    @Autowired
    private OrderLifecycleService orderLifecycleService;
    @Autowired
    private DeliveryOrderNotificationService deliveryOrderNotificationService;
    @Autowired
    private MerchantNotificationRouter merchantNotificationRouter;

    /**
     * 申请电子发票（订单完成后，客户主动申请：B2C 邮箱 / B2B 统编 / 载具）
     */

    @PostMapping("/applyInvoice")
    @Auth
    @Anonymous
    public AjaxResult applyInvoice(@RequestHeader String token, @RequestBody @Valid ApplyInvoiceDto dto) {
        Long userId = Long.valueOf(new JwtUtil().getusid(token));
        return AjaxResult.success(orderInvoiceService.applyInvoice(dto, userId));
    }

    /**
     * 查询订单电子发票（状态 / 发票号 / 凭证）；门店不可开票时 invoiceStatus=null
     */

    @GetMapping("/getInvoice")
    @Auth
    @Anonymous
    public AjaxResult getInvoice(@RequestHeader String token, @RequestParam Long orderId) {
        Long userId = Long.valueOf(new JwtUtil().getusid(token));
        return AjaxResult.success(orderInvoiceService.getInvoice(orderId, userId));
    }

    /**
     * 捐赠机构列表（客户端选择器，仅 enabled=1，按机构名/简称/捐赠码搜索；分页）
     */
    @GetMapping("/loveOrg/list")
    @Auth
    @Anonymous
    public TableDataInfo loveOrgList(@RequestHeader String token,
                                     @RequestParam(defaultValue = "1") int pageNum,
                                     @RequestParam(defaultValue = "20") int pageSize,
                                     @RequestParam(required = false) String keyword) {
        PageHelper.startPage(pageNum, pageSize);
        return getDataTable(orderInvoiceService.listLoveOrg(keyword));
    }

    /**
     * 会员发票夹（010 Phase 11）：我的发票列表，按 userId 查已开发票（不含码图）。
     */
    @GetMapping("/myInvoices")
    @Auth
    @Anonymous
    public TableDataInfo myInvoices(@RequestHeader String token,
                                    @RequestParam(defaultValue = "1") int pageNum,
                                    @RequestParam(defaultValue = "10") int pageSize) {
        Long userId = Long.valueOf(new JwtUtil().getusid(token));
        PageHelper.startPage(pageNum, pageSize);
        List<com.ruoyi.system.domain.vo.PosOrderInvoiceVo> list = orderInvoiceService.listMyInvoices(userId);
        return getDataTable(list);
    }

    /**
     * 创建订单
     */
    @PostMapping("/createOrder")
    @Auth
    @Anonymous
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult createOrder(@RequestHeader String token, @RequestBody OrderCreateInput input)
    {
        JwtUtil jwtUtil = new JwtUtil();
        String id = jwtUtil.getusid(token);
        String shdz=null;
        if(input.getType()==0){
            InfoAddress usadd = infoAddressService.getById(input.getShdzId());
            if (usadd == null) {
                throw new ServiceException(MessageUtils.message("no.address.not.exist"));
            }
            shdz= JSON.toJSONString(usadd);
        }
        // 现金支付(4)仅商家创建订单可用，客户端下单直接拒绝
        if (OrderLifecycleService.PAY_TYPE_CASH.equals(input.getPaymentMethod())) {
            throw new ServiceException(MessageUtils.message("no.paytype.cash.merchant.only"));
        }
        // === 发票意图（010 Phase 11）===
        // 校验：载具/捐赠/统编验真前置，不通过拦截下单（联网校验用第一个可开票门店的 ezPay 凭证）
        if (!StringUtils.isEmpty(input.getInvoiceChoice()) && !"PAPER".equals(input.getInvoiceChoice())) {
            Long invoiceStoreId = input.getItems() == null ? null : input.getItems().stream()
                    .map(OrderCreatItem::getMdId)
                    .filter(md -> orderInvoiceService.canInvoice(md))
                    .findFirst().orElse(null);
            if (invoiceStoreId == null) {
                throw new ServiceException("所选门店暂不支持电子发票");
            }
            orderInvoiceService.validateCheckoutInvoiceIntent(input.getInvoiceChoice(), input.getCarrierNum(),
                    input.getLoveCode(), input.getBuyerUbn(), input.getBuyerEmail(), invoiceStoreId);
        }
        createOrderParent(input,Long.valueOf(id),shdz);
        createOrderChild(input,Long.valueOf(id),Long.valueOf(id),shdz);
        // saveAsDefault：把载具/统编存为默认抬头（同一用户仅一条 is_default=1）
        if (Boolean.TRUE.equals(input.getSaveAsDefault()) && !StringUtils.isEmpty(input.getInvoiceChoice())) {
            saveDefaultInvoiceProfile(input, Long.valueOf(id));
        }

        OrderParent result = orderParentService.getOne(new LambdaQueryWrapper<OrderParent>().eq(OrderParent::getDdId, input.getDdId().toString()));
        System.out.println("创建返回信息："+result);
        return super.success(result);
    }

    //创建父订单
    private void createOrderParent(OrderCreateInput input,Long UserId,String shdz){
        OrderParent orderParent = new OrderParent();
        orderParent.setUserId(UserId);
        orderParent.setOrderStatus(0L);
        orderParent.setTotalAmount(input.getTotalAmount());
        orderParent.setActualPayAmount(input.getActualPayAmount());
        orderParent.setPaymentMethod(input.getPaymentMethod());
//        orderParent.setPaymentTime(new Date());
        orderParent.setCreateTime(new Date());
//        orderParent.setUpdateTime(new Date());
        orderParent.setDdId(input.getDdId().toString());
        orderParent.setTableId(input.getTableId());
        orderParent.setShdzId(input.getShdzId());
        orderParent.setShAddress(shdz);
        orderParent.setType(input.getType());
        orderParent.setRemarks(input.getRemarks());
        orderParentService.save(orderParent);
    }
    //创建子订单
    private void createOrderChild(OrderCreateInput input,Long orderParentId,Long userId,String shdz){
        // 检查items是否为空
        if (input.getItems() == null || input.getItems().isEmpty()) {
            throw new ServiceException(MessageUtils.message("订单商品不能为空"));
        }
        String ddId = input.getDdId().toString();
        List<Long> mdIdList = input.getItems().stream().map(OrderCreatItem::getMdId).collect(Collectors.toList());
        // 将Long类型的门店ID转换为Integer类型用于查询（因为PosStore.getId()返回Integer）
        List<Integer> mdIdIntegerList = mdIdList.stream().map(Long::intValue).collect(Collectors.toList());
        List<PosStore> storeList = posStoreService.list(new LambdaQueryWrapper<PosStore>().in(PosStore::getId, mdIdIntegerList));
        LambdaQueryWrapper<OperatingHours> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(OperatingHours::getMdId, mdIdList);
        List<OperatingHours> hourslist = operatingHoursService.list(wrapper);
        boolean isMultiStore = input.getItems().size() > 1;
        // 循环items，为每个item创建一条PosOrder
        int index = 0;
        for (OrderCreatItem item : input.getItems()) {

            // 检查门店是否开放
            checkStoreOpen(storeList,hourslist,item);
            index++;
            // 单店铺：子订单号与父订单一致；多店铺：子订单号追加序号
            String subddId = isMultiStore ? ddId + String.format("%03d", index) : ddId;
            PosOrder posOrder = new PosOrder();
            posOrder.setIsDisplay(false);
            handleIsDisplay(posOrder);
            posOrder.setShdzId(input.getShdzId());
            // 设置子订单的基本信息
            posOrder.setDdId(subddId);
            posOrder.setParentDdId(input.getDdId().toString()); // 设置父订单ID
            posOrder.setCretim(new Date());
            // 根据item设置订单信息
            posOrder.setShId(item.getShId());
            posOrder.setMdId(item.getMdId());
            posOrder.setShdzId(input.getShdzId());
            posOrder.setUserId(userId);
            posOrder.setAmount(item.getAmount());
            posOrder.setRemarks(item.getRemarks());
            posOrder.setType(item.getType());
            posOrder.setDelryTime(item.getDelryTime());
            posOrder.setFood(JSON.toJSONString(item.getFood()));
            if("1".equals(input.getPaymentMethod())){
                posOrder.setCollectPayment("1");
            }
            // 设置支付相关信息（从父订单继承）
            posOrder.setPayType(input.getPaymentMethod());
            //设置桌号（从父订单继承）
            posOrder.setTableId(input.getTableId());
            posOrder.setTableNo(input.getTableNo());
            posOrder.setLogo(item.getLogo());
            posOrder.setPosName(item.getPosName());
            posOrder.setJvli(item.getJvli());
            posOrder.setFreight(item.getFreight());
            posOrder.setShdzId(input.getShdzId());
            posOrder.setShAddress(shdz);
            posOrder.setFoodAmount(item.getFoodAmount());
            posOrder.setPickUpTime(input.getPickUpTime());
            posOrder.setReservePhone(input.getReservePhone());
            // 新状态字段初始化
            // 堂食订单(type=2)自动接单，跳过商家确认
            if (item.getType() != null && item.getType() == 2) {
                posOrder.setState(1L);
            } else {
                posOrder.setState(0L);
            }
            posOrder.setAfterSaleStatus(0L);
            posOrder.setDeliveryStatus(Long.valueOf(0L).equals(item.getType()) ? 0L : null);
            // 外送到付(payType=1)：payStatus=1；其他：payStatus=0
            if (item.getType() != null && item.getType() == 0 && "1".equals(input.getPaymentMethod())) {
                posOrder.setPayStatus(1L);
            } else {
                posOrder.setPayStatus(0L);
            }
           Optional<PosStore> storeOptional = storeList.stream()
                   .filter(x -> item.getMdId().equals(Long.valueOf(x.getId())))
                   .findFirst();
           if (storeOptional.isEmpty()) {
               throw new ServiceException(MessageUtils.message("no.mendian.not.exist"));
           }
           PosStore store = storeOptional.get();
            posOrder.setLongitude(store.getLongitude());
            posOrder.setLatitude(store.getLatitude());
            //设置订单分类
            storeList.stream().filter(s -> Long.valueOf(s.getId()).equals(item.getMdId())).findFirst().ifPresent(st -> {
                posOrder.setOrderCategory(String.valueOf(st.getType()));
            });
            // 保存子订单
            // --- 促销：算价 + 校验 + 回填优惠字段（save之前） ---
            PromotionCalcResponse calc = orderPromotionHelper.calcPromotion(item, userId);
            orderPromotionHelper.validatePromotionAmount(calc, item.getPromoReduceAmount());
            orderPromotionHelper.fillPromotionFields(posOrder, calc);

            createFootprint(userId, item.getMdId());
            setPickUpNum(posOrder);
            posOrderService.save(posOrder);
            // 发票意图落 pos_order_invoice 意图行（010 Phase 11，方案B）：仅开发票门店插，
            // issueTriggered=0 未触发；出餐 autoIssue 在该行落结果并置 issueTriggered=1
            if (!StringUtils.isEmpty(input.getInvoiceChoice()) && orderInvoiceService.canInvoice(item.getMdId())) {
                String choice = input.getInvoiceChoice();
                PosOrderInvoice intent = new PosOrderInvoice();
                intent.setOrderId(posOrder.getId());
                intent.setOrderNo(subddId);
                intent.setStoreId(item.getMdId());
                intent.setInvoiceChoice(choice);
                intent.setCarrierNum(input.getCarrierNum());
                intent.setBuyerUbn(input.getBuyerUbn());
                intent.setBuyerName(input.getBuyerName());
                intent.setBuyerEmail(input.getBuyerEmail());
                intent.setLoveCode(input.getLoveCode());
                if ("PHONE_BARCODE".equals(choice)) {
                    intent.setCarrierType("0");
                } else if ("CITIZEN".equals(choice)) {
                    intent.setCarrierType("1");
                } else if ("MEMBER".equals(choice)) {
                    // 【决定 2026-08-05】会员载具走 PrintFlag=Y 纸本，不传 ezPay 电子载具(见 OrderInvoiceService.buildAutoIssueDto 决策备注)；carrierType 留空
                } else {
                    intent.setCarrierType(input.getCarrierType());
                }
                orderInvoiceService.captureInvoiceIntent(intent);
            }

            // --- 促销：写入快照 + 标记券已使用（save之后，需要posOrder.id） ---
            if (calc != null) {
                orderPromotionHelper.savePromotionSnapshots(posOrder.getId(), calc);
                if (calc.getCouponId() != null) {
                    orderPromotionHelper.markCouponUsed(calc.getCouponId(), posOrder.getId(), userId);
                }
            }

            // 创建订单成功，添加操作日志
            InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, userId));
            String uName = uUser != null ? uUser.getNickName() : "";
            orderLogHelper.log(subddId, 4, userId, uName, "用户" + uName + "创建订单");

            if (Long.valueOf(0L).equals(posOrder.getType())) {
                // 到付订单创建后立即开放；在线支付订单由支付成功回调开放。
                deliveryOrderNotificationService.notifyOrderAvailable(posOrder);
            } else {
                // 自取和堂食保持原流程，创建后直接通知商家。
                String body = OrderPushBodyDto.getJson(subddId, String.valueOf(posOrder.getState()), 0);
                AsyncManager.me().execute(new TimerTask() {
                    @Override
                    public void run() {
                        merchantNotificationRouter.sendStoreNotification(posOrder.getMdId(), posOrder.getShId(),
                                "no.message.push.message", "no.message.push.new.order", body, subddId);
                    }
                });
            }
        }
    }

    //保存默认发票抬头（010 Phase 11：结账勾「同意设置为默认」；同一用户仅一条 is_default=1）
    private void saveDefaultInvoiceProfile(OrderCreateInput input, Long userId) {
        String choice = input.getInvoiceChoice();
        if ("PAPER".equals(choice) || "LOVE_CODE".equals(choice) || "MEMBER".equals(choice)) {
            return; // 纸本/捐赠/会员载具不存抬头（会员载具=会员手机号，无需记忆）
        }
        InfoInvoice profile = new InfoInvoice();
        profile.setUserId(userId);
        profile.setIsDefault(1);
        switch (choice) {
            case "PHONE_BARCODE":
                profile.setCategory("B2C");
                profile.setTitleName("手机条码载具");
                profile.setBuyerName("手机条码载具");
                profile.setCarrierType("0");
                profile.setCarrierNum(input.getCarrierNum());
                break;
            case "CITIZEN":
                profile.setCategory("B2C");
                profile.setTitleName("自然人凭证载具");
                profile.setBuyerName("自然人凭证载具");
                profile.setCarrierType("1");
                profile.setCarrierNum(input.getCarrierNum());
                break;
            case "COMPANY":
                profile.setCategory("B2B");
                profile.setTitleName(input.getBuyerName());
                profile.setBuyerName(input.getBuyerName());
                profile.setBuyerUbn(input.getBuyerUbn());
                break;
            default:
                return;
        }
        // 先清掉该用户其它默认（同一用户仅一条为 1），再存新的
        InfoInvoice reset = new InfoInvoice();
        reset.setIsDefault(0);
        infoInvoiceService.update(reset, new LambdaQueryWrapper<InfoInvoice>()
                .eq(InfoInvoice::getUserId, userId)
                .eq(InfoInvoice::getIsDefault, 1));
        infoInvoiceService.save(profile);
    }

    //生成取餐码，根据门店id,当天取餐码从1开始累加
    private void setPickUpNum(PosOrder order) {
        if (order == null || order.getMdId() == null) {
            return;
        }
        try {
            // 获取当前日期，格式为 yyyy-MM-dd
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String today = dateFormat.format(new Date());

            // 查询当天该门店的最大取餐码
            LambdaQueryWrapper<PosOrder> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(PosOrder::getMdId, order.getMdId());
            queryWrapper.like(PosOrder::getCretim, today);
            queryWrapper.orderByDesc(PosOrder::getPickUpNum);
            queryWrapper.last("LIMIT 1");

            PosOrder lastOrder = posOrderService.getOne(queryWrapper);
            long nextNum = 1L;
            if (lastOrder != null && lastOrder.getPickUpNum() != null) {
                // 直接使用Long类型的取餐码
                nextNum = lastOrder.getPickUpNum() + 1;
            }
            // 生成新的取餐码：直接使用Long数字
            order.setPickUpNum(nextNum);
            logger.info("生成取餐码成功：门店ID={}, 取餐码={}", order.getMdId(), nextNum);

        } catch (Exception e) {
            logger.error("生成取餐码失败：门店ID={}, 错误信息={}", order.getMdId(), e.getMessage(), e);
            // 生成失败时使用时间戳作为备用方案
            Long fallbackPickUpNum = System.currentTimeMillis() % 10000;
            order.setPickUpNum(fallbackPickUpNum);
        }
    }

    //设置显示状态
    private void handleIsDisplay(PosOrder posOrder){
        if (StringUtils.isEmpty(posOrder.getDelryTime())) {
            posOrder.setIsDisplay(true);
            posOrder.setDisplayTime(new Date());
        }
        //预约单不在时间范围内不显示
        if (StringUtils.isNotEmpty(posOrder.getDelryTime())) {
            int minute = DateUtil.getAppoinmentOrderHandleTime();
            boolean isNear = DateUtil.isNearOrPastStartTime(posOrder.getDelryTime(), minute);
            if (isNear) {
                posOrder.setIsDisplay(true);
                posOrder.setDisplayTime(new Date());
            }
        }
    }


    private void createFootprint(Long uid,Long mdId){
        QueryWrapper<UserFootprint> Wrapper = new QueryWrapper<>();
        Wrapper.eq("user_id", uid);
        Wrapper.eq("md_id", mdId);
        UserFootprint foot = userFootprintService.getOne(Wrapper);
        DateUtil dateString = new DateUtil();
        if (foot == null) {
            UserFootprint usf = new UserFootprint();
            usf.setUserId(uid);
            usf.setMdId(mdId);
            usf.setCretim(dateString.GetDate());
            userFootprintService.saveOrUpdate(usf);
        } else {
            foot.setCretim(dateString.GetDate());
            userFootprintService.saveOrUpdate(foot);
        }
    }
    /**
     * 检查门店是否开放
     */
    private void checkStoreOpen( List<PosStore> storeList,List<OperatingHours> hourslist, OrderCreatItem item) {
        List<OperatingHours> storeHourslist = hourslist.stream().filter(x-> x.getMdId().equals(item.getMdId())).collect(Collectors.toList());
        PosStore store = storeList.stream()
                .filter(x -> Long.valueOf(x.getId()).equals(item.getMdId()))
                .findFirst()
                .orElseThrow(() -> new ServiceException(MessageUtils.message("no.mendian.not.exist")));
        if (storeHourslist.isEmpty()) {
            throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.not.set.business.hours"));
        }
        if (store.getState() == 1) {
            throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.is.closed"));
        }
        boolean isOperating = OperatingUtil.storeIsOperatingOfDay(storeHourslist);
        if (!isOperating) {
            throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.is.closed"));
        }
    }

    /**
     * 获取父订单详情
     */
    @GetMapping("/getParentOrderDetail")
    @Auth
    @Anonymous
    public AjaxResult getParentOrderDetail(@RequestHeader String token, @RequestParam String ddId){
        OrderParent result = orderParentService.getOne(new LambdaQueryWrapper<OrderParent>().eq(OrderParent::getDdId,ddId));
        List<PosOrder> posOrders = posOrderService.list(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getParentDdId,ddId).orderBy(true,true,PosOrder::getCretim));
        result.setItems(posOrders);
        // 转为JSONObject以追加非实体字段（promotions）
        JSONObject org = JSON.parseObject(JSON.toJSONStringWithDateFormat(result, "yyyy-MM-dd HH:mm:ss"));
        JSONArray items = org.getJSONArray("items");
        if (items != null) {
            for (int i = 0; i < items.size(); i++) {
                JSONObject item = items.getJSONObject(i);
                Long orderId = item.getLong("id");
                List<PosOrderPromotion> promotions = posOrderPromotionMapper.selectByOrderId(orderId);
                item.put("promotions", promotions);
            }
        }
        return super.success(org);
    }

    /**
     * 计算两个经纬度之间的距离
     */
    @GetMapping("/getDistance")
    @Anonymous
    public AjaxResult getDistance(@RequestParam Double lat1, @RequestParam Double lng1,
                                  @RequestParam Double lat2, @RequestParam Double lng2) {
        double distance = GetArea.getDistance(lat1, lng1, lat2, lng2);
        return success(distance);
    }

    /**
     * 用户端订单列表（Tab分页）
     */
    @GetMapping("/orderList")
    @Auth
    @Anonymous
    public AjaxResult orderList(@RequestHeader String token,
                                @RequestParam(defaultValue = "1") int page,
                                @RequestParam(defaultValue = "10") int size,
                                @RequestParam(required = false) String tab,
                                @RequestParam(required = false) String type) {
        JwtUtil jwtUtil = new JwtUtil();
        String userId = jwtUtil.getusid(token);

        LambdaQueryWrapper<PosOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PosOrder::getUserId, Long.valueOf(userId));

        // 类型筛选
        if (type != null && !type.isEmpty()) {
            wrapper.eq(PosOrder::getType, Long.valueOf(type));
        }

        switch (tab) {
            case "unpaid":
                // 在线支付订单不区分配送类型；自取/堂食未付款也必须先留在待付款。
                wrapper.eq(PosOrder::getPayStatus, 0L)
                       .in(PosOrder::getPayType, "2", "3")
                       .ne(PosOrder::getState, 4L)
                       .eq(PosOrder::getAfterSaleStatus, 0L);
                break;
            case "active":
                wrapper.in(PosOrder::getState, 0L, 1L, 2L)
                       .eq(PosOrder::getAfterSaleStatus, 0L)
                       // 现金自取/堂食可以线下收款；OMG、LINE Pay 等在线订单必须已付款才能履约。
                       .and(w -> w.eq(PosOrder::getPayStatus, 1L)
                               .or(cash -> cash.eq(PosOrder::getPayType, "1")
                                       .in(PosOrder::getType, 1L, 2L)));
                break;
            case "completed":
                wrapper.eq(PosOrder::getState, 3L).eq(PosOrder::getAfterSaleStatus, 0L);
                break;
            case "cancelled":
                wrapper.eq(PosOrder::getState, 4L).eq(PosOrder::getAfterSaleStatus, 0L);
                break;
            case "refund":
                wrapper.gt(PosOrder::getAfterSaleStatus, 0L);
                break;
            default:
               break;
        }
        wrapper.orderByDesc(PosOrder::getCretim);

        // 分页
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> pageParam =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size);
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> result = posOrderService.page(pageParam, wrapper);

        // 附加关联数据
        for (PosOrder order : result.getRecords()) {
            enrichOrderData(order);
        }

        java.util.Map<String, Object> data = new java.util.LinkedHashMap<>();
        data.put("records", result.getRecords());
        data.put("total", result.getTotal());
        data.put("page", page);
        data.put("size", size);
        return success(result);
    }

    private void enrichOrderData(PosOrder order) {
        try {
            if (order.getShId() != null) {
                InfoUser shanghu = infoUserService.getById(order.getShId());
                order.setPriceAll(shanghu != null ? String.valueOf(shanghu.getNickName()) : null);
            }
        } catch (Exception ignored) {}
    }

    /**
     * 确认取餐（自取类型订单，state=2 → state=3）
     */
    @Transactional(rollbackFor = Exception.class)
    @GetMapping("/confirmPickup")
    @Auth
    @Anonymous
    public AjaxResult confirmPickup(@RequestHeader String token, @RequestParam Long id) {
        JwtUtil jwtUtil = new JwtUtil();
        String userId = jwtUtil.getusid(token);

        PosOrder order = posOrderService.getOne(
            new LambdaQueryWrapper<PosOrder>()
                .eq(PosOrder::getId, id)
                .eq(PosOrder::getUserId, Long.valueOf(userId))
        );
        if (order == null) {
            return error("订单不存在");
        }
        if (order.getType() == null || order.getType() != 1) {
            return error("仅自取订单可确认取餐");
        }
        if (order.getState() == null || order.getState() != 2) {
            return error("当前订单状态不可确认取餐");
        }
        if (order.getPayStatus() == null || order.getPayStatus() != 1) {
            return error("订单未支付，不可确认取餐");
        }
        if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
            return error("订单存在售后处理中，不可确认取餐");
        }
        if (orderLifecycleService.isRealLineOrder(order)) {
            orderLifecycleService.completePaidLineOrder(order.getId());
        } else {
            order.setState(3L);
            posOrderService.updateById(order);
        }

        // 确认取餐成功，添加操作日志
        InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
        String uName = uUser != null ? uUser.getNickName() : "";
        orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "确认取餐");

        return success("确认取餐成功");
    }

    /**
     * 用户确认完成外送订单（骑手已送达后，state=2 → state=3）
     */
    @Transactional(rollbackFor = Exception.class)
    @GetMapping("/confirmDelivery")
    @Auth
    @Anonymous
    public AjaxResult confirmDelivery(@RequestHeader String token, @RequestParam Long id) {
        JwtUtil jwtUtil = new JwtUtil();
        String userId = jwtUtil.getusid(token);
        PosOrder order = posOrderService.getOne(
            new LambdaQueryWrapper<PosOrder>()
                .eq(PosOrder::getId, id)
                .eq(PosOrder::getUserId, Long.valueOf(userId))
        );
        if (order == null) {
            return error("订单不存在");
        }
        if (order.getState() == null || order.getState() != 2L) {
            return error("当前订单状态不可确认完成");
        }
        if (order.getDeliveryStatus() == null || order.getDeliveryStatus() != 3L) {
            return error("骑手尚未送达，不可确认完成");
        }
        if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
            return error("订单存在售后处理中，不可确认完成");
        }
        if (orderLifecycleService.isRealLineOrder(order)) {
            orderLifecycleService.completePaidLineOrder(order.getId());
        } else {
            PosOrder update = new PosOrder();
            update.setId(order.getId());
            update.setState(3L);
            posOrderService.updateById(update);
        }

        // 用户确认完成，添加操作日志
        InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
        String uName = uUser != null ? uUser.getNickName() : "";
        orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "确认完成订单");

        return success("确认完成成功");
    }

    /**
     * 取消订单（未接单前，state=0 → state=4）
     */
    @GetMapping("/cancelOrder")
    @Auth
    @Anonymous
    public AjaxResult cancelOrder(@RequestHeader String token, @RequestParam Long id) {
        JwtUtil jwtUtil = new JwtUtil();
        String userId = jwtUtil.getusid(token);

        PosOrder order = posOrderService.getOne(
            new LambdaQueryWrapper<PosOrder>()
                .eq(PosOrder::getId, id)
                .eq(PosOrder::getUserId, Long.valueOf(userId))
        );
        if (order == null) {
            return error("订单不存在");
        }
        if (order.getState() == null || order.getState() != 0) {
            return error("仅待处理状态的订单可取消");
        }
        PosOrder update = new PosOrder();
        update.setState(4L);
        boolean cancelled = posOrderService.update(update, new LambdaUpdateWrapper<PosOrder>()
                .eq(PosOrder::getId, order.getId())
                .eq(PosOrder::getState, 0L));
        if (!cancelled) {
            return error("订单状态已变化，请刷新后重试");
        }

        // 取消落库后按最新支付状态退款，避免旧订单对象覆盖退款状态或漏掉并发成功回调。
        PosOrder latest = posOrderService.getById(order.getId());
        if (latest != null && Long.valueOf(1L).equals(latest.getPayStatus())) {
            linePaymentService.getByDdId(String.valueOf(latest.getDdId())).stream()
                    .filter(payment -> "PAID".equals(payment.getStatus()))
                    .findFirst()
                    .ifPresent(payment -> linePayRefundService
                            .createRefundIntent(payment.getId(), "USER_CANCEL"));
        }

        // 取消订单成功，添加操作日志
        InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
        String uName = uUser != null ? uUser.getNickName() : "";
        orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "取消订单");
        deliveryOrderNotificationService.notifyOrderCancelled(order, Long.valueOf(userId));

        return success("取消订单成功");
    }

}
