package com.yc.ship.module.trade.controller.admin.order;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.common.util.collection.MapUtils;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.excel.core.util.ExcelUtils;
import com.yc.ship.framework.ip.core.utils.AreaUtils;
import com.yc.ship.framework.operatelog.core.annotations.OperateLog;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.module.product.api.dto.CategoryRespDTO;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.product.controller.admin.voyage.vo.VoyageRespVO;
import com.yc.ship.module.product.dal.dataobject.voyage.VoyageDO;
import com.yc.ship.module.product.service.voyage.VoyageService;
import com.yc.ship.module.resource.api.ship.ShipApi;
import com.yc.ship.module.resource.api.ship.dto.ShipRespDTO;
import com.yc.ship.module.trade.api.contract.dto.group.WindowCreateOrderGroup;
import com.yc.ship.module.trade.controller.admin.order.vo.order.*;
import com.yc.ship.module.trade.controller.admin.order.vo.otc.PrintRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.refund.*;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PublicOrderVoyageRespVO;
import com.yc.ship.module.trade.controller.app.otc.vo.AppTradeOrderPageReqVO;
import com.yc.ship.module.trade.controller.app.otc.vo.AppTradeOrderRespVO;
import com.yc.ship.module.trade.dal.dataobject.contract.ContractDO;
import com.yc.ship.module.trade.dal.dataobject.insurance.InsuranceDO;
import com.yc.ship.module.trade.dal.dataobject.order.*;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
import com.yc.ship.module.trade.framework.annotation.PlatTenantEnv;
import com.yc.ship.module.trade.framework.annotation.RequestDecryption;
import com.yc.ship.module.trade.framework.annotation.TradeRateLimit;
import com.yc.ship.module.trade.service.order.AdminTradeOrderService;
import com.yc.ship.module.trade.service.order.TradeOrderCountryService;
import com.yc.ship.module.trade.service.order.TradeOrderRoomModelService;
import com.yc.ship.module.trade.service.order.TradeOrderService;
import com.yc.ship.module.trade.service.order.TradeOrderTotalService;
import com.yc.ship.module.trade.service.otc.AppOtcTradeOrderService;
import com.yc.ship.module.trade.service.otc.OtcTradeOrderService;
import com.yc.ship.module.trade.service.pay.TradeOrderPayService;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.pojo.CommonResult.error;
import static com.yc.ship.framework.common.pojo.CommonResult.success;
import static com.yc.ship.framework.operatelog.core.enums.OperateTypeEnum.API;
import static com.yc.ship.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static com.yc.ship.framework.operatelog.core.enums.OperateTypeEnum.TRADE;


/***
 *&#064;Description  旅行社交易服务
 *@author qsl
 *&#064;date  2025/8/13 17:14
 */
@Validated
@RestController
@Slf4j
@RequestMapping("/travl/trade")
@Tag(name = "旅行社交易服务")
public class OtcTradeOrderController {

    @Resource
    private OtcTradeOrderService otcTradeOrderService;
    @Resource
    private AdminTradeOrderService adminTradeOrderService;
    @Resource
    private AgencyAuthUtils agencyAuthUtils;
    @Resource
    private AppOtcTradeOrderService appOtcTradeOrderService;

    @Resource
    private TradeOrderService tradeOrderService;

    @Resource
    private TradeOrderTotalService tradeOrderTotalService;

    @Resource
    private TradeOrderCountryService tradeOrderCountryService;

    @Resource
    private VoyageService voyageService;

    @Resource
    private ShipApi shipApi;

    @Resource
    private TradeOrderPayService tradeOrderPayService;

    @Resource
    private TradeOrderRoomModelService tradeOrderRoomModelService;

    @RequestDecryption
    @PostMapping("/travl/createOtcOrder")
    @Operation(summary = "win-旅行社游船订单创建")
    @OperateLog(type = TRADE)
    @TradeRateLimit(name = "createOtcOrder", limitNum = 2, limitTime = 5)
    public CommonResult<?> createOtcOrder(@Validated(value = WindowCreateOrderGroup.class) @RequestBody TradeOrderOtcCreateVO createVO) {
        return otcTradeOrderService.createOtcOrder(agencyAuthUtils.getAgencyLoginUser(), createVO);
    }

