package com.alibaba.fastjson2.issues;

import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.TypeReference;
import com.alibaba.fastjson2.read.ClassLoaderTest;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;

public class Issue783 {
    @Test
    public void test() {
        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(Bean.class);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(Bean.class);
        assertSame(objectWriter, objectWriter1);
        writerProvider.cleanup(Bean.class);
        ObjectWriter objectWriter2 = writerProvider.getObjectWriter(Bean.class);
        assertNotSame(objectWriter, objectWriter2);
    }

    @Test
    public void test1() {
        ObjectReaderProvider readerProvider = JSONFactory.defaultObjectReaderProvider;
        ObjectReader objectReader = readerProvider.getObjectReader(Bean.class);
        ObjectReader objectReader1 = readerProvider.getObjectReader(Bean.class);
        assertSame(objectReader, objectReader1);

        readerProvider.cleanup(Bean.class);
        ObjectReader objectReader2 = readerProvider.getObjectReader(Bean.class);
        assertNotSame(objectReader, objectReader2);
    }

    public static class Bean {
    }

    @Test
    public void test2() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");

        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(objectClass);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(objectClass);
        assertSame(objectWriter, objectWriter1);

        writerProvider.cleanup(classLoader);

        ObjectWriter objectWriter2 = writerProvider.getObjectWriter(objectClass);
        assertNotSame(objectWriter, objectWriter2);
    }

    @Test
    public void testWriteMap() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type mapType = TypeReference.mapType(Map.class, String.class, objectClass);

        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(mapType);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(mapType);
        assertSame(objectWriter, objectWriter1);

        writerProvider.cleanup(classLoader);

        ObjectWriter objectWriter2 = writerProvider.getObjectWriter(mapType);
        assertNotSame(objectWriter, objectWriter2);
    }

    @Test
    public void testWriteMap1() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type mapType = TypeReference.mapType(Map.class, objectClass, String.class);

        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(mapType);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(mapType);
        assertSame(objectWriter, objectWriter1);

        writerProvider.cleanup(classLoader);

        ObjectWriter objectWriter2 = writerProvider.getObjectWriter(mapType);
        assertNotSame(objectWriter, objectWriter2);
    }

    @Test
    public void testWriteCollection() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type listType = TypeReference.collectionType(List.class, objectClass);

        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(listType);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(listType);
        assertSame(objectWriter, objectWriter1);

        objectWriter.write(JSONWriter.of(), Arrays.asList(objectClass.newInstance()), null, null, 0);
        writerProvider.cleanup(classLoader);

        ObjectWriter objectWriter2 = writerProvider.getObjectWriter(listType);
        assertSame(objectWriter, objectWriter2);
    }

    @Test
    public void testWriteParamType2() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type paramType = TypeReference.parametricType(Bean2.class, TypeReference.collectionType(List.class, objectClass));

        ObjectWriterProvider writerProvider = JSONFactory.defaultObjectWriterProvider;
        ObjectWriter objectWriter = writerProvider.getObjectWriter(paramType);
        ObjectWriter objectWriter1 = writerProvider.getObjectWriter(paramType);
        assertSame(objectWriter, objectWriter1);

        Bean2 bean = new Bean2();
        bean.value = Arrays.asList(objectClass.newInstance());
        objectWriter1.write(JSONWriter.of(), bean, null, null, 0);

        writerProvider.cleanup(classLoader);
    }

    @Test
    public void testClass() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");

        ObjectReaderProvider readerProvider = JSONFactory.defaultObjectReaderProvider;
        ObjectReader objectReader = readerProvider.getObjectReader(objectClass);
        ObjectReader objectReader1 = readerProvider.getObjectReader(objectClass);
        assertSame(objectReader, objectReader1);

        readerProvider.cleanup(classLoader);
        ObjectReader objectReader2 = readerProvider.getObjectReader(objectClass);
        assertNotSame(objectReader, objectReader2);
    }

    @Test
    public void test4() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type listType = TypeReference.collectionType(List.class, objectClass);

        ObjectReaderProvider readerProvider = JSONFactory.defaultObjectReaderProvider;
        ObjectReader objectReader = readerProvider.getObjectReader(listType);
        ObjectReader objectReader1 = readerProvider.getObjectReader(listType);
        assertSame(objectReader, objectReader1);

        readerProvider.cleanup(classLoader);
        ObjectReader objectReader2 = readerProvider.getObjectReader(listType);
        assertNotSame(objectReader, objectReader2);
    }

    @Test
    public void test5() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type listType = TypeReference.mapType(Map.class, String.class, objectClass);

        ObjectReaderProvider readerProvider = JSONFactory.defaultObjectReaderProvider;
        ObjectReader objectReader = readerProvider.getObjectReader(listType);
        ObjectReader objectReader1 = readerProvider.getObjectReader(listType);
        assertSame(objectReader, objectReader1);

        readerProvider.cleanup(classLoader);
        ObjectReader objectReader2 = readerProvider.getObjectReader(listType);
        assertNotSame(objectReader, objectReader2);
    }

    @Test
    public void test6() throws Exception {
        ClassLoaderTest.ExtClassLoader classLoader = new ClassLoaderTest.ExtClassLoader();
        Class objectClass = classLoader.loadClass("com.alibaba.mock.demo.api.Demo");
        Type listType = TypeReference.mapType(Map.class, objectClass, String.class);

        ObjectReaderProvider readerProvider = JSONFactory.defaultObjectReaderProvider;
        ObjectReader objectReader = readerProvider.getObjectReader(listType);
        ObjectReader objectReader1 = readerProvider.getObjectReader(listType);
        assertSame(objectReader, objectReader1);

        readerProvider.cleanup(classLoader);
        ObjectReader objectReader2 = readerProvider.getObjectReader(listType);
        assertNotSame(objectReader, objectReader2);
    }

    public static class ExtClassLoader
            extends ClassLoader {
        public ExtClassLoader() throws IOException {
            super(Thread.currentThread().getContextClassLoader());

            {
                byte[] bytes;
                InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("external/Demo.clazz");
                bytes = IOUtils.toByteArray(is);
                is.close();

                super.defineClass("com.alibaba.mock.demo.api.Demo", bytes, 0, bytes.length);
            }
            {
                byte[] bytes;
                InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("external/MockDemoService.clazz");
                bytes = IOUtils.toByteArray(is);
                is.close();

                super.defineClass("com.alibaba.mock.demo.service.MockDemoService", bytes, 0, bytes.length);
            }
        }
    }

    public static class Bean1<T> {
        public T value;
    }

    public static class Bean2<T> {
        public List<T> value;
    }
}
