package com.haha.miniapp.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.haha.common.constant.OrderConstants;
import com.haha.common.enums.DeviceDoorStatus;
import com.haha.common.enums.DeviceOnlineStatus;
import com.haha.common.enums.NotifyType;
import com.haha.common.enums.ProductAuditStatus;
import com.haha.common.enums.RecognizeActionType;
import com.haha.common.enums.RecognizeConsumeType;
import com.haha.entity.Order;
import com.haha.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import jakarta.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 哈哈平台回调通知接口
 *
 * 接收哈哈平台推送的各类通知，分为两类回调地址：
 *
 * 1. 消息回调地址 (/callback/haha/message)
 *    - DEVICE_STATUS: 开关门状态通知
 *    - ONLINE_STATUS: 设备在线状态通知
 *    - VOICE_RESULT: 音量调节结果通知
 *    - CLIENT_NEW_PRODUCT: 新品审核结果回调
 *    - MERGE_PRODUCT: 商品合并结果通知
 *    - ORC_RESULT: AI识别结果通知（最重要）
 *
 * 2. 订单回调地址 (/callback/haha/order)
 *    - 哈哈平台生成订单后推送的订单信息
 *    - 包含订单号、设备号、用户ID、订单明细、金额等
 *
 * 注意：
 * - 所有通知都包含 sign 字段用于验证
 * - 必须返回 "success" 字符串表示接收成功
 * - 未正确响应会触发重试机制
 */