    @RequestDecryption
    @PostMapping("/travl/createMoveOrder")
    @Operation(summary = "win-旅行社游船订单搬单创建")
    @OperateLog(type = TRADE)
    public CommonResult<?> createMoveOrder(@Validated(value = WindowCreateOrderGroup.class) @RequestBody TradeOrderOtcCreateVO createVO) {
        createVO.setMoveOrder(true);
        return otcTradeOrderService.createOtcOrder(agencyAuthUtils.getAgencyLoginUser(), createVO);
    }

    @RequestDecryption
    @GetMapping("/travl/getOrderById")
    @Operation(summary = "win-旅行社游船订单获取")
    @OperateLog(type = TRADE)
    public CommonResult<?> getOrderById(@RequestParam Long orderId) {
        return otcTradeOrderService.getOrderById(orderId);
    }

    @GetMapping("/travl/getRole")
    @Operation(summary = "win-旅行社游船订单获取")
    @OperateLog(type = TRADE)
    public CommonResult<?> getRole() {
        LoginUser agencyLoginUser = agencyAuthUtils.getAgencyLoginUser();
        if (agencyLoginUser != null && agencyLoginUser.getDistributorId() != null) {
            return success(1);
        }
        return success(2);
    }


    @RequestDecryption
    @PostMapping("/travl/motifyOtcOrder")
    @Operation(summary = "win-旅行社游船订单修改")
    @OperateLog(type = TRADE)
    public CommonResult<?> motifyOtcOrder(@Validated(value = WindowCreateOrderGroup.class) @RequestBody TradeOrderOtcCreateVO createVO) {
        return otcTradeOrderService.motifyOtcOrder(agencyAuthUtils.getAgencyLoginUser(), createVO);
    }

    @RequestDecryption
    @PostMapping("/travl/createOtcFisrtOrder")
    @Operation(summary = "win-旅行社游船订单创建-预定单")
    @OperateLog(type = TRADE)
    @TradeRateLimit(name = "createOtcFisrtOrder", limitNum = 2, limitTime = 5)
    public CommonResult<?> createOtcFisrtOrder(@Validated(value = WindowCreateOrderGroup.class) @RequestBody TradeOrderOtcCreateVO createVO) {
        createVO.setIsYd(1);
        return otcTradeOrderService.createOtcOrder(agencyAuthUtils.getAgencyLoginUser(), createVO);
    }

    @RequestDecryption
    @PostMapping("/travl/payOrder")
    @Operation(summary = "win-旅行社下单支付订单")
    @OperateLog(type = TRADE)
    public CommonResult<PayOrderRespVO> windowPayOrder(@Valid @RequestBody PayOrderReqVO payOrderReqVO) {
        log.error("windowPayOrder:{}", JSON.toJSONString(payOrderReqVO));
        return otcTradeOrderService.windowPayOrder(agencyAuthUtils.getAgencyLoginUser(), payOrderReqVO);
    }

    @RequestDecryption
    @PostMapping("/travl/payDeposiOrder")
    @Operation(summary = "win-旅行社下单支付定金")
    @OperateLog(type = TRADE)
    public CommonResult<PayOrderRespVO> payDeposiOrder(@Valid @RequestBody PayOrderReqVO payOrderReqVO) {
        log.error("payDeposiOrder:{}", JSON.toJSONString(payOrderReqVO));
        return otcTradeOrderService.payDeposiOrder(agencyAuthUtils.getAgencyLoginUser(), payOrderReqVO);
    }

    @RequestDecryption
    @PostMapping("/travl/payBcOrder")
    @Operation(summary = "win-旅行社下单支付补差价")
    @OperateLog(type = TRADE)
    public CommonResult<PayOrderRespVO> payBcOrder(@Valid @RequestBody PayOrderReqVO payOrderReqVO) {
        log.error("payBcOrder:{}", JSON.toJSONString(payOrderReqVO));
        return otcTradeOrderService.payBcOrder(agencyAuthUtils.getAgencyLoginUser(), payOrderReqVO);
    }

