package org.jboss.resteasy.test.injection;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Response;

import org.hibernate.validator.HibernateValidatorPermission;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.resteasy.api.validation.Validation;
import org.jboss.resteasy.api.validation.ViolationReport;
import org.jboss.resteasy.test.injection.resource.PostConstructInjectionEJBInterceptor;
import org.jboss.resteasy.test.injection.resource.PostConstructInjectionEJBResource;
import org.jboss.resteasy.test.injection.resource.PostConstructInjectionResource;
import org.jboss.resteasy.utils.PermissionUtil;
import org.jboss.resteasy.utils.PortProviderUtil;
import org.jboss.resteasy.utils.TestUtil;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * @tpSubChapter Validation and @PostConstruct methods
 * @tpChapter Integration tests
 * @tpTestCaseDetails Regression tests for RESTEASY-2227
 * @tpSince RESTEasy 3.6
 */
@RunWith(Arquillian.class)
@RunAsClient
public class PostConstructInjectionTest {

    static Client client;

    // deployment names
    private static final String WAR_CDI_ON = "war_with_cdi_on";
    private static final String WAR_CDI_OFF = "war_with_cdi_off";

    /**
     * Deployment with CDI activated
     */
    @Deployment(name = WAR_CDI_ON)
    public static Archive<?> deployCdiOn() {
        WebArchive war = TestUtil.prepareArchive(PostConstructInjectionTest.class.getSimpleName() + "_CDI_ON");
        war.addClass(PostConstructInjectionEJBInterceptor.class);
        war.addAsWebInfResource(PostConstructInjectionTest.class.getPackage(), "PostConstructInjection_beans_cdi_on.xml",
                "beans.xml");
        war.addAsManifestResource(PermissionUtil.createPermissionsXmlAsset(
                new HibernateValidatorPermission("accessPrivateMembers")), "permissions.xml");
        return TestUtil.finishContainerPrepare(war, null, PostConstructInjectionResource.class,
                PostConstructInjectionEJBResource.class);
    }

    /**
     * Deployment with CDI not activated
     */
    @Deployment(name = WAR_CDI_OFF)
    public static Archive<?> deployCdiOff() {
        WebArchive war = TestUtil.prepareArchive(PostConstructInjectionTest.class.getSimpleName() + "_CDI_OFF");
        war.addClass(PostConstructInjectionEJBInterceptor.class);
        war.addAsWebInfResource(PostConstructInjectionTest.class.getPackage(), "PostConstructInjection_beans_cdi_off.xml",
                "beans.xml");
        war.addAsManifestResource(PermissionUtil.createPermissionsXmlAsset(
                new HibernateValidatorPermission("accessPrivateMembers")), "permissions.xml");
        return TestUtil.finishContainerPrepare(war, null, PostConstructInjectionResource.class);
    }

    @BeforeClass
    public static void init() {
        client = ClientBuilder.newClient();
    }

    @AfterClass
    public static void after() {
        client.close();
    }

    private String generateURL(String jar, String path) {
        return PortProviderUtil.generateURL(path, PostConstructInjectionTest.class.getSimpleName() + "_CDI_" + jar);
    }

    /**
     * @tpTestDetails In an environment with managed beans, a @PostConstruct method on either an ordinary
     *                resource or an EJB interceptor should execute before class and property validation is done.
     * @tpSince RESTEasy 3.7.0
     */
    @Test
    public void TestPostInjectCdiOn() throws Exception {
        doTestPostInjectCdiOn("ON", "/normal");
    }

    /**
     * @tpTestDetails In an environment with managed beans, a @PostConstruct method on either an ordinary
     *                resource or an EJB interceptor should execute before class and property validation is done.
     * @tpSince RESTEasy 3.7.0
     */
    @Test
    @Ignore("This test doesn't work yet. See RESTEASY-2264")
    public void TestPostInjectCdiOnEJB() throws Exception {
        doTestPostInjectCdiOn("ON", "/ejb");
    }

    /**
     * @tpTestDetails In an environment without managed beans, a @PostConstruct method on a resource will not be called.
     * @tpSince RESTEasy 3.7.0
     */
    @Test
    public void TestPostInjectCdiOff() throws Exception {
        Response response = client.target(generateURL("OFF", "/normal/get")).request().get();
        Assert.assertEquals(200, response.getStatus());
        Assert.assertEquals("ab", response.readEntity(String.class));
        response.close();
    }

    void doTestPostInjectCdiOn(String cdi, String resource) {
        Response response = client.target(generateURL(cdi, resource + "/get")).request().get();
        Assert.assertEquals(400, response.getStatus());
        String header = response.getHeaderString(Validation.VALIDATION_HEADER);
        Assert.assertNotNull(header);
        ViolationReport report = response.readEntity(ViolationReport.class);
        Assert.assertEquals(1, report.getPropertyViolations().size());
        response.close();
    }
}
