package org.jboss.resteasy.test.providers;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.ext.MessageBodyWriter;
import jakarta.ws.rs.ext.Providers;

import org.jboss.logging.Logger;
import org.jboss.resteasy.client.jaxrs.internal.ClientConfiguration;
import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.jboss.resteasy.resteasy_jaxrs.i18n.LogMessages;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.test.providers.resource.ProviderFactoryPrecedenceIntegerPlainTextWriter;
import org.jboss.resteasy.test.providers.resource.ProviderFactoryPrecendencePlainTextWriter;
import org.jboss.resteasy.util.DelegatingOutputStream;
import org.junit.jupiter.api.Test;

/**
 * @tpSubChapter Providers
 * @tpChapter Unit tests
 * @tpTestCaseDetails Multipart Contains JSON part should not trigger Stream Closed
 * @tpSince RESTEasy 3.0.14
 */
public class MultipurtContainsJsonTest {
    private static final LogMessages logger = Logger.getMessageLogger(MethodHandles.lookup(), LogMessages.class,
            MultipurtContainsJsonTest.class.getName());
    private static final Annotation[] EMPTY_ANNOTATION = new Annotation[0];

    public static MultipartFormDataOutput getMultipartWithoutJSON() {
        MultipartFormDataOutput dataOutput = new MultipartFormDataOutput();

        dataOutput.addFormData("str-field", "Hello World", MediaType.TEXT_PLAIN_TYPE);
        dataOutput.addFormData("bytes-field", "text file".getBytes(StandardCharsets.UTF_8),
                MediaType.APPLICATION_OCTET_STREAM_TYPE);
        return dataOutput;
    }

    public static MessageBodyWriter<MultipartFormDataOutput> getWriter() {
        ResteasyProviderFactory factory = ResteasyProviderFactory.newInstance();
        RegisterBuiltin.register(factory);
        factory.registerProviderInstance(new ProviderFactoryPrecendencePlainTextWriter());
        factory.registerProviderInstance(new ProviderFactoryPrecedenceIntegerPlainTextWriter());
        ResteasyContext.pushContext(Providers.class, factory);

        MultipartFormDataOutput data = getMultipartWithoutJSON();
        ClientConfiguration config = new ClientConfiguration(factory);
        return config.getMessageBodyWriter(MultipartFormDataOutput.class, data.getClass(), EMPTY_ANNOTATION,
                MediaType.MULTIPART_FORM_DATA_TYPE);
    }

    @Test
    public void testMultipartWithoutJSON() throws IOException {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MultipartFormDataOutput data = getMultipartWithoutJSON();
        MessageBodyWriter<MultipartFormDataOutput> writer = getWriter();

        DelegatingOutputStream delegatingOutputStream = new DelegatingOutputStream(outputStream) {
            @Override
            public void close() throws IOException {
                throw new IOException("stream closed");
                //super.close();
            }
        };
        MultivaluedMapImpl<String, Object> headers = new MultivaluedMapImpl<String, Object>();
        writer.writeTo(data, data.getClass(), data.getClass(), EMPTY_ANNOTATION, MediaType.MULTIPART_FORM_DATA_TYPE, headers,
                delegatingOutputStream);
        byte[] buf = outputStream.toByteArray();
        logger.info(new String(buf, StandardCharsets.UTF_8));
    }

    @Test
    public void testMultipartContainsJSON() throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MultipartFormDataOutput data = getMultipartWithoutJSON();
        MessageBodyWriter<MultipartFormDataOutput> writer = getWriter();

        Map<String, Object> jsonMap = new HashMap<String, Object>();
        jsonMap.put("key1", "value1");
        jsonMap.put("key2", "value2");
        jsonMap.put("key3", 3);
        data.addFormData("json-field", jsonMap, MediaType.APPLICATION_JSON_TYPE);
        data.addFormData("str-field-1", "str-field-1", MediaType.TEXT_PLAIN_TYPE);

        DelegatingOutputStream delegatingOutputStream = new DelegatingOutputStream(outputStream) {
            @Override
            public void close() throws IOException {
                throw new IOException("Stream closed wrongly!!!");
            }
        };
        MultivaluedMapImpl<String, Object> headers = new MultivaluedMapImpl<String, Object>();
        writer.writeTo(data, data.getClass(), data.getClass(), new Annotation[0], MediaType.MULTIPART_FORM_DATA_TYPE, headers,
                delegatingOutputStream);
        byte[] buf = outputStream.toByteArray();
        logger.info(new String(buf, StandardCharsets.UTF_8));
    }
}
