package com.nanri.aiimage.modules.ziniao.memory.service;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanri.aiimage.common.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 进程内缓存：紫鸟 token/员工/店铺列表等短期数据。
 * 不写入 biz_ziniao_memory_store，避免与「店铺索引落库」混用同一张表。
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ZiniaoTransientCacheService {

    private static final String SEP = "::";

    private final ObjectMapper objectMapper;

    private final ConcurrentHashMap<String, Holder> map = new ConcurrentHashMap<>();

    public <T> Optional<T> get(String cacheType, String cacheKey, Class<T> valueType) {
        Holder holder = getHolder(cacheType, cacheKey);
        if (holder == null) {
            return Optional.empty();
        }
        try {
            return Optional.ofNullable(objectMapper.readValue(holder.json, valueType));
        } catch (Exception ex) {
            throw new BusinessException("读取紫鸟进程缓存失败");
        }
    }

    public <T> Optional<T> get(String cacheType, String cacheKey, JavaType javaType) {
        Holder holder = getHolder(cacheType, cacheKey);
        if (holder == null) {
            return Optional.empty();
        }
        try {
            @SuppressWarnings("unchecked")
            T value = (T) objectMapper.readValue(holder.json, javaType);
            return Optional.ofNullable(value);
        } catch (Exception ex) {
            throw new BusinessException("读取紫鸟进程缓存失败");
        }
    }

    public <E> Optional<List<E>> getList(String cacheType, String cacheKey, Class<E> elementType) {
        JavaType type = objectMapper.getTypeFactory().constructCollectionType(List.class, elementType);
        return get(cacheType, cacheKey, type);
    }

    public void put(String cacheType, String cacheKey, Object payload, Duration ttl) {
        String normalizedType = normalizeRequired(cacheType, "cacheType 不能为空");
        String normalizedKey = normalizeRequired(cacheKey, "cacheKey 不能为空");
        if (ttl == null || ttl.isZero() || ttl.isNegative()) {
            throw new BusinessException("ttl 不合法");
        }
        try {
            String json = objectMapper.writeValueAsString(payload);
            LocalDateTime expiresAt = LocalDateTime.now().plusSeconds(ttl.getSeconds());
            map.put(compoundKey(normalizedType, normalizedKey), new Holder(json, expiresAt));
            log.trace("[ziniao-transient] put type={} keyLen={} ttlSec={}", normalizedType, normalizedKey.length(), ttl.getSeconds());
        } catch (Exception ex) {
            throw new BusinessException("写入紫鸟进程缓存失败");
        }
    }

    public void delete(String cacheType, String cacheKey) {
        String normalizedType = normalizeRequired(cacheType, "cacheType 不能为空");
        String normalizedKey = normalizeRequired(cacheKey, "cacheKey 不能为空");
        map.remove(compoundKey(normalizedType, normalizedKey));
        log.trace("[ziniao-transient] delete type={}", normalizedType);
    }

    private Holder getHolder(String cacheType, String cacheKey) {
        String normalizedType = normalizeRequired(cacheType, "cacheType 不能为空");
        String normalizedKey = normalizeRequired(cacheKey, "cacheKey 不能为空");
        Holder holder = map.get(compoundKey(normalizedType, normalizedKey));
        if (holder == null) {
            return null;
        }
        if (holder.expiresAt == null || !holder.expiresAt.isAfter(LocalDateTime.now())) {
            map.remove(compoundKey(normalizedType, normalizedKey));
            return null;
        }
        return holder;
    }

    private static String compoundKey(String cacheType, String cacheKey) {
        return cacheType + SEP + cacheKey;
    }

    private static String normalizeRequired(String value, String message) {
        String normalized = Objects.toString(value, "").trim();
        if (normalized.isEmpty()) {
            throw new BusinessException(message);
        }
        return normalized;
    }

    private record Holder(String json, LocalDateTime expiresAt) {
    }
}
