/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.metadata.rest.jaxrs;

import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader;
import org.apache.dubbo.metadata.rest.DefaultRestService;
import org.apache.dubbo.metadata.rest.PathUtil;
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
import org.apache.dubbo.metadata.rest.RestService;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.metadata.rest.StandardRestService;
import org.apache.dubbo.metadata.rest.api.AnotherUserRestService;
import org.apache.dubbo.metadata.rest.api.JaxrsRestService;
import org.apache.dubbo.metadata.rest.api.JaxrsRestServiceImpl;
import org.apache.dubbo.metadata.rest.api.SpringRestService;
import org.apache.dubbo.rpc.model.ApplicationModel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * {@link JAXRSServiceRestMetadataResolver} Test
 *
 * @since 2.7.6
 */
class JAXRSServiceRestMetadataResolverTest {

    private JAXRSServiceRestMetadataResolver instance =
            new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel());

    @Test
    void testSupports() {
        // JAX-RS RestService class
        assertTrue(instance.supports(StandardRestService.class));
        // Spring MVC RestService class
        assertFalse(instance.supports(SpringRestService.class));
        // Default RestService class
        assertFalse(instance.supports(DefaultRestService.class));
        // No annotated RestService class
        assertFalse(instance.supports(RestService.class));
        // null
        assertFalse(instance.supports(null));
    }

    @Test
    @Disabled
    void testResolve() {
        // Generated by "dubbo-metadata-processor"
        ClassPathServiceRestMetadataReader reader =
                new ClassPathServiceRestMetadataReader("META-INF/dubbo/jax-rs-service-rest-metadata.json");
        List<ServiceRestMetadata> serviceRestMetadataList = reader.read();

        ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0);
        ServiceRestMetadata serviceRestMetadata = instance.resolve(StandardRestService.class);

        assertTrue(CollectionUtils.equals(expectedServiceRestMetadata.getMeta(), serviceRestMetadata.getMeta()));

        assertEquals(expectedServiceRestMetadata, serviceRestMetadata);
    }

    @Test
    void testResolves() {
        testResolve(JaxrsRestService.class);
        testResolve(JaxrsRestServiceImpl.class);
    }

    void testResolve(Class service) {
        // Generated by "dubbo-metadata-processor"

        List<String> jsons = Arrays.asList(
                "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnno\",\"produces\":[\"text/plain\"]}}",
                "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.PathParam\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
                "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":true,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"map\",\"paramType\":\"javax.ws.rs.core.MultivaluedMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
                "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}",
                "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.QueryParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}",
                "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.HeaderParam\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}");

        ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata();
        jaxrsRestMetadata.setServiceInterface(service.getName());
        ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata);

        List<String> jsonsTmp = new ArrayList<>();
        for (RestMethodMetadata restMethodMetadata : jaxrsMetadata.getMeta()) {
            restMethodMetadata.setReflectMethod(null);
            restMethodMetadata.setMethod(null);
            jsonsTmp.add(JsonUtils.toJson(restMethodMetadata));
        }

        Comparator<String> comparator = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        };
        jsons.sort(comparator);
        jsonsTmp.sort(comparator);

        for (int i = 0; i < jsons.size(); i++) {
            assertEquals(jsons.get(i), jsonsTmp.get(i));
        }
    }

    @Test
    void testJaxrsPathPattern() {
        Class service = AnotherUserRestService.class;
        ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata();
        jaxrsRestMetadata.setServiceInterface(service.getName());
        ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata);

        RestMethodMetadata[] objects = jaxrsMetadata.getMeta().toArray(new RestMethodMetadata[0]);
        RestMethodMetadata object = null;
        for (RestMethodMetadata obj : objects) {
            if ("getUser".equals(obj.getReflectMethod().getName())) {
                object = obj;
            }
        }

        Assertions.assertEquals(
                "/u/1", PathUtil.resolvePathVariable("/u/{id : \\d+}", object.getArgInfos(), Arrays.asList(1)));
    }
}
