/*
 *  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.causeway.commons.internal.reflection;

import org.junit.jupiter.api.Test;

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

import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;

import org.apache.causeway.commons.collections.Can;

import lombok.val;

class GenericReflectionMagicTest {

    @Test
    void resolveGenericReturnTypeAndParameterType() {
        val declaredMethodsMatching = Can.ofStream(_Reflect.streamAllMethods(_GenericAbstractImpl.class, true))
                .filter(m->m.getName().equals("sampleAction4"));
        //debug
        //declaredMethodsMatching.forEach(m->System.err.printf("%s (%s)%n", _Util.methodSummary(m), m));

        assertEquals(1, declaredMethodsMatching.size());

        val sampleAction = declaredMethodsMatching.getFirstElseFail();

        val returnType = GenericTypeResolver.resolveReturnType(sampleAction, _GenericAbstractImpl.class);
        assertEquals(String.class, returnType);

        @SuppressWarnings("deprecation") // proposed alternative is not publicly visible
        val param0Type = GenericTypeResolver.resolveParameterType(new MethodParameter(sampleAction, 0), _GenericAbstractImpl.class);
        assertEquals(String.class, param0Type);
    }

    @Test
    void mostSpecificMethodFind() {
        val declaredMethodsMatching = Can.ofStream(_Reflect.streamAllMethods(_GenericAbstractImpl.class, true))
                .filter(m->m.getName().equals("sampleAction2"));
        assertEquals(3, declaredMethodsMatching.size());
        //debug
        //declaredMethodsMatching.forEach(m->System.err.printf("+ %s bridge->%b%n", m, m.isBridge()));

        val mostSpecific = _ClassCache.getInstance().findMethodUniquelyByNameOrFail(_GenericAbstractImpl.class, "sampleAction2");
        assertEquals(String.class, mostSpecific.paramType(0));
        assertEquals(String.class, mostSpecific.returnType());
    }

    @Test
    void detectMethodOverride() {

        val declaredMethodsMatching = _ClassCache.getInstance().streamResolvedMethods(_GenericAbstractImpl.class)
                .filter(m->m.name().equals("sampleAction2"))
                .collect(Can.toCan());
        //debug
        //declaredMethodsMatching.forEach(m->System.err.printf("+ %s%n", m.method()));

        assertEquals(1, declaredMethodsMatching.size());

        val mostSpecific = declaredMethodsMatching.getFirstElseFail();
        assertEquals(String.class, mostSpecific.paramType(0));
        assertEquals(String.class, mostSpecific.returnType());
    }

}
