package tech.easyflow.common.cache;

import com.alicp.jetcache.anno.SerialPolicy;
import com.alicp.jetcache.support.CacheEncodeException;
import com.alicp.jetcache.support.JavaValueDecoder;
import org.springframework.core.ConfigurableObjectInputStream;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Objects;

/**
 * 使用应用类加载器反序列化 JetCache Java 缓存值。
 *
 * <p>异步线程的上下文类加载器可能无法访问 Spring Boot 可执行包中的嵌套依赖，
 * 因此解码时固定使用本类的定义类加载器。</p>
 */
public class ApplicationClassLoaderJavaValueDecoder extends JavaValueDecoder {

    private final ClassLoader applicationClassLoader;

    /**
     * 创建使用 EasyFlow 应用类加载器的 Java 缓存解码器。
     */
    public ApplicationClassLoaderJavaValueDecoder() {
        this(ApplicationClassLoaderJavaValueDecoder.class.getClassLoader());
    }

    /**
     * 创建使用指定类加载器的 Java 缓存解码器。
     *
     * @param applicationClassLoader 反序列化缓存对象时使用的类加载器
     * @throws NullPointerException 类加载器为空时抛出
     */
    ApplicationClassLoaderJavaValueDecoder(ClassLoader applicationClassLoader) {
        super(true);
        this.applicationClassLoader = Objects.requireNonNull(
            applicationClassLoader,
            "applicationClassLoader must not be null"
        );
    }

    /**
     * 解码带 JetCache Java 编码标识的缓存值。
     *
     * @param buffer Redis 中读取的缓存字节
     * @return 反序列化后的缓存对象
     * @throws CacheEncodeException 缓存内容为空、编码类型不匹配或反序列化失败时抛出
     */
    @Override
    public Object apply(byte[] buffer) {
        try {
            if (buffer == null || buffer.length < Integer.BYTES) {
                throw new CacheEncodeException("decode error: invalid java cache payload");
            }
            int identityNumber = parseHeader(buffer);
            if (identityNumber != SerialPolicy.IDENTITY_NUMBER_JAVA) {
                throw new CacheEncodeException(
                    "decode error: unsupported cache identity number " + identityNumber
                );
            }
            return doApply(buffer);
        } catch (CacheEncodeException e) {
            throw e;
        } catch (Throwable e) {
            throw new CacheEncodeException("decode error", e);
        }
    }

    /**
     * 创建绑定应用类加载器的对象输入流。
     *
     * @param input 缓存对象字节输入流
     * @return 可从应用依赖中解析类的对象输入流
     * @throws IOException 对象输入流初始化失败时抛出
     */
    @Override
    protected ObjectInputStream buildObjectInputStream(ByteArrayInputStream input) throws IOException {
        return new ConfigurableObjectInputStream(input, applicationClassLoader);
    }
}