@Slf4j
@RestController
@RequestMapping("/callback/haha")
@SaIgnore // 第三方回调接口，不需要token验证
public class CallbackController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Value("${haha.api.app-secret}")
    private String appSecret;

    private static final String DEVICE_STATUS_KEY = "haha:device:status:";
    private static final String RECOGNIZE_RESULT_KEY = "haha:recognize:result:";
    private static final String ORDER_INFO_KEY = "haha:order:info:";

    /**
     * 消息回调通知统一入口
     *
     * 接收哈哈平台推送的所有类型通知，通过 notify_type 区分具体类型
     * 支持 application/json 和 application/x-www-form-urlencoded 两种格式
     *
     * @param request HTTP请求对象
     * @param requestParams form表单参数（用于application/x-www-form-urlencoded）
     * @return 响应结果，必须返回 "success" 字符串
     */
    @PostMapping("/message")
    public String handleMessageCallback(HttpServletRequest request, @RequestParam(required = false) Map<String, String> requestParams) {
        try {
            // 解析请求参数（兼容JSON和表单两种格式）
            Map<String, Object> params = parseRequestParams(request, requestParams);

            // 记录回调日志
            log.info("收到消息回调通知: {}", JSON.toJSONString(params));

            // 1. 验证签名（如果需要）
            // validateSign(params);

            // 2. 获取通知类型
            String notifyType = (String) params.get("notify_type");

            if (notifyType == null || notifyType.isEmpty()) {
                log.warn("消息回调缺少 notify_type 参数");
                return "success";
            }

            NotifyType type = NotifyType.fromCode(notifyType);
            if (type == null) {
                log.warn("未知的消息通知类型: {}", notifyType);
                return "success";
            }

            switch (type) {
                case DEVICE_STATUS:
                    handleDeviceStatus(params);
                    break;
                case ONLINE_STATUS:
                    handleOnlineStatus(params);
                    break;
                case VOICE_RESULT:
                    handleVoiceResult(params);
                    break;
                case CLIENT_NEW_PRODUCT:
                    handleNewProductAudit(params);
                    break;
                case MERGE_PRODUCT:
                    handleMergeProduct(params);
                    break;
                case ORC_RESULT:
                    handleOrcResult(params);
                    break;
                default:
                    log.warn("未处理的消息通知类型: {}", type);
            }

            // 4. 返回成功响应（必须返回 "success" 字符串）
            return "success";

        } catch (Exception e) {
            log.error("处理消息回调通知失败", e);
            // 即使处理失败，也返回成功，避免重复推送
            return "success";
        }
    }

    /**
     * 处理开关门状态通知 (DEVICE_STATUS)
     *
     * status取值：
     * - 1=ERROR（开门失败）
     * - 2=OPENED（门已开）
     * - 3=CLOSED（门已关）
     * - 4=ANOTHER（设备繁忙）
     *
     * @param params 通知参数
     */
    private void handleDeviceStatus(Map<String, Object> params) {
        try {
            String activityId = (String) params.get("activity_id");
            String deviceId = (String) params.get("device_id");
            String status = (String) params.get("status");
            String openType = (String) params.get("open_type");
            String outUserId = (String) params.get("out_user_id");

            log.info("开关门状态通知 - 设备: {}, 状态: {}, 类型: {}, 用户: {}",
                deviceId, status, openType, outUserId);

            // 保存设备状态到Redis，供小程序轮询查询
            String statusKey = DEVICE_STATUS_KEY + deviceId;
            Map<String, String> statusData = new HashMap<>();
            statusData.put("deviceId", deviceId);
            statusData.put("activityId", activityId != null ? activityId : "");
            statusData.put("status", status);
            statusData.put("openType", openType != null ? openType : "");
            statusData.put("userId", outUserId != null ? outUserId : "");
            statusData.put("doorStatus", DeviceDoorStatus.convertToStatus(status));
            statusData.put("timestamp", String.valueOf(System.currentTimeMillis()));

            redisTemplate.opsForHash().putAll(statusKey, statusData);
            redisTemplate.expire(statusKey, 30, TimeUnit.MINUTES);

            DeviceDoorStatus doorStatus = DeviceDoorStatus.fromCode(status);
            if (doorStatus != null) {
                switch (doorStatus) {
                    case OPENED:
                        log.info("设备 {} 开门成功", deviceId);
                        break;
                    case CLOSED:
                        log.info("设备 {} 关门成功，等待AI识别", deviceId);
                        break;
                    case ERROR:
                        log.error("设备 {} 开门失败", deviceId);
                        break;
                    case BUSY:
                        log.warn("设备 {} 繁忙", deviceId);
                        break;
                }
            }

        } catch (Exception e) {
            log.error("处理开关门状态通知失败", e);
        }
    }

    private void handleOnlineStatus(Map<String, Object> params) {
        try {
            String deviceId = (String) params.get("device_id");
            Integer isOnline = parseInteger(params.get("is_online"));

            log.info("设备在线状态通知 - 设备: {}, 在线状态: {}",
                deviceId, DeviceOnlineStatus.isOnline(isOnline) ? "在线" : "离线");

        } catch (Exception e) {
            log.error("处理设备在线状态通知失败", e);
        }
    }

    /**
     * 处理音量调节结果通知 (VOICE_RESULT)
     *
     * @param params 通知参数
     */
    private void handleVoiceResult(Map<String, Object> params) {
        try {
            String deviceId = (String) params.get("device_id");
            Integer voice = parseInteger(params.get("voice"));

            log.info("音量调节结果通知 - 设备: {}, 音量值: {}", deviceId, voice);

        } catch (Exception e) {
            log.error("处理音量调节结果通知失败", e);
        }
    }

    /**
     * 处理新品审核结果回调 (CLIENT_NEW_PRODUCT)
     *
     * status取值：
     * - 1=已通过
     * - 2=已拒绝
     * - 3=已上架
     *
     * @param params 通知参数
     */
    private void handleNewProductAudit(Map<String, Object> params) {
        try {
            String id = (String) params.get("id");
            Integer status = parseInteger(params.get("status"));
            String name = (String) params.get("name");
            String code = (String) params.get("code");
            String rejectReason = (String) params.get("reject_reason");

            log.info("新品审核结果 - ID: {}, 商品名: {}, 状态: {}, code: {}", id, name, status, code);

            if (ProductAuditStatus.isRejected(status)) {
                log.warn("新品 {} 被拒绝，原因: {}", name, rejectReason);
            }

        } catch (Exception e) {
            log.error("处理新品审核结果通知失败", e);
        }
    }

    /**
     * 处理商品合并结果通知 (MERGE_PRODUCT)
     *
     * @param params 通知参数
     */
    private void handleMergeProduct(Map<String, Object> params) {
        try {
            Object listObj = params.get("list");

            if (listObj instanceof JSONArray list) {
                log.info("商品合并结果通知 - 合并数量: {}", list.size());

                for (Object item : list) {
                    if (item instanceof JSONObject merge) {
                        String mainCode = merge.getString("main_code");
                        String productCode = merge.getString("product_code");
                        log.info("商品合并 - 主商品: {}, 合并商品: {}", mainCode, productCode);
                    }
                }
            }

        } catch (Exception e) {
            log.error("处理商品合并结果通知失败", e);
        }
    }

    /**
     * 处理AI识别结果通知 (ORC_RESULT) - 最重要的回调
     *
     * 用户关门后，AI识别完成，哈哈平台推送识别结果
     *
     * @param params 通知参数
     */
    private void handleOrcResult(Map<String, Object> params) {
        try {
            String activityId = (String) params.get("activity_id");
            String deviceId = (String) params.get("device_id");
            String userId = (String) params.get("out_user_id");

            // 安全转换 nobuy 字段（兼容字符串和整型）
            Integer nobuy = parseInteger(params.get("nobuy"));

            // 解析识别结果
            String resultStr = (String) params.get("result");
            String skuListStr = (String) params.get("sku_list");
            String resourceInfoStr = (String) params.get("resource_info");

            log.info("AI识别结果通知 - 设备: {}, 活动: {}, 是否消费: {}",
                deviceId, activityId, RecognizeConsumeType.isNoConsume(nobuy) ? "无消费" : "有消费");

            // 保存识别结果到Redis，供小程序轮询查询
            String resultKey = RECOGNIZE_RESULT_KEY + activityId;
            Map<String, String> recognizeData = new HashMap<>();
            recognizeData.put("activityId", activityId != null ? activityId : "");
            recognizeData.put("deviceId", deviceId != null ? deviceId : "");
            recognizeData.put("userId", userId != null ? userId : "");
            recognizeData.put("nobuy", nobuy != null ? String.valueOf(nobuy) : "0");
            recognizeData.put("result", resultStr != null ? resultStr : "");
            recognizeData.put("skuList", skuListStr != null ? skuListStr : "");
            recognizeData.put("resourceInfo", resourceInfoStr != null ? resourceInfoStr : "");
            recognizeData.put("timestamp", String.valueOf(System.currentTimeMillis()));

            redisTemplate.opsForHash().putAll(resultKey, recognizeData);
            redisTemplate.expire(resultKey, 30, TimeUnit.MINUTES);

            if (RecognizeConsumeType.isNoConsume(nobuy)) {
                log.info("用户打开柜门但未消费，无需处理");
                return;
            }

            if (resultStr != null && !resultStr.isEmpty()) {
                JSONObject result = JSON.parseObject(resultStr);
                String type = result.getString("type");
                JSONArray data = result.getJSONArray("data");
                JSONArray excepts = result.getJSONArray("excepts");

                RecognizeActionType actionType = RecognizeActionType.fromCode(type);
                log.info("识别类型: {}, 数据: {}",
                    actionType != null ? actionType.getDescription() : type,
                    data != null ? data.size() : 0);

                if (excepts != null && !excepts.isEmpty()) {
                    log.warn("识别结果包含异常: {}", excepts.toJSONString());
                }
            }

            // 解析sku_list
            if (skuListStr != null && !skuListStr.isEmpty()) {
                JSONArray skuList = JSON.parseArray(skuListStr);
                log.info("商品数量: {}", skuList != null ? skuList.size() : 0);
            }

            // 解析resource_info获取视频URL
            if (resourceInfoStr != null && !resourceInfoStr.isEmpty()) {
                JSONObject resourceInfo = JSON.parseObject(resourceInfoStr);
                String videoUrl = resourceInfo.getString("video_url");
                log.info("视频URL: {}", videoUrl);
            }

            // 解析 confidence
            BigDecimal confidence = null;
            if (params.get("confidence") != null) {
                try {
                    confidence = new BigDecimal(params.get("confidence").toString());
                } catch (NumberFormatException ignored) {}
            }

            // 委托 Service 层创建订单
            Order order = orderService.createOrderFromRecognition(
                activityId, deviceId, userId, skuListStr, resourceInfoStr, confidence);

            if (order != null) {
                log.info("AI识别订单创建成功 - 订单ID: {}", order.getId());
            }

            // 3. 优惠信息处理
            // TODO: 根据业务需求处理优惠券、折扣等

            // 4. 触发支付流程
            // TODO: 根据业务需求触发支付

        } catch (Exception e) {
            log.error("处理AI识别结果通知失败", e);
        }
    }

    /**
     * 订单回调通知接口
     *
     * 当AI识别完成并生成订单后，哈哈平台会推送订单信息到此接口
     * 这是独立于消息回调的订单回调接口
     * 支持 application/json 和 application/x-www-form-urlencoded 两种格式
     *
     * @param request HTTP请求对象
     * @param requestParams form表单参数（用于application/x-www-form-urlencoded）
     * @return 响应结果，必须返回 "success" 字符串
     */
    @PostMapping("/order")
    public String handleOrderCallback(
            HttpServletRequest request,
            @RequestParam(required = false) Map<String, String> requestParams) {
        try {
            // 解析请求参数（兼容JSON和表单两种格式）
            Map<String, Object> params = parseRequestParams(request, requestParams);

            // 记录回调日志
            log.info("收到订单回调通知: {}", JSON.toJSONString(params));

            // 1. 验证签名（如果需要）
            // validateSign(params);

            // 2. 提取订单信息
            String orderId = (String) params.get("order_id");
            String deviceId = (String) params.get("device_id");
            String activityId = (String) params.get("activity_id");
            String userId = (String) params.get("user_id");
            String orderName = (String) params.get("order_name");
            String orderDetail = (String) params.get("order_detail");
            String from = (String) params.get("from");
            Object orderMoney = params.get("order_money");
            Object createTime = params.get("create_time");

            log.info("订单信息 - 订单号: {}, 设备: {}, 活动: {}, 用户: {}, 金额: {}",
                orderId, deviceId, activityId, userId, orderMoney);

            // 3. 查找本地订单记录
            Order localOrder = orderService.lambdaQuery()
                .eq(Order::getUserId, Long.parseLong(userId))
                .eq(Order::getDeviceId, deviceId)
                .eq(Order::getStatus, OrderConstants.STATUS_PENDING_PAYMENT)
                .orderByDesc(Order::getCreateTime)
                .last("LIMIT 1")
                .one();

            if (localOrder == null) {
                log.warn("未找到对应的本地订单记录，用户: {}, 设备: {}", userId, deviceId);
                return "success";
            }

            // 4. 更新本地订单信息
            localOrder.setOrderNo(orderId);
            localOrder.setActivityId(activityId);

            // 将订单明细存储到 items 字段（JSON格式）
            if (orderDetail != null) {
                localOrder.setItems(orderDetail);
            }

            // 设置订单金额
            if (orderMoney != null) {
                localOrder.setTotalAmount(new BigDecimal(orderMoney.toString()));
            }

            boolean updated = orderService.updateById(localOrder);
            if (updated) {
                log.info("订单信息更新成功: {}", orderId);
            } else {
                log.error("订单信息更新失败: {}", orderId);
            }

            // 保存订单信息到Redis，供小程序轮询查询
            String orderKey = ORDER_INFO_KEY + activityId;
            Map<String, String> orderData = new HashMap<>();
            orderData.put("orderId", orderId != null ? orderId : "");
            orderData.put("activityId", activityId != null ? activityId : "");
            orderData.put("deviceId", deviceId != null ? deviceId : "");
            orderData.put("userId", userId != null ? userId : "");
            orderData.put("orderName", orderName != null ? orderName : "");
            orderData.put("totalAmount", orderMoney != null ? orderMoney.toString() : "0");
            orderData.put("products", orderDetail != null ? orderDetail : "");
            orderData.put("timestamp", String.valueOf(System.currentTimeMillis()));

            redisTemplate.opsForHash().putAll(orderKey, orderData);
            redisTemplate.expire(orderKey, 30, TimeUnit.MINUTES);

            // 5. TODO: 根据业务需要进行后续处理
            // - 触发支付流程
            // - 发送通知给用户
            // - 更新库存等

            // 6. 返回成功响应
            return "success";

        } catch (Exception e) {
            log.error("处理订单回调通知失败", e);
            // 即使处理失败，也返回成功，避免重复推送
            return "success";
        }
    }

    /**
     * 解析请求参数，支持 JSON 和 form-urlencoded 两种格式
     *
     * @param request HTTP请求对象
     * @param requestParams form表单参数
     * @return 参数Map
     */
    private Map<String, Object> parseRequestParams(HttpServletRequest request, Map<String, String> requestParams) {
        Map<String, Object> params = new HashMap<>();

        String contentType = request.getContentType();
        log.debug("请求Content-Type: {}", contentType);

        try {
            // 判断 Content-Type
            if (contentType != null && contentType.toLowerCase().contains("application/json")) {
                // JSON 格式：从 request body 读取
                StringBuilder sb = new StringBuilder();
                try (BufferedReader reader = request.getReader()) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        sb.append(line);
                    }
                }
                String body = sb.toString();
                if (body != null && !body.isEmpty()) {
                    params = JSON.parseObject(body, Map.class);
                    log.debug("解析JSON参数: {}", params.size());
                }
            } else {
                // form-urlencoded 格式：使用 @RequestParam 捕获的参数
                if (requestParams != null && !requestParams.isEmpty()) {
                    params.putAll(requestParams);
                    log.debug("解析表单参数: {}", params.size());
                }
            }
        } catch (Exception e) {
            log.error("解析请求参数失败", e);
        }

        return params;
    }

    /**
     * 安全转换为整型，兼容字符串和整型
     *
     * @param value 原始值
     * @return 整型值，如果转换失败返回null
     */
    private Integer parseInteger(Object value) {
        if (value == null) {
            return null;
        }

        try {
            if (value instanceof Integer) {
                return (Integer) value;
            } else if (value instanceof String) {
                String str = (String) value;
                if (str.trim().isEmpty()) {
                    return null;
                }
                return Integer.parseInt(str);
            } else if (value instanceof Number) {
                return ((Number) value).intValue();
            }
        } catch (NumberFormatException e) {
            log.warn("无法转换为整型: {}", value);
        }

        return null;
    }

    /**
     * 验证签名
     *
     * @param params 回调参数
     * @throws Exception 签名验证失败
     */
    private void validateSign(Map<String, Object> params) throws Exception {
        String receivedSign = (String) params.get("sign");
        if (receivedSign == null || receivedSign.isEmpty()) {
            throw new Exception("签名参数为空");
        }

        // TODO: 实现签名验证逻辑
        // 1. 提取所有参数（除sign外）
        // 2. 按字典序排序
        // 3. 拼接 + AppSecret
        // 4. MD5加密
        // 5. 比对签名
    }
}