    @GetMapping("/travl/queryOrderStatus")
    @Operation(summary = "win-查询订单状态")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> queryOrderStatus(@RequestParam Long orderId) {
        return otcTradeOrderService.queryOrderStatus(orderId);
    }

    @GetMapping("/travl/queryDeposiOrderStatus")
    @Operation(summary = "win-查询订单定金支付状态")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> queryDeposiOrderStatus(@RequestParam Long orderId) {
        return otcTradeOrderService.queryDeposiOrderStatus(orderId);
    }

    @GetMapping("/travl/queryBcOrderStatus")
    @Operation(summary = "win-查询订单补差价支付状态")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> queryBcOrderStatus(@RequestParam Long orderId) {
        return otcTradeOrderService.queryBcOrderStatus(orderId);
    }

    @GetMapping("/travl/queryOrderAudit")
    @Operation(summary = "win-查询订单审核信息")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> queryOrderAudit(@RequestParam Long orderId) {
        return otcTradeOrderService.queryOrderAudit(orderId);
    }

    @GetMapping("/travl/queryCancelAudit")
    @Operation(summary = "win-查询订单审核信息")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> queryCancelAudit(@RequestParam Long orderId) {
        return otcTradeOrderService.queryCancelAudit(orderId);
    }

    @GetMapping("/travl/orderAudit")
    @Operation(summary = "win-订单审核")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> orderAudit(@RequestParam Long orderId, @RequestParam Integer auditStatus, @RequestParam String auditAdvice) {
        return otcTradeOrderService.orderAudit(orderId, auditStatus,auditAdvice);
    }

    @GetMapping("/travl/cancelAudit")
    @Operation(summary = "win-订单取消审核")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> cancelAudit(@RequestParam Long orderId, @RequestParam Integer auditStatus, @RequestParam BigDecimal damaged) {
        return otcTradeOrderService.cancelAudit(orderId, auditStatus,damaged);
    }

    @GetMapping("/payOrderQuery")
    @Operation(summary = "支付反查，0待支付(轮询) 1已支付 2未支付 8888已支付等待结果(轮询) ")
    @OperateLog(type = TRADE)
    @PlatTenantEnv
    public CommonResult<?> payOrderQuery(@RequestParam Long payOrderId) {
        return tradeOrderPayService.payOrderQuery(payOrderId);
    }

    @GetMapping("/orderRead")
    @Operation(summary = "订单审阅")
    @OperateLog(type = TRADE)
    @PlatTenantEnv
    public CommonResult<?> orderRead(@RequestParam Long orderId) {
        return otcTradeOrderService.orderRead(orderId);
    }

