package com.ruoyi.jarvis.service.impl;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.jarvis.domain.dto.WeComChatSession;
import com.ruoyi.jarvis.service.IWeComChatSessionService;
import com.ruoyi.jarvis.wecom.WeComConvention;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class WeComChatSessionServiceImpl implements IWeComChatSessionService {

    private static final Logger log = LoggerFactory.getLogger(WeComChatSessionServiceImpl.class);

    private static final DateTimeFormatter FMT = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${jarvis.wecom.session-ttl-minutes:30}")
    private long sessionTtlMinutes;

    @Value("${jarvis.wecom.session-idle-timeout-minutes:30}")
    private long idleTimeoutMinutes;

    @Override
    public WeComChatSession get(String wecomUserId) {
        if (!StringUtils.hasText(wecomUserId)) {
            return null;
        }
        String key = WeComConvention.sessionRedisKey(wecomUserId.trim());
        String json = stringRedisTemplate.opsForValue().get(key);
        if (!StringUtils.hasText(json)) {
            log.debug("企微会话 Redis 未命中 key={}", key);
            return null;
        }
        try {
            WeComChatSession s = JSON.parseObject(json, WeComChatSession.class);
            if (s == null || !StringUtils.hasText(s.getLastInteractionTime())) {
                log.info("企微会话 Redis 命中但缺字段 userId={} key={}", wecomUserId, WeComConvention.sessionRedisKey(wecomUserId.trim()));
                return null;
            }
            LocalDateTime last = LocalDateTime.parse(s.getLastInteractionTime(), FMT);
            if (ChronoUnit.MINUTES.between(last, LocalDateTime.now()) > idleTimeoutMinutes) {
                stringRedisTemplate.delete(WeComConvention.sessionRedisKey(wecomUserId.trim()));
                log.info("企微会话空闲超时已删 userId={} key={}", wecomUserId, WeComConvention.sessionRedisKey(wecomUserId.trim()));
                return null;
            }
            log.debug("企微会话读取成功 userId={} scene={} step={}", wecomUserId, s.getScene(), s.getStep());
            return s;
        } catch (Exception e) {
            log.warn("解析企微会话失败 userId={} key={} jsonSnippet={}", wecomUserId, WeComConvention.sessionRedisKey(wecomUserId.trim()),
                    json != null && json.length() > 120 ? json.substring(0, 120) + "..." : json, e);
            return null;
        }
    }

    @Override
    public void put(String wecomUserId, WeComChatSession session) {
        if (!StringUtils.hasText(wecomUserId) || session == null) {
            return;
        }
        session.touch();
        String key = WeComConvention.sessionRedisKey(wecomUserId.trim());
        String payload = JSON.toJSONString(session);
        stringRedisTemplate.opsForValue().set(key, payload, sessionTtlMinutes, TimeUnit.MINUTES);
        log.info("企微会话已写入 Redis key={} ttlMin={} bytes={}", key, sessionTtlMinutes, payload.length());
    }

    @Override
    public void delete(String wecomUserId) {
        if (!StringUtils.hasText(wecomUserId)) {
            return;
        }
        stringRedisTemplate.delete(WeComConvention.sessionRedisKey(wecomUserId.trim()));
    }

    @Override
    public int deleteAllWecomSessionsForTest() {
        Set<String> keys = stringRedisTemplate.keys(WeComConvention.SESSION_REDIS_KEY_PREFIX + "*");
        if (keys == null || keys.isEmpty()) {
            return 0;
        }
        int n = 0;
        for (String key : keys) {
            if (key != null && stringRedisTemplate.delete(key)) {
                n++;
            }
        }
        log.info("企微会话测试清理完成 prefix={} deletedKeys={}", WeComConvention.SESSION_REDIS_KEY_PREFIX, n);
        return n;
    }

    /**
     * 参考 JDUtil#cleanUpTimeoutStates：按 lastInteractionTime 清理超长空闲会话（Redis TTL 与业务空闲均可生效）
     */
    @Scheduled(fixedDelayString = "${jarvis.wecom.session-sweep-ms:60000}")
    public void sweepIdleSessions() {
        try {
            Set<String> keys = stringRedisTemplate.keys(WeComConvention.SESSION_REDIS_KEY_PREFIX + "*");
            if (keys == null || keys.isEmpty()) {
                return;
            }
            LocalDateTime now = LocalDateTime.now();
            for (String key : keys) {
                String json = stringRedisTemplate.opsForValue().get(key);
                if (!StringUtils.hasText(json)) {
                    continue;
                }
                try {
                    WeComChatSession s = JSON.parseObject(json, WeComChatSession.class);
                    if (s == null || !StringUtils.hasText(s.getLastInteractionTime())) {
                        stringRedisTemplate.delete(key);
                        continue;
                    }
                    LocalDateTime last = LocalDateTime.parse(s.getLastInteractionTime(), FMT);
                    if (ChronoUnit.MINUTES.between(last, now) > idleTimeoutMinutes) {
                        stringRedisTemplate.delete(key);
                        log.debug("企微会话超时删除 key={}", key);
                    }
                } catch (Exception e) {
                    log.debug("企微会话扫描跳过 key={} {}", key, e.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("企微会话扫描异常 {}", e.getMessage());
        }
    }
}
