package com.cyksj.service.claude.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.mapper.*;
import com.cyksj.model.entity.*;
import com.cyksj.model.request.gpt.ConversationRequest;
import com.cyksj.model.response.ConversationLimitResponse;
import com.cyksj.model.views.ClaudeUserView;
import com.cyksj.redis.RedisService;
import com.cyksj.service.claude.ClaudeService;
import com.cyksj.service.sys.SysConfigService;
import com.cyksj.service.user.UserBindRelationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * @author chan
 * @date 2024/8/8 18:34
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ClaudeServiceImpl implements ClaudeService {

    private final ClaudeUserMapper claudeUserMapper;

    private final GroupsRelationMapper groupsRelationMapper;

    private final GroupsMapper groupsMapper;

    private final GoodsDonSkuMapper skuMapper;

    private final UserBindRelationService userBindRelationService;

    private final UserMapper userMapper;

    private final SysConfigService sysConfigService;

    private final RedisService redisService;

    private final ClaudeUserCarUsedRecordMapper claudeUserCarUsedRecordMapper;

    private final ClaudeUserConversationRecordMapper claudeUserConversationRecordMapper;

    @Override
    public ClaudeUserView getUserInfo(long userId, Long relationId) {
        ClaudeUser claudeUser = checkCarAccount(userId, relationId);
        if (claudeUser != null) {

            GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
            GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
            GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());

            ClaudeUserView view = ClaudeUserView.builder()
                    .skuName(goodsDonSku.getSubTitle())
                    .expireTime(claudeUser.getExpireTime())
                    .limitNum(claudeUser.getLimitNum())
                    .limitTime(claudeUser.getLimitTime())
                    .isRenewPackage(claudeUser.getRenewOrderId() != null ? true : false)
                    .renewSkuId(claudeUser.getRenewSkuId())
                    .renewOrderId(claudeUser.getRenewOrderId())
                    .use(Math.min(claudeUser.getLimitNum(), getConversationCount(claudeUser.getUserToken(), claudeUser.getLimitTime())))
                    .build();
            if (view.getRenewSkuId() != null) {
                Optional.ofNullable(skuMapper.selectById(view.getRenewSkuId())).ifPresent(renewSku -> view.setRenewSpecVal(renewSku.getSpecVal()));
            }
            return view;
        } else {
            throw BusinessRuntimeException.getInstance("您还未购买该车票");
        }
    }

    @Override
    public ClaudeUser findByUserTokenAndExpireTimeAfter(String userToken, LocalDateTime now) {
        return claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getUserToken, userToken).gt(ClaudeUser::getExpireTime, now));
    }

    @Override
    public ConversationLimitResponse conversationLimit(String userToken, ClaudeUser user) {
        return isConversationAllowed(userToken, user.getLimitNum(), user.getLimitTime());
    }

    @Override
    public void carLimited(String carId, String userToken, Long expTime) {
        redisService.set("claude:clears_in:" + carId, expTime, expTime);

        try {
            //记录用户触发限制
            ClaudeUserCarUsedRecord claudeUserCarUsedRecord = new ClaudeUserCarUsedRecord();
            claudeUserCarUsedRecord.setUserToken(userToken);
            claudeUserCarUsedRecord.setCarId(carId);

            String carChatKey = RedisService.key.CLAUDE_CAR_CHAT.getName() + carId;

            Long carChatNum = redisService.zCard(carChatKey);
            if (carChatNum != null) {
                claudeUserCarUsedRecord.setNum(Integer.parseInt(carChatNum.toString()));
            }
            claudeUserCarUsedRecordMapper.insert(claudeUserCarUsedRecord);
            log.info("记录userToken:{} 触发限制 carId：{}", userToken, carId);
        } catch (Exception e) {
            log.info("记录userToken:{} 触发限制 carId：{} 操作失败e：{}", userToken, carId, StringUtil.getErrorText(e));
        }
    }

    @Override
    public ClaudeUser getClaudeUser(long userId, Long relationId) {
        GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
        GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
        GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());

        if (goodsDonSku != null && goodsDonSku.getGoodsId() == 75 && goodsDonSku.getIsMirror()) {
            return getClaudeUser(userId, relationId, groupsRelation, goodsDonSku);
        } else {
            throw BusinessRuntimeException.getInstance("服务器出了点问题");
        }
    }

    @Override
    public void saveConversationRecord(String userToken, ConversationRequest conversationRequest) {
        ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getUserToken, userToken));
        ClaudeUserConversationRecord claudeUserConversationRecord = new ClaudeUserConversationRecord();
        claudeUserConversationRecord.setUserToken(userToken);
        claudeUserConversationRecord.setCarId(conversationRequest.getCarId());

        if (StringUtils.isNotBlank(conversationRequest.getConversation_id())) {
            claudeUserConversationRecord.setConversationId(conversationRequest.getConversation_id());
        }

        claudeUserConversationRecord.setModel(conversationRequest.getModel());
        claudeUserConversationRecordMapper.insert(claudeUserConversationRecord);


        String key = RedisService.key.CLAUDE_CONVERSATION_LIMIT.getName() + ":" + userToken;
        long currentTimeMillis = System.currentTimeMillis();

        if (redisService.hasKey("claude:clears_in:" + conversationRequest.getCarId())) {
            redisService.del("claude:clears_in:" + conversationRequest.getCarId());
        }

        // 如果未超过限制，记录当前请求的时间戳
        redisService.zAdd(key, currentTimeMillis, currentTimeMillis);
        // 设置ZSet的过期时间，窗口大小加上一段冗余时间
        redisService.expire(key, (claudeUser.getLimitTime() * 60 * 60) + 20);

        if (StringUtils.isNotBlank(conversationRequest.getCarId())) {
            updateExperienceAndScore(conversationRequest.getCarId(), System.currentTimeMillis());
        }
    }

    @Override
    public ClaudeUser checkCarAccount(Long userId, Long relationId) {
        GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
        GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
        GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());

        if (goodsDonSku != null && goodsDonSku.getGoodsId() == 75 && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) {
            return getClaudeUser(userId, relationId, groupsRelation, goodsDonSku);
        } else {
            return null;
        }
    }


    @Override
    public String getCarLoginUrl(String domain, Long userId, Long relationId, String carId) {

        GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
        GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
        GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
        if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) {
            ClaudeUser claudeUser = getClaudeUser(userId, relationId, groupsRelation, goodsDonSku);

            String DOMAIN = domain;
            if (StrUtil.isEmpty(DOMAIN)) {
                DOMAIN = "https://claudeplus.com.cn";
                try {
                    SysConfig sysConfig = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "mirror_claude_domain"));
                    if (sysConfig != null) {
                        String sysValue = sysConfig.getSysValue();
                        if (JSONUtil.isJsonArray(sysValue)) {
                            List<String> domainList = JSONUtil.parseArray(sysValue).toList(String.class);
                            DOMAIN = domainList.get(RandomUtil.randomInt(domainList.size()));
                        }
                    }
                } catch (Exception e) {
                    log.error("随机抽取域名错误. msg:{}", StringUtil.getErrorText(e));
                }
            }
            log.info("domain:{}，用户:{},所在车次:{},座位id:{},在{}获取跳转Claude镜像的登录url", DOMAIN, userId, groupsTrips.getId(), relationId, DateTime.now());
            return DOMAIN + "/auth/logintoken?carid=" + carId + "&usertoken=" + claudeUser.getUserToken();
        } else {
            throw BusinessRuntimeException.getInstance("服务器出了点问题");
        }
    }

    /**
     * 获取指定用户ID在滑动窗口内的提问次数
     *
     * @param userToken 用户token
     * @return 滑动窗口内的请求次数
     */
    @Override
    public Long getConversationCount(String userToken, Long limitTime) {
        JSONObject conversationCount = getConversationCount(userToken);
        Long count = Optional.ofNullable(conversationCount.getLong("count")).orElse(0l);
        return new BigDecimal(count).divide(BigDecimal.valueOf(4000),0, RoundingMode.HALF_UP).longValue();
    }

    @Override
    public Long carLimit(String carId) {
        if(redisService.hasKey("claude:clears_in:" + carId)){
            return redisService.getExpire("claude:clears_in:" + carId);
        }
        return 0L;
    }

    @Override
    public void clearNum(String userToken) throws Exception {
        HttpRequest request = new HttpRequest("http://147.79.20.183:9611/claude/cleanUserTokenLimit?userToken=" + userToken);
        HttpResponse execute = request.method(Method.GET).setConnectionTimeout(3000).timeout(3000).execute();
        String body = execute.body();
        JSONObject re = Jsons.parseObject(body, JSONObject.class);
        if (!body.contains("清除成功")) {
            throw BusinessRuntimeException.getInstance(re.getStr("message"));
        }
    }

    @Override
    public void clearNumByRelationId(Long relationId) throws Exception {
        ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getRelationId, relationId));

        HttpRequest request = new HttpRequest("http://147.79.20.183:9611/claude/cleanUserTokenLimit?userToken=" + claudeUser.getUserToken());
        HttpResponse execute = request.method(Method.GET).setConnectionTimeout(3000).timeout(3000).execute();
        String body = execute.body();
        JSONObject re = Jsons.parseObject(body, JSONObject.class);
        if (!body.contains("清除成功")) {
            throw BusinessRuntimeException.getInstance(re.getStr("message"));
        }
    }



    /**
     * 获取车位信息
     *
     * @param userId
     * @param relationId
     * @return
     */
    private GroupsRelation getGroupsRelation(Long userId, Long relationId) {
        List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
        GroupsRelation groupsRelation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).in(GroupsRelation::getUserId, userIdList).eq(GroupsRelation::getId, relationId));
        if (groupsRelation == null) {
            throw BusinessRuntimeException.getInstance("车票不存在");
        }
        return groupsRelation;
    }

    /**
     * 获取车队信息
     *
     * @param groupsRelation
     * @return
     */
    private GroupsTrips getGroupsTrips(GroupsRelation groupsRelation) {
        GroupsTrips groupsTrips = groupsMapper.selectById(groupsRelation.getGroupsId());
        if (groupsTrips == null) {
            throw BusinessRuntimeException.getInstance("车队异常");
        }
        return groupsTrips;
    }

    private ClaudeUser getClaudeUser(Long userId, Long relationId, GroupsRelation groupsRelation, GoodsDonSku goodsDonSku) {
        ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getRelationId, relationId));
        User user = userMapper.selectById(userId);
        if (claudeUser == null) {
            claudeUser = createClaudeUser(groupsRelation, user, goodsDonSku);
        } else {
            updateClaudeUser(groupsRelation, user, claudeUser);
        }
        return claudeUser;
    }


    /**
     * 创建chatgptUser
     */
    private synchronized ClaudeUser createClaudeUser(GroupsRelation groupsRelation, User user, GoodsDonSku goodsDonSku) {
        ClaudeUser claudeUser = new ClaudeUser();
        if (goodsDonSku.getIsCar()) {
            claudeUser.setLimitNum(goodsDonSku.getGptLimitNum());
            claudeUser.setLimitTime(goodsDonSku.getGptLimitTime());
        }
        try {
            claudeUser.setExpireTime(groupsRelation.getExpiryTime());
            claudeUser.setIsPlus(true);
            claudeUser.setName(user.getNickname());
            claudeUser.setRelationId(groupsRelation.getId());
            claudeUser.setUserToken(UUID.randomUUID().toString());
            claudeUserMapper.insert(claudeUser);

        } catch (DuplicateKeyException e) {
            log.error("重复插入Claude镜像用户:{}token,errmsg:{}", user.getNickname(), StringUtil.getErrorText(e));
        }
        return claudeUser;
    }


    private void updateClaudeUser(GroupsRelation groupsRelation, User user, ClaudeUser claudeUser) {
        claudeUser.setName(user.getNickname());
        claudeUser.setExpireTime(groupsRelation.getExpiryTime());
        claudeUserMapper.updateById(claudeUser);
    }


    /**
     * 检查是否允许进行进行提问
     *
     * @param userToken 用户token
     * @return true 如果允许请求，false 如果请求被限制
     */
    public ConversationLimitResponse isConversationAllowed(String userToken, int limit, Long limitTime) {
        String key = RedisService.key.CLAUDE_CONVERSATION_LIMIT.getName() + ":" + userToken;
        long currentTimeMillis = System.currentTimeMillis();
        long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
        ConversationLimitResponse conversationLimitResponse = new ConversationLimitResponse();
        // 清除时间窗口之前的请求记录
        redisService.zRemoveRangeByScore(key, 0, windowStartMillis);

        Long currentSize = redisService.zCard(key);
        if (currentSize != null && currentSize >= limit) {
            // 如果当前请求次数超过限制，则拒绝请求
            Set<Object> times = redisService.zRangeByScore(key, 0, currentTimeMillis, 0, 1);
            Long oldestTime = (Long) times.stream().findFirst().orElse(null);
            if (oldestTime != null) {
                // 下一次可用时间是最早请求时间之后的3小时
                long nextAvailableTime = oldestTime + (limitTime * 60 * 60 * 1000);
                conversationLimitResponse.setNextAvailableTime(nextAvailableTime);
            }
            conversationLimitResponse.setLimited(true);
            //用户次数使用限制
            ClaudeUserCarUsedRecord claudeUserCarUsedRecord = new ClaudeUserCarUsedRecord();
            claudeUserCarUsedRecord.setUserToken(userToken);
            claudeUserCarUsedRecord.setIsUserLimit(true);
            claudeUserCarUsedRecordMapper.insert(claudeUserCarUsedRecord);
            return conversationLimitResponse;
        } else {
            conversationLimitResponse.setLimited(false);
            return conversationLimitResponse;
        }
    }

    // 更新体验并计算评分
    public void updateExperienceAndScore(String carId, long timestamp) {
        // 定义键名
        String key = RedisService.key.CLAUDE_CAR_CHAT.getName() + carId;
        // 更新体验数据
        redisService.zAdd(key, timestamp, String.valueOf(timestamp));

        // 清理旧数据（可选）和重新计算评分（根据需要实现）
        cleanupOldExperiencesAndRecalculateScore(carId, timestamp);
    }

    // 清理旧数据和重新计算评分
    public void cleanupOldExperiencesAndRecalculateScore(String carId, long currentTimestamp) {
        long threeHoursAgo = currentTimestamp - (6 * 60 * 60 * 1000); // 3小时前的时间戳

        // 清理高级体验旧数据
        redisService.zRemoveRangeByScore(RedisService.key.CLAUDE_CAR_CHAT.getName() + carId, 0, threeHoursAgo);

        // 重新计算评分
        recalculateScore(carId, currentTimestamp);
    }


    // 重新计算指定车队的评分
    private void recalculateScore(String carId, long currentTimestamp) {
        // 实际应用中，你需要根据高级体验和低级体验的数量重新计算得分
        double highExperienceScore = (double) redisService.zCount(RedisService.key.CLAUDE_CAR_CHAT.getName() + carId, currentTimestamp - (6 * 60 * 60 * 1000), currentTimestamp) * 2;
        redisService.zAdd(RedisService.key.CLAUDE_CAR_SCORES.getName(), highExperienceScore, carId);
    }

    public JSONObject getConversationCount(String userToken) {
        try {
            String res = HttpUtil.get("http://147.79.20.183:9611/claude/getConversationCount?userToken=" + userToken);
            return new JSONObject(res);
        }catch (Exception e){
            return new JSONObject("{\"count\": 0}");
        }
    }

}
