package com.ylx.usercenter.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ylx.common.core.domain.R;
import com.ylx.common.exception.ServiceException;
import com.ylx.lottery.service.LotteryCountService;
import com.ylx.massage.domain.TWxUser;
import com.ylx.massage.service.TWxUserService;
import com.ylx.usercenter.domain.dto.UnifiedUserCenterDTO;
import com.ylx.usercenter.domain.vo.OneAccountVO;
import com.ylx.usercenter.domain.vo.UnifiedUserCenterResponseVO;
import com.ylx.usercenter.service.UnifiedUserCenterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {

    @Value("${remote.user-center.base-url}")
    private String userCenterBaseUrl;
    @Value("${remote.night-fragrance.client-id}")
    private String clientId;

    // 定义接口路径常量
    private static final String QUERY_BIND_PATH = "/userApp/queryBind";
    private static final String BIND_PATH = "/userApp/bind";
    private static final String UNBIND_PATH = "/userApp/unbind";
    private static final String QUERY_CLIENTS_PATH = "/userApp/queryClients";
    private static final int DEFAULT_TIMEOUT = 5000; // 5秒超时
    @Resource
    private TWxUserService wxUserService;
    @Resource
    private LotteryCountService lotteryCountService;

    @Override
    public R<?> queryBind(UnifiedUserCenterDTO dto) {
        // 只需要关注业务路径和日志描述即可
        return executePost(QUERY_BIND_PATH, dto, "查询用户绑定信息");
    }

    @Override
    public R<?> bind(UnifiedUserCenterDTO dto) {

        try {
            // 1.检验参数信息
            validateDto(dto);

            // 2.获取用户信息
            TWxUser wxUser = this.wxUserService.getById(dto.getTargetUserId());
            if (ObjUtil.isEmpty(wxUser)) {
                log.error("用户信息不存在, userId: {}", dto.getTargetUserId());
                throw new ServiceException("用户信息不存在");
            }

            // 3.添加用户绑定信息
            R<?> remoteResult = executePost(BIND_PATH, dto, "添加用户绑定信息");

            // 4. 判断绑定成功
            if (ObjUtil.notEqual(remoteResult.getCode(), 200)) {
                log.error("远程绑定失败, userId: {}, msg: {}", dto.getTargetUserId(), remoteResult.getMsg());
                return R.fail("用户绑定失败：" + remoteResult.getMsg());
            }

            // 5. 更新本地数据库（独立事务）
            updateUserBindStatus(dto,wxUser);

            // 6. 同步抽奖次数（异步处理）
            syncLotteryCount(dto);

            return R.ok(remoteResult.getData(), "用户绑定成功");

        } catch (ServiceException e) {
            log.error("用户绑定业务异常, userId:{}", dto.getTargetUserId(), e);
            throw e;
        } catch (Exception e) {
            log.error("用户绑定系统异常, userId:{}", dto.getTargetUserId(), e);
            throw new ServiceException("系统异常，请稍后重试");
        }
    }

    @Override
    public R<?> unbind(UnifiedUserCenterDTO dto) {
        validateDto(dto);
        return executePost(UNBIND_PATH, dto, "解除用户绑定信息");
    }

    @Override
    public OneAccountVO queryClients(String uuid) {
        log.info("调用一账通查询平台绑定信息：{}", uuid);
        return this.queryClientsByUuid(uuid);
    }

    /**
     * 通用 HTTP POST 请求执行模板
     */
    private R<?> executePost(String path, UnifiedUserCenterDTO dto, String logDesc) {
        try {
            // 1. 构建 URL
            String url = userCenterBaseUrl + path;

            // 2. 转换参数
            String jsonParam = JSON.toJSONString(dto);

            // 3. 发送请求
            String result = HttpUtil.post(url, jsonParam);

            // 4. 校验结果
            if (StrUtil.isEmpty(result)) {
                throw new RuntimeException(logDesc + "失败：接口返回空结果");
            }

            // 5. 解析结果
            UnifiedUserCenterResponseVO response = JSON.parseObject(
                    result,
                    new TypeReference<UnifiedUserCenterResponseVO>() {
                    }
            );

            // 4. 处理业务状态
            if (!response.isSuccess()) {
                log.warn("{}失败: data={},code={}, msg={}", logDesc, response.getData(), response.getCode(), response.getMessage());
                return R.fail(response.getData(), response.getMessage());
            }

            Object dataObj = response.getData();

            // 情况 A: data 是布尔值 false (虽然 code=0，但没数据)
            if (dataObj instanceof Boolean) {
                log.info("接口返回 data 为布尔值: {}", dataObj);
                return R.ok(response.getData());
            }

            // 情况 B: data 是 JSONObject (例如: { "userClients": [...] })
            if (dataObj instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) dataObj;

                // 优先尝试获取 "userClients" 字段 (针对当前截图的场景)
                Object userClientsObj = jsonObject.get("userClients");

                if (userClientsObj instanceof JSONArray) {
                    JSONArray userClientsArray = (JSONArray) userClientsObj;
                    log.info("获取到用户客户端数量: {}", userClientsArray.size());
                    // 如果需要转为具体的 List<UserClientVO>，可以在这里做转换
                    return R.ok(userClientsArray);
                }

                // 兜底逻辑：如果是其他类型的 JSONObject (例如查询详情)，直接返回整个对象
                // 避免数据丢失
                return R.ok(jsonObject);
            }

            // 情况 C: data 是其他类型 (例如直接是一个 String 或 Number)
            return R.ok(dataObj);

        } catch (Exception e) {
            log.error("{}发生异常", logDesc, e);
            throw new ServiceException(logDesc + "异常" + e);
        }
    }

    /**
     * 参数校验提取
     */
    private void validateDto(UnifiedUserCenterDTO dto) {

        if (StrUtil.isEmpty(dto.getSourceUserId())) {
            throw new IllegalArgumentException("源用户ID不能为空");
        }
        if (StrUtil.isEmpty(dto.getTargetUserId())) {
            throw new IllegalArgumentException("目标用户ID不能为空");
        }
        if (StrUtil.isEmpty(dto.getSourceClientId())) {
            throw new IllegalArgumentException("源客户端ID不能为空");
        }

        if (StrUtil.isEmpty(dto.getTargetClientId())) {
            throw new IllegalArgumentException("目标客户端ID不能为空");
        }
    }

    // 独立事务方法
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void updateUserBindStatus(UnifiedUserCenterDTO dto, TWxUser wxUser) {
        try {
            wxUser.setIsBind(1);
            wxUser.setLocalLiveUserId(dto.getSourceUserId());
            boolean wxUserBool = this.wxUserService.updateById(wxUser);
            if (!wxUserBool) {
                log.error("更新用户绑定状态失败, userId: {}", dto.getTargetUserId());
                throw new ServiceException("更新用户绑定状态失败");
            }
            log.info("更新用户绑定一账通信息成功, userId:{}", dto.getTargetUserId());
        } catch (Exception e) {
            log.error("更新用户绑定状态异常, userId: {}", dto.getTargetUserId(), e);
            // 如果本地更新失败，异步调用远程解绑接口进行补偿
            asyncCompensateUnbind(dto);
            throw e;
        }
    }


    // 异步同步抽奖次数
    @Async
    @Override
    public void syncLotteryCount(UnifiedUserCenterDTO dto) {
        try {
            boolean lotteryCountBool = this.lotteryCountService.batchAdd(dto);
            if (lotteryCountBool) {
                log.info("同步用户抽奖次数到本地生活服务成功, userId:{}", dto.getTargetUserId());
            } else {
                log.warn("同步用户抽奖次数到本地生活服务失败, userId:{}", dto.getTargetUserId());
                // 同步失败，记录告警日志
                log.error("【告警】同步用户抽奖次数到本地生活服务失败，需要人工处理, userId:{}", dto.getTargetUserId());
            }
        } catch (Exception e) {
            log.error("同步用户抽奖次数到本地生活服务异常, userId:{}", dto.getTargetUserId(), e);
            // 异常情况，记录告警日志
            log.error("【告警】同步用户抽奖次数到本地生活服务异常，需要人工处理, userId:{}", dto.getTargetUserId(), e);
        }
    }

    @Override
    public R<?> bindUpdate(UnifiedUserCenterDTO dto) {
        try {

            // 1.获取用户信息
            TWxUser wxUser = this.wxUserService.getById(dto.getTargetUserId());
            if (ObjUtil.isEmpty(wxUser)) {
                log.error("用户信息不存在, userId: {}", dto.getTargetUserId());
                throw new ServiceException("用户信息不存在");
            }

            // 2. 更新本地数据库（独立事务）
            updateUserBindStatus(dto,wxUser);

            // 3. 同步抽奖次数（异步处理）
            syncLotteryCount(dto);

            log.info("同步用户抽奖次数到本地生活服务成功, userId:{}", dto.getTargetUserId());

            return R.ok();
        } catch (ServiceException e) {
            log.error("同步用户抽奖次数到本地生活服务异常, userId:{}", dto.getTargetUserId(), e);
            throw e;
        } catch (Exception e) {
            log.error("同步用户抽奖次数到本地生活服务异常, userId:{}", dto.getTargetUserId(), e);
            throw new ServiceException("系统异常，请稍后重试");
        }
    }

    /**
     * 异步补偿：解除用户绑定
     * 带重试机制，最多重试3次
     */
    @Async
    public void asyncCompensateUnbind(UnifiedUserCenterDTO dto) {
        final int maxRetries = 3;
        int retryCount = 0;
        boolean success = false;

        while (retryCount < maxRetries && !success) {
            retryCount++;
            try {
                log.info("异步补偿：尝试解除用户绑定, userId: {}, 重试次数: {}", dto.getTargetUserId(), retryCount);
                executePost(UNBIND_PATH, dto, "补偿：解除用户绑定信息");
                log.info("异步补偿：成功解除用户绑定, userId: {}", dto.getTargetUserId());
                success = true;
            } catch (Exception e) {
                log.error("异步补偿：解除用户绑定失败, userId: {}, 重试次数: {}", dto.getTargetUserId(), retryCount, e);
                if (retryCount < maxRetries) {
                    try {
                        // 等待一段时间后重试
                        Thread.sleep(1000 * retryCount);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        log.error("异步补偿：线程被中断, userId: {}", dto.getTargetUserId(), ie);
                        break;
                    }
                } else {
                    // 重试次数用完，记录告警日志
                    log.error("【告警】异步补偿：解除用户绑定失败，已达最大重试次数，需要人工处理, userId: {}", dto.getSourceUserId(), e);
                }
            }
        }
    }


    private OneAccountVO queryClientsByUuid(String uuid) {
        try {
            // 1. 构建请求参数
            String url = userCenterBaseUrl + QUERY_CLIENTS_PATH;
            Map<String, Object> params = Collections.singletonMap("uuid", uuid);

            // 2. 发送请求
            String resultJson = HttpUtil.get(url, params, DEFAULT_TIMEOUT);

            // 3. 校验结果
            if (StrUtil.isEmpty(resultJson)) {
                throw new RuntimeException("调用一账通查询平台绑定信息接口失败：接口返回空结果");
            }

            // 4. 解析结果
            UnifiedUserCenterResponseVO<List<OneAccountVO>> response = JSON.parseObject(
                    resultJson,
                    new TypeReference<UnifiedUserCenterResponseVO<List<OneAccountVO>>>() {
                    }
            );

            // 5. 处理业务状态
            if (ObjUtil.isNull(response) || !response.isSuccess()) {
                String msg = ObjUtil.isNotNull(response) ? response.getMessage() : "未知错误";
                Integer code = ObjUtil.isNotNull(response) ? response.getCode() : -1;

                log.warn("调用一账通查询平台绑定信息接口失败: code={}, msg={}", code, msg);
                throw new ServiceException("远程接口返回失败: " + msg);
            }

            // 6. 获取数据列表
            List<OneAccountVO> oneAccountVOs = response.getData();

            if (CollUtil.isEmpty(oneAccountVOs)) {
                log.info("未查询到相关活动数据");
                return null;
            }

            OneAccountVO oneAccount = oneAccountVOs.stream().filter(item -> item.getClientId().equals(clientId)).findFirst().orElse(null);

            log.info("成功获取调用一账通查询平台绑定信息: {}", JSON.toJSONString(oneAccount));

            return oneAccount;

        } catch (ServiceException e) {
            // 透传业务异常，不要重复包装
            throw e;
        } catch (Exception e) {
            log.error("调用一账通查询平台绑定信息接口发生系统异常", e);
            // 优化点：只抛出消息，不要直接把整个 Exception 对象 toString() 拼接到字符串里
            throw new ServiceException("调用一账通查询平台绑定信息接口异常: " + e.getMessage());
        }
    }
}