/*
 * Licensed 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.jdbi.v3.core.generic;

import java.lang.reflect.Type;
import java.util.Optional;
import org.junit.Test;

import static java.util.Optional.empty;
import static org.assertj.core.api.Assertions.assertThat;

public class GenericTypesTest {
    @Test
    public void getErasedTypeOfRaw() throws NoSuchMethodException {
        assertThat(GenericTypes.getErasedType(methodReturnType(Foo.class, "raw"))).isEqualTo(Foo.class);
    }

    @Test
    public void findGenericParameterOfRaw() throws NoSuchMethodException {
        assertThat(GenericTypes.findGenericParameter(methodReturnType(Foo.class, "raw"), Foo.class)).isEqualTo(empty());
    }

    @Test
    public void getErasedTypeOfGeneric() throws NoSuchMethodException {
        assertThat(GenericTypes.getErasedType(methodReturnType(Foo.class, "generic"))).isEqualTo(Foo.class);
    }

    @Test
    public void findGenericParameterOfGeneric() throws NoSuchMethodException {
        assertThat(GenericTypes.findGenericParameter(methodReturnType(Foo.class, "generic"), Foo.class))
                .contains(String.class);
    }

    @Test
    public void getErasedTypeOfNestedGeneric() throws NoSuchMethodException {
        assertThat(GenericTypes.getErasedType(methodReturnType(Foo.class, "nestedGeneric"))).isEqualTo(Foo.class);
    }

    @Test
    public void findGenericParameterOfNestedGeneric() throws NoSuchMethodException {
        assertThat(GenericTypes.findGenericParameter(methodReturnType(Foo.class, "nestedGeneric"), Foo.class))
                .contains(methodReturnType(Foo.class, "generic"));
    }

    @Test
    public void findGenericParameterOfSuperClass() throws NoSuchMethodException {
        assertThat(GenericTypes.findGenericParameter(methodReturnType(Bar.class, "subTypeGeneric"), Foo.class))
                .isEqualTo(Optional.of(Integer.class));
    }

    @Test
    public void findGenericParameterOfAncestorClass() throws NoSuchMethodException {
        assertThat(GenericTypes.findGenericParameter(methodReturnType(Baz.class, "descendentTypeGeneric"), Foo.class))
                .contains(String.class);
    }

    @Test
    public void resolveType() throws Exception {
        abstract class A<T> {
            abstract T a();
        }

        abstract class B extends A<String> {}

        assertThat(GenericTypes.resolveType(A.class.getDeclaredMethod("a").getGenericReturnType(), B.class))
                .isEqualTo(String.class);
    }

    @Test
    public void resolveTypeUnrelatedContext() throws NoSuchMethodException {
        abstract class A1<T> {
            abstract T a();
        }
        abstract class A2<T> {
            abstract T a();
        }
        abstract class B extends A2<String> {}

        Type t = A1.class.getDeclaredMethod("a").getGenericReturnType();
        assertThat(GenericTypes.resolveType(t, B.class)).isEqualTo(t);
    }

    private static Type methodReturnType(Class<?> clazz, String methodName) throws NoSuchMethodException {
        return clazz.getDeclaredMethod(methodName).getGenericReturnType();
    }

    private static class Foo<T> {
        private static Foo raw() {
            return null;
        }

        private static Foo<String> generic() {
            return null;
        }

        private static Foo<Foo<String>> nestedGeneric() {
            return null;
        }
    }

    private static class Bar<T> extends Foo<T> {
        private static Bar<Integer> subTypeGeneric() {
            return null;
        }
    }

    private static class Baz<T> extends Bar<T> {
        private static Baz<String> descendentTypeGeneric() {
            return null;
        }
    }
}

@@ -2653,32 +2653,559 @@
 .class);%0A    %7D%0A%0A
+    @Test%0A    public void findMultipleGenericParameters() throws NoSuchMethodException %7B%0A        assertThat(GenericTypes.findGenericParameter(methodReturnType(Xyz.class, %22sample%22), Xyz.class, 0))%0A            .contains(String.class);%0A        assertThat(GenericTypes.findGenericParameter(methodReturnType(Xyz.class, %22sample%22), Xyz.class, 1))%0A            .contains(Integer.class);%0A        assertThat(GenericTypes.findGenericParameter(methodReturnType(Xyz.class, %22sample%22), Xyz.class, 2))%0A            .contains(Void.class);%0A    %7D%0A%0A
     @Test%0A    pu
@@ -3235,16 +3235,28 @@
  throws 
+NoSuchMethod
 Exceptio
@@ -4730,11 +4730,154 @@
  %7D%0A    %7D
+%0A%0A    private static class Xyz%3CX, Y, Z%3E %7B%0A        private static Xyz%3CString, Integer, Void%3E sample() %7B%0A            return null;%0A        %7D%0A    %7D
 %0A%7D%0A

add test for >0 param index