package com.spot.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spot.constant.SysConstant;
import com.spot.dto.mq.AssetChangeMessage;
import com.spot.dto.mq.OrderMessage;
import com.spot.entity.ConfigSymbol;
import com.spot.entity.ExOrder;
import com.spot.enums.MessageCode;
import com.spot.req.AdminOrderQueryReq;
import com.spot.req.CancelOrderReq;
import com.spot.req.CreateOrderReq;
import com.spot.req.OrderListReq;
import com.spot.resp.ApiResultVo;
import com.spot.resp.CreateOrderResp;
import com.spot.service.AssetChangeMqService;
import com.spot.service.ConfigSymbolService;
import com.spot.service.OrderService;
import com.spot.exception.BizException;
import com.spot.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 订单控制器，提供下单、撤单、订单查询接口
 *
 * @author jean
 * @date 2026-05-06
 */
@Slf4j
@RestController
@RequestMapping("/v1")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private AssetChangeMqService assetChangeMqService;

    @Autowired
    private ConfigSymbolService configSymbolService;

    /**
     * 创建订单
     * 事务内：生成订单、冻结余额、生成流水；事务外：发送MQ消息
     *
     * @param request HTTP请求
     * @param req     创建订单请求
     * @return 订单基本信息
     */
    @PostMapping("/order/create")
    @ResponseBody
    public ApiResultVo<CreateOrderResp> createOrder(HttpServletRequest request,
                                                    @RequestBody CreateOrderReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            // 1. 手动校验参数
            validateCreateOrderReq(req);

            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            log.info("用户 {} 创建订单 - Symbol: {}, Side: {}, Type: {}, Price: {}, Volume: {}",
                    uid, req.getSymbol(), req.getSide(), req.getType(), req.getPrice(), req.getVolume());

            // 2. 事务内创建订单
            OrderMessage orderMessage = orderService.createOrder(req, uid);

            // 3. 构建响应
            CreateOrderResp resp = buildCreateOrderResp(orderMessage);

            // 4. 事务外发送MQ（失败不影响下单）
            if (orderMessage.getId() != null) {
                sendOrderMq(orderMessage, uid);
            }

            return ApiResultVo.success(lang, resp);
        } catch (BizException e) {
            log.warn("创建订单失败 - {}", e.getMessage());
            return ApiResultVo.fail(lang, e.getMessageCode(), e.getMessage(lang));
        } catch (Exception e) {
            log.error("创建订单失败", e);
            return ApiResultVo.fail(lang, MessageCode.ORDER_SYSTEM_ERROR);
        }
    }

    /**
     * 撤单
     * 事务内：更新订单状态为撤单中；事务外：发送MQ消息
     *
     * @param request HTTP请求
     * @param req     撤单请求
     * @return 订单基本信息
     */
    @PostMapping("/order/cancel")
    @ResponseBody
    public ApiResultVo<CreateOrderResp> cancelOrder(HttpServletRequest request,
                                                    @RequestBody CancelOrderReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            // 1. 手动校验参数
            if (req.getOrderId() == null) {
                return ApiResultVo.fail(lang, MessageCode.PARAMETER_ERROR, "订单ID不能为空");
            }

            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            log.info("用户 {} 撤单 - OrderId: {}", uid, req.getOrderId());

            // 2. 事务内撤单
            OrderMessage orderMessage = orderService.cancelOrder(req.getOrderId(), uid);

            // 3. 构建响应
            CreateOrderResp resp = buildCreateOrderResp(orderMessage);

            // 4. 事务外发送MQ（失败不影响撤单）
            if (orderMessage.getId() != null) {
                try {
                    orderService.sendMqMessage(orderMessage);
                    assetChangeMqService.sendChangeMessage(
                            AssetChangeMessage.TYPE_ORDER, uid,
                            Collections.singletonList(orderMessage.getSymbol()));
                } catch (Exception e) {
                    log.error("发送撤单MQ失败 - OrderId: {}", orderMessage.getId(), e);
                }
            }

            return ApiResultVo.success(lang, resp);
        } catch (BizException e) {
            log.warn("撤单失败 - {}", e.getMessage());
            return ApiResultVo.fail(lang, e.getMessageCode(), e.getMessage(lang));
        } catch (Exception e) {
            log.error("撤单失败", e);
            return ApiResultVo.fail(lang, MessageCode.CANCEL_ORDER_FAIL);
        }
    }

    /**
     * 一键撤销当前用户所有可撤订单（INIT/NEW/PART_FILLED）
     * 事务内：批量更新订单状态为撤单中；事务外：逐单发送MQ消息
     *
     * @param request HTTP请求
     * @return 已提交撤单数量
     */
    @PostMapping("/order/cancel/all")
    @ResponseBody
    public ApiResultVo<Integer> cancelAllOrders(HttpServletRequest request) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            log.info("用户 {} 发起一键撤单", uid);

            // 1. 事务内批量标记撤单中
            List<OrderMessage> orderMessages = orderService.cancelAllCurrentOrders(uid);
            if (orderMessages == null || orderMessages.isEmpty()) {
                return ApiResultVo.success(lang, 0);
            }

            // 2. 事务外发送撤单消息（逐单发送，失败不影响接口成功返回）
            List<String> symbols = new ArrayList<>();
            for (OrderMessage orderMessage : orderMessages) {
                try {
                    orderService.sendMqMessage(orderMessage);
                    if (orderMessage.getSymbol() != null) {
                        symbols.add(orderMessage.getSymbol());
                    }
                } catch (Exception e) {
                    log.error("发送一键撤单MQ失败 - OrderId: {}", orderMessage.getId(), e);
                }
            }

            // 3. 发送订单变动通知（按交易对去重）
            if (!symbols.isEmpty()) {
                assetChangeMqService.sendChangeMessage(
                        AssetChangeMessage.TYPE_ORDER,
                        uid,
                        symbols.stream().distinct().collect(Collectors.toList()));
            }

            return ApiResultVo.success(lang, orderMessages.size());
        } catch (BizException e) {
            log.warn("一键撤单失败 - {}", e.getMessage());
            return ApiResultVo.fail(lang, e.getMessageCode(), e.getMessage(lang));
        } catch (Exception e) {
            log.error("一键撤单失败", e);
            return ApiResultVo.fail(lang, MessageCode.CANCEL_ORDER_FAIL);
        }
    }

    /**
     * 查询当前委托（未完成的挂单）
     *
     * @param request HTTP请求
     * @param req     查询请求
     * @return 分页结果
     */
    @PostMapping("/order/current")
    @ResponseBody
    public ApiResultVo<Page<ExOrder>> currentOrders(HttpServletRequest request,
                                                    @RequestBody OrderListReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            return ApiResultVo.success(lang, orderService.currentOrders(req, uid));
        } catch (Exception e) {
            log.error("查询当前委托失败", e);
            return ApiResultVo.fail(lang, MessageCode.UNKNOWN_ERROR);
        }
    }

    /**
     * 查询历史委托（已完成或已撤单）
     *
     * @param request HTTP请求
     * @param req     查询请求
     * @return 分页结果
     */
    @PostMapping("/order/history")
    @ResponseBody
    public ApiResultVo<Page<ExOrder>> historyOrders(HttpServletRequest request,
                                                    @RequestBody OrderListReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            return ApiResultVo.success(lang, orderService.historyOrders(req, uid));
        } catch (Exception e) {
            log.error("查询历史委托失败", e);
            return ApiResultVo.fail(lang, MessageCode.UNKNOWN_ERROR);
        }
    }

    /**
     * 查询成交记录（有成交量的订单）
     *
     * @param request HTTP请求
     * @param req     查询请求
     * @return 分页结果
     */
    @PostMapping("/order/trades")
    @ResponseBody
    public ApiResultVo<Page<ExOrder>> tradeRecords(HttpServletRequest request,
                                                   @RequestBody OrderListReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            Long uid = (Long) request.getAttribute(SysConstant.REQUEST_ATTR_MEMBER_ID);
            return ApiResultVo.success(lang, orderService.tradeRecords(req, uid));
        } catch (Exception e) {
            log.error("查询成交记录失败", e);
            return ApiResultVo.fail(lang, MessageCode.UNKNOWN_ERROR);
        }
    }

    /**
     * 管理后台撤销指定交易对所有活跃订单
     *
     * @param symbol 交易对符号
     * @return 已提交撤单数量
     */
    @PostMapping("/admin/order/cancel/all")
    @ResponseBody
    public ApiResultVo<Integer> cancelAllOrdersBySymbol(HttpServletRequest request,@RequestParam String symbol) {
        String lang = RequestUtils.getLangFromHeader(request);
        try {
            if (!StringUtils.hasText(symbol)) {
                return ApiResultVo.fail(lang, MessageCode.PARAMETER_ERROR, "交易对不能为空");
            }
            log.info("管理后台撤销交易对 {} 的所有活跃订单", symbol);
            int count = orderService.cancelAllOrdersBySymbol(symbol);
            return ApiResultVo.success(lang, count);
        } catch (BizException e) {
            log.warn("管理后台撤销订单失败 - {}", e.getMessage());
            return ApiResultVo.fail(lang, e.getMessageCode(), e.getMessage(lang));
        } catch (Exception e) {
            log.error("管理后台撤销订单失败 - symbol: {}", symbol, e);
            return ApiResultVo.fail(lang, MessageCode.CANCEL_ORDER_FAIL);
        }
    }

    // ==================== 私有方法 ====================

    /**
     * 手动校验创建订单请求参数，校验不通过时抛出 BizException
     */
    private void validateCreateOrderReq(CreateOrderReq req) {
        if (req == null) {
            throw new BizException(MessageCode.PARAMETER_ERROR);
        }
        if (!StringUtils.hasText(req.getSymbol())) {
            throw new BizException(MessageCode.SYMBOL_EMPTY);
        }
        if (!StringUtils.hasText(req.getSide())) {
            throw new BizException(MessageCode.ORDER_SIDE_EMPTY);
        }
        if (req.getType() == null) {
            throw new BizException(MessageCode.ORDER_TYPE_EMPTY);
        }
        if (req.getVolume() == null) {
            throw new BizException(MessageCode.ORDER_VOLUME_INVALID);
        }
    }

    /**
     * 从 OrderMessage 构建响应对象
     */
    private CreateOrderResp buildCreateOrderResp(OrderMessage orderMessage) {
        CreateOrderResp resp = new CreateOrderResp();
        resp.setOrderId(orderMessage.getId());
        resp.setSymbol(orderMessage.getSymbol());
        resp.setSide(orderMessage.getSide());
        resp.setType(orderMessage.getType());
        resp.setStatus(orderMessage.getStatus());
        return resp;
    }

    /**
     * 事务外发送下单 MQ 和资产变动通知
     */
    private void sendOrderMq(OrderMessage orderMessage, Long uid) {
        try {
            orderService.sendMqMessage(orderMessage);
            ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(orderMessage.getSymbol());
            if (configSymbol != null) {
                // 资产变动：买单冻结计价币，卖单冻结基础币
                String frozenSymbol = "BUY".equals(orderMessage.getSide())
                        ? configSymbol.getQuote()
                        : configSymbol.getBase();
                assetChangeMqService.sendChangeMessage(
                        AssetChangeMessage.TYPE_ASSET, uid,
                        Collections.singletonList(frozenSymbol));
            }
            assetChangeMqService.sendChangeMessage(
                    AssetChangeMessage.TYPE_ORDER, uid,
                    Collections.singletonList(orderMessage.getSymbol()));
        } catch (Exception e) {
            log.error("发送下单MQ失败 - OrderId: {}", orderMessage.getId(), e);
        }
    }

    @PostMapping("/admin/order/page")
    @ResponseBody
    public ApiResultVo<Page<ExOrder>> adminOrderPage(HttpServletRequest request, @RequestBody AdminOrderQueryReq req) {
        String lang = RequestUtils.getLangFromHeader(request);
        return ApiResultVo.success(lang, orderService.adminPageQuery(req));
    }
}