    @PostMapping("/window/orderList")
    @OperateLog(type = API)
    @Operation(summary = "win-平台订单列表[分页]", description = "根据条件查询订单列表")
    @PlatTenantEnv
    public CommonResult<PageResult<TradeOrderRespNewVO>> getTradeOrderPage(@Valid @RequestBody TradeOrderPageReqVO pageReqVO) {
        // 1. 查询订单分页数据并转换为新的VO对象
        PageResult<TradeOrderRespVO> platOrderPage = tradeOrderService.getPlatOrderPage(pageReqVO);
        PageResult<TradeOrderRespNewVO> page = BeanUtils.toBean(platOrderPage, TradeOrderRespNewVO.class);
        List<TradeOrderRespNewVO> list = page.getList();

        //处理游轮名称
        // 2. 批量查询游轮信息：提取所有订单的游轮ID，通过API批量查询游轮详情，构建游轮ID到游轮信息的映射
        List<Long> shipIds = CollectionUtils.convertList(list, TradeOrderRespNewVO::getShipId);
        Map<Long, ShipRespDTO> shipMap = shipApi.queryShip(shipIds);

        //国籍数据
        // 3. 批量查询订单关联数据：提取所有订单ID
        List<Long> orderIds = CollectionUtils.convertList(list, TradeOrderRespNewVO::getId);
        // 3.1 查询订单国籍信息列表
        List<TradeOrderCountryDO> countryList = tradeOrderCountryService.getListByOrderIds(orderIds);
        // 3.2 查询订单房型信息列表
        List<TradeOrderRoomModelDO> roomModelList = tradeOrderRoomModelService.getListByOrderIds(orderIds);
        // 3.3 将DO对象转换为VO对象
        List<TradeOrderCountryBaseVO> countRespVOList = BeanUtils.toBean(countryList, TradeOrderCountryBaseVO.class);
        List<TradeOrderRoomModelVO> roomModeVOList = BeanUtils.toBean(roomModelList, TradeOrderRoomModelVO.class);
        // 3.4 填充国籍名称：根据国籍ID转换为国籍名称字符串
        countRespVOList.forEach(item -> {
            if (item.getCountryId() != null) {
                item.setCountryName(AreaUtils.format2Str(item.getCountryId()));
            }
        });
        // 3.5 构建订单ID到国籍列表的映射（一个订单可能有多个国籍）
        Map<Long, List<TradeOrderCountryBaseVO>> countryMap = CollectionUtils.convertMultiMap(countRespVOList, TradeOrderCountryBaseVO::getOrderId);
        // 3.6 构建订单ID到房型列表的映射（一个订单可能有多个房型）
        Map<Long, List<TradeOrderRoomModelVO>> roomModelMap = CollectionUtils.convertMultiMap(roomModeVOList, TradeOrderRoomModelVO::getOrderId);

        //订单汇总数据
        // 4. 批量查询订单汇总统计数据：包含成人、儿童、婴儿数量等统计信息
        List<TradeOrderTotalDO> tradeOrderTotalDOList = tradeOrderTotalService.getByOrderIds(orderIds);
        List<TradeOrderTotalVO> tradeOrderTotalVOList = BeanUtils.toBean(tradeOrderTotalDOList, TradeOrderTotalVO.class);
        // 4.1 构建旧订单ID到汇总数据的映射
        Map<Long, TradeOrderTotalVO> orderTotalVoMap = CollectionUtils.convertMap(tradeOrderTotalVOList, TradeOrderTotalVO::getOldOrderId);

        //航次信息
        // 5. 批量查询航次信息：提取所有订单的航次ID，查询航次详情并构建映射
        List<Long> voyageIds = CollectionUtils.convertList(list, TradeOrderRespNewVO::getVoyageId);
        List<VoyageDO> voyageList = voyageService.getList(voyageIds);
        List<VoyageRespVO> voyageRespVOList = BeanUtils.toBean(voyageList, VoyageRespVO.class);
        Map<Long, VoyageRespVO> voyageRespVoMap = CollectionUtils.convertMap(voyageRespVOList, VoyageRespVO::getId);

        // 拼房数量
        // 6. 批量查询拼房统计数据：查询每个订单的拼房人数和性别统计
        List<OrderShareVO> orderShareVOS = tradeOrderRoomModelService.getOrderShareStatsByOrderIds(orderIds);
        Map<Long, List<OrderShareVO>> orderShareMap = CollectionUtils.convertMultiMap(orderShareVOS, OrderShareVO::getOrderId);

        // 7. 组装订单详细信息：遍历订单列表，将各类关联数据填充到订单对象中
        if (!list.isEmpty()) {
            list.forEach(item -> {
                // 7.1 填充游轮名称
                MapUtils.findAndThen(shipMap, item.getShipId(), ship -> item.setShipName(ship.getName()));
                // 7.2 填充国籍列表
                MapUtils.findAndThen(countryMap, item.getId(), item::setCountryList);
                // 7.3 填充房型列表
                MapUtils.findAndThen(roomModelMap, item.getId(), item::setRoomModelList);
                // 7.4 填充订单汇总数据和人员统计描述（格式：X大Y小）
                MapUtils.findAndThen(orderTotalVoMap, item.getId(), totalVO -> {
                    item.setSummary(totalVO);
                    item.setPersonStat(totalVO.getAdultTotalNum() + "大" + (totalVO.getChildTotalNum() + totalVO.getBabyTotalNum()) + "小");
                });
                // 7.5 填充航次信息
                MapUtils.findAndThen(voyageRespVoMap, item.getVoyageId(), item::setVoyage);
                // 7.6 计算订单总人数：根据游客列表大小设置
                if (item.getVisitors() != null && !item.getVisitors().isEmpty()) {
                    item.setTotalPerson(item.getVisitors().size());
                } else {
                    item.setTotalPerson(0);
                }

                // 7.7 生成房型详情描述：按"房型名称(楼层F)"分组统计数量，格式：数量*房型名称(楼层F)
                if(item.getRoomModelList()!=null && !item.getRoomModelList().isEmpty()) {
                    // 按房型名称和楼层分组
                    Map<String, List<TradeOrderRoomModelVO>> roomDesc = item.getRoomModelList().stream().collect(Collectors.groupingBy(p -> p.getRoomModelName() + "(" + p.getFloor() + "F)"));
                    AtomicReference<String> roomDescStr = new AtomicReference<>("");
                    // 拼接房型描述字符串
                    roomDesc.forEach((key, value) -> roomDescStr.set(roomDescStr.get() + " " + value.size() + "*" + key));
                    item.setRoomModelDetails(roomDescStr.get().trim());
                }
                // 7.8 生成拼房描述：拼接拼房人数和性别信息
                MapUtils.findAndThen(orderShareMap, item.getId(), orderShareList -> {
                    String shareDesc = orderShareList.stream().map(orderShare -> orderShare.getNum() + "" + orderShare.getGender()).collect(Collectors.joining());
                    item.setShareDesc(shareDesc);
                });
            });
        }
        return success(page);
    }

    @PostMapping("/window/getOrderCount")
    @OperateLog(type = API)
    @Operation(summary = "win-平台订单列表[分页]", description = "根据条件查询订单列表")
    @PlatTenantEnv
    public CommonResult<?> getOrderCount(@Valid @RequestBody TradeOrderPageReqVO pageReqVO) {
        String totalDesc = tradeOrderService.getOrderCount(pageReqVO);
        return success(totalDesc);
    }


    @PostMapping("/window/paidOrderList")
    @Operation(summary = "win-已支付订单列表[分页]", description = "根据条件查询订单列表")
    @OperateLog(type = API)
    public CommonResult<PageResult<TradeOrderRespVO>> getPaidOrderList(@Valid @RequestBody TradeOrderPageReqVO pageReqVO) {
        return success(otcTradeOrderService.getPaidOrderPage(pageReqVO));
    }


    @GetMapping("/window/orderInfo")
    @OperateLog(type = API)
    @Operation(summary = "win-订单详情", description = "根据订单id查询订单详情")
    @PlatTenantEnv
    public CommonResult<TradeOrderRespVO> getOrderInfo(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.getOrderInfo(id));
    }

    @GetMapping("/window/orderInfoNew")
    @OperateLog(type = API)
    @Operation(summary = "win-订单详情", description = "根据订单id查询订单详情")
    @PlatTenantEnv
    public CommonResult<TradeOrderRespNewVO> getOrderInfoNew(@Valid @RequestParam("id") Long id) {
        TradeOrderRespNewVO orderInfoNew = otcTradeOrderService.getOrderInfoNew(id);
        if(orderInfoNew == null) {
            return error(500,"订单已修改请刷新页面后再试");
        }
        return success(orderInfoNew);
    }

    @GetMapping("/window/orderConfirmation")
    @OperateLog(type = API)
    @Operation(summary = "win-订单确认单", description = "根据订单id查询订单确认单")
    @PlatTenantEnv
    public CommonResult<TradeOrderConfirmationVO> getOrderConfirmation(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.getOrderConfirmation(id));
    }

    @PostMapping("/travl/refund")
    @Operation(summary = "win-旅行社退款")
    @OperateLog(type = TRADE)
    public CommonResult<TradeRefundCreateRespVO> windowRefund(@Valid @RequestBody TradeRefundCreateReqVO refundCreateReqVO) {
        return adminTradeOrderService.refund(agencyAuthUtils.getAgencyLoginUser(), refundCreateReqVO);
    }

    @PostMapping("/refund/calculateRefundAmount")
    @Operation(summary = "退款计算")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<TradeRefundCalculateRespVO> calculateRefundAmount(@Valid @RequestBody TradeRefundCreateReqVO refundCreateReqVO) {
        return adminTradeOrderService.calculateRefundAmount(agencyAuthUtils.getAgencyLoginUser(), refundCreateReqVO);
    }

    @PostMapping("/cancelRefund")
    @Operation(summary = "取消退款，只能取消退款中，且供应商退款失败的")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> cancelRefund(@RequestParam("refundId") Long refundId) {
        return adminTradeOrderService.cancelRefund(refundId);
    }

    @GetMapping("/cancelOrder")
    @Operation(summary = "取消订单，只能取消预定、确定没有付款的订单")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> cancelOrder(@Valid @RequestParam("id") Long id) {
        return adminTradeOrderService.cancelOrder(id);
    }

    @GetMapping("/cancelAuto")
    @Operation(summary = "审核撤回订单")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<?> cancelAuto(@Valid @RequestParam("id") Long id) {
        return otcTradeOrderService.cancelAuto(id);
    }

    @PostMapping("/refund/refundOrderList")
    @Operation(summary = "退单列表", description = "根据条件查询退单列表")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<PageResult<RefundRespVO>> getRefundRecordPage(@Valid @RequestBody RefundPageReqVO pageReqVO) {
        return success(otcTradeOrderService.getRefundRecordPage(pageReqVO));
    }

    @RequestDecryption
    @PostMapping("/refundAudit")
    @Operation(summary = "退款审核")
    @OperateLog(type = API)
    public CommonResult<?> refundAudit(@Valid @RequestBody TradeRefundAuditReqVO refundAuditReqVO) {
        return adminTradeOrderService.refundAudit(agencyAuthUtils.getAgencyLoginUser(), refundAuditReqVO);
    }

    @GetMapping("/refund/refundDetailList")
    @Operation(summary = "退单批次详情", description = "根据退单id查询退单批次详情")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<List<RefundDetailRespVO>> getRefundBatchDetails(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.getRefundBatchDetails(id));
    }


    @GetMapping("/refund/refundRecordList")
    @Operation(summary = "退款记录[分页]", description = "根据条件查询退款记录")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<PageResult<RefundRecordRespVO>> getPageRefundRecord(@Valid RefundRecordPageReqVO pageReqVO) {
        return success(otcTradeOrderService.getPageRefundRecord(pageReqVO));
    }

    /**
     * 退款记录如果长时间处于退款中，可以向支付平台反查
     */
    @GetMapping("/refundQuery")
    @Operation(summary = "退款中-退款查询(向支付平台)")
    @OperateLog(type = API)
    public CommonResult<?> refundQuery(@Valid Long refundRecordId) {
        return adminTradeOrderService.refundQuery(refundRecordId);
    }

    @PostMapping("/refundByRecord")
    @Operation(summary = "根据退款记录再发发起退款")
    @OperateLog(type = API)
    public CommonResult<?> refundByRecord(Long refundRecordId) {
        return adminTradeOrderService.refundByRecord(agencyAuthUtils.getAgencyLoginUser(), refundRecordId);
    }

    /**
     * 退款单如果处于长时间处于等待退款（因mq消息失败了）、或者供应商退款失败。可手动发起退款给游客
     */
    @PostMapping("/manualRefund")
    @Operation(summary = "人工退款")
    @OperateLog(type = API)
    public CommonResult<?> manualRefund(Long refundId) {

        return adminTradeOrderService.manualRefund(agencyAuthUtils.getAgencyLoginUser(), refundId);
    }

    @GetMapping("/window/orderDetailInfo")
    @Operation(summary = "win-门票详情", description = "根据订单id查询门票详情")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<List<TradeDetailBaseDO>> getDetailInfo(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.getDetailInfo(id));
    }

    @PostMapping("/payOuterOrder")
    @Operation(summary = "外部订单支付")
    @OperateLog(type = TRADE)
    public CommonResult<PayOrderRespVO> payOuterOrder(@Valid @RequestBody PayOrderReqVO payOrderReqVO) {
        return tradeOrderPayService.payOuterOrder(payOrderReqVO);
    }

    @GetMapping("/categoryOrSpu")
    @Operation(summary = "sell-产品分组和产品数据")
    @OperateLog(type = API)
    public CommonResult<List<CategoryRespDTO>> categoryOrSpu(String type, String travelDate) {
        return success(otcTradeOrderService.getCategoryOrSpuList(type, travelDate));
    }

    @GetMapping("/categoryAndSpuAll")
    @Operation(summary = "sell-产品分组和产品数据")
    @OperateLog(type = API)
    public CommonResult<List<CategoryRespDTO>> categoryAndSpuAll(String type, String travelDate) {
        return success(otcTradeOrderService.getCategoryAndSpuAll(type, travelDate));
    }


    @GetMapping("/productSupInfo")
    @Operation(summary = "sell-产品详情")
    @OperateLog(type = API)
    public CommonResult<ProductSpuRespDTO> productSpu(@Valid @RequestParam("id") Long id, @RequestParam("date") String date, @RequestParam(value = "planId", required = false) String planId) {
        //查看日期是否存在
        if (date.isEmpty()) {
            date = DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        }
        return success(otcTradeOrderService.getProductSpuDTO(id, date, planId));
    }

    @GetMapping("/plat/productSupInfo")
    @Operation(summary = "sell-产品详情")
    @OperateLog(type = API)
    public CommonResult<ProductSpuRespDTO> platProductSpu(@Valid @RequestParam("id") Long id, @RequestParam("date") String date, @RequestParam(value = "planId", required = false) String planId
            , @RequestParam("distributorId") Long distributorId) {
        //查看日期是否存在
        if (date.isEmpty()) {
            date = DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        }
        return success(otcTradeOrderService.getProductSpuDTO(id, date, planId, distributorId));
    }


    @GetMapping("/getOrderContract")
    @Operation(summary = "sell-查询保合同")
    @OperateLog(type = API)
    public CommonResult<List<ContractDO>> getContract(@Valid @RequestParam("orderId") Long orderId) {
        return success(otcTradeOrderService.getContract(orderId));
    }

    @GetMapping("/getOrderInsurance")
    @Operation(summary = "sell-查询保险")
    @OperateLog(type = API)
    public CommonResult<List<InsuranceDO>> getInsurance(@Valid @RequestParam("orderId") Long orderId) {
        return success(otcTradeOrderService.getInsurance(orderId));
    }

    @GetMapping("/orderPrintData")
    @Operation(summary = "sell-通过订单查询打印模板")
    @OperateLog(type = API)
    public CommonResult<List<PrintRespVO>> getOrderPrintData(@Valid @RequestParam("orderId") Long orderId) {
        return success(otcTradeOrderService.getOrderPrintData(orderId));
    }

    @PostMapping("/window/queryCanInvoicedOrder")
    @OperateLog(type = API)
    @Operation(summary = "win-订单列表[分页]", description = "根据条件查询可开票订单列表")
    @PlatTenantEnv
    public CommonResult<PageResult<TradeOrderRespVO>> queryCanInvoicedOrderPage(@Valid @RequestBody TradeOrderPageReqVO pageReqVO) {
        return success(otcTradeOrderService.queryCanInvoicedOrderPage(pageReqVO));
    }

    @PostMapping("/window/queryPlatCanInvoicedOrder")
    @OperateLog(type = API)
    @Operation(summary = "win-订单列表[分页]", description = "根据条件查询可开票订单列表")
    @PlatTenantEnv
    public CommonResult<PageResult<TradeOrderRespVO>> queryPlatCanInvoicedOrder(@Valid @RequestBody TradeOrderPageReqVO pageReqVO) {
        return success(otcTradeOrderService.queryPlatCanInvoicedOrder(pageReqVO));
    }

    @GetMapping("/orderRuleOrUserDetails")
    @Operation(summary = "win-订单规格和游客信息详情", description = "根据订单id查询订单规格和游客信息详情")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<TradeOrderRespVO> getOrderRuleOrUserDetails(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.orderRuleOrUserDetails(id));
    }

    @PostMapping("/getOrderListByMemberId")
    @Operation(summary = "根据会员id查询订单列表[分页]", description = "根据条件查询订单列表")
    @OperateLog(type = API)
    public CommonResult<PageResult<AppTradeOrderRespVO>> getOrderListPageByMemberId(@Valid @RequestBody AppTradeOrderPageReqVO pageReqVO) {
        return appOtcTradeOrderService.miniTradeOrderPage(pageReqVO);
    }

    @GetMapping("/getPayAmount")
    @Operation(summary = "待支付时获取支付金额")
    @OperateLog(type = API)
    @PlatTenantEnv
    public CommonResult<OrderPayAmountRespVO> getPayAmount(@Valid @RequestParam("id") Long id) {
        return success(otcTradeOrderService.getPayAmount(id));
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出订单列表 Excel")
    @OperateLog(type = EXPORT, enable = false)
    @PlatTenantEnv
    public void exportOrderExcel(@Valid TradeOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(10000);
        List<TradeOrderRespExcelVO> list = tradeOrderService.getExportVisitorList(pageReqVO);
        ExcelUtils.write(response, "游客列表.xls", "数据", TradeOrderRespExcelVO.class, list);
    }

    /**
     * 导出确认单
     */
    @GetMapping("/export-order-confirmation")
    @Operation(summary = "导出确认单 Excel")
    @OperateLog(type = EXPORT, enable = false)
    public void exportOrderBillExcel(Long id, Double totalMoney, HttpServletResponse response) throws IOException {
        File tempFile = otcTradeOrderService.createConfirmaction(id, totalMoney);
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment; filename=" + tempFile.getName());
        InputStream is = Files.newInputStream(tempFile.toPath());
        IOUtils.copy(is, response.getOutputStream());
    }


    @GetMapping("/window/update-status")
    @OperateLog(type = API)
    @Operation(summary = "win-订单列表[分页]", description = "修改订单状态")
    @PreAuthorize("@ss.hasPermission('trade:order:status-update')")
    public CommonResult<?> updateStatus(@RequestParam Long orderId, @RequestParam Integer status) {
        return success(otcTradeOrderService.updateStatus(orderId, status));
    }



    @PostMapping("/getOrderTotal")
    @Operation(summary = "获取订单统计")
    @OperateLog(type = API)
    public CommonResult<OrderTotalRespVO> getOrderTotal(@Valid @RequestBody OrderTotalQueryVO queryVO) {
        return success(otcTradeOrderService.getOrderTotal(queryVO));
    }

    /**
     * 导出游客名单-计调
     */
    @PostMapping("/export-touristExcel")
    @Operation(summary = "导出游客名单 Excel")
    @OperateLog(type = EXPORT, enable = false)
    @PlatTenantEnv
    @PreAuthorize("@ss.hasPermission('trade:order:export-operator')")
    public void exportTouristList(@Valid @RequestBody TradeOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
        File tempFile = otcTradeOrderService.exportTouristList(pageReqVO);
        try {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=" + tempFile.getName());
            try (InputStream is = Files.newInputStream(tempFile.toPath())) {
                IOUtils.copy(is, response.getOutputStream());
            }
        } finally {
            // 确保临时文件被删除
            if (tempFile != null && tempFile.exists()) {
                tempFile.delete();
            }
        }
    }

    /**
     * 导出游客名单-代理商
     */
    @PostMapping("/export-touristExcel-agent")
    @Operation(summary = "导出游客名单 Excel")
    @OperateLog(type = EXPORT, enable = false)
    @PlatTenantEnv
    @PreAuthorize("@ss.hasPermission('trade:order:export-agent')")
    public void exportTouristListToAgent(@Valid @RequestBody TradeOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
        File tempFile = otcTradeOrderService.exportTouristListToAgent(pageReqVO);
        try {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=" + tempFile.getName());
        // 使用 try-with-resources 自动关闭流
        try (InputStream is = Files.newInputStream(tempFile.toPath())) {
            IOUtils.copy(is, response.getOutputStream());
        }
    } finally {
        // 确保临时文件被删除
        if (tempFile != null && tempFile.exists()) {
            tempFile.delete();
        }
    }
    }

    /**
     * 获取订单航次信息（免登录）
     * 用于H5页面签到等无需登录的场景
     *
     * @param orderId 订单ID
     * @return 订单航次信息
     */
    @GetMapping("/orderVoyage")
    @Operation(summary = "获取订单航次信息（免登录）", description = "根据订单ID查询航次信息，用于签到页面判断开航时间")
    @PermitAll
    public CommonResult<PublicOrderVoyageRespVO> getOrderVoyage(@RequestParam("orderId") Long orderId) {
        return otcTradeOrderService.getOrderVoyage(orderId);
    }
}
