package com.alibaba.fastjson2.annotation;

import com.alibaba.fastjson2.util.BeanUtils;
import org.junit.jupiter.api.Test;

import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.util.*;

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

/**
 * Test for {@link JSONCreator}
 *
 * @author lzhpo
 */
class JSONCreatorCombinationTest {
    @Test
    void testDirectlyGetFromClass() {
        Class<DirectlyJSONCreatorPojo> clazz = DirectlyJSONCreatorPojo.class;
        Constructor<?>[] constructors = clazz.getConstructors();

        assertExistJSONCreator(constructors);
    }

    @Test
    void testDirectlyGetFromAnnotationType() {
        Class<DirectlyJSONCreatorPojo> clazz = DirectlyJSONCreatorPojo.class;
        Constructor<?>[] constructors = clazz.getConstructors();

        List<Annotation> annotations = new ArrayList<>();
        for (Constructor<?> constructor : constructors) {
            annotations.addAll(Arrays.asList(constructor.getAnnotations()));
        }

        assertExistJSONCreator(annotations);
    }

    @Test
    void testCombinationGetFromAnnotationType() {
        Class<CombinationJSONCreatorPojo> clazz = CombinationJSONCreatorPojo.class;
        Constructor<?>[] constructors = clazz.getConstructors();

        List<Annotation> annotations = new ArrayList<>();
        for (Constructor<?> constructor : constructors) {
            annotations.addAll(Arrays.asList(constructor.getAnnotations()));
        }

        assertExistJSONCreator(annotations);
    }

    private static void assertExistJSONCreator(Constructor<?>[] constructors) {
        Optional<JSONCreator> jsonCreator = Arrays.stream(constructors)
                .map(constructor -> BeanUtils.findAnnotation(constructor, JSONCreator.class))
                .filter(Objects::nonNull)
                .findAny();

        assertTrue(jsonCreator.isPresent());
    }

    private static void assertExistJSONCreator(List<Annotation> annotations) {
        Optional<JSONCreator> jsonCreator = annotations.stream()
                .map(annotation -> {
                    Class<? extends Annotation> annotationType = annotation.annotationType();
                    if (annotationType != JSONCreator.class) {
                        return BeanUtils.findAnnotation(annotationType, JSONCreator.class);
                    } else {
                        return (JSONCreator) annotation;
                    }
                })
                .filter(Objects::nonNull)
                .findAny();

        assertTrue(jsonCreator.isPresent());
    }

    public static class DirectlyJSONCreatorPojo {
        private String nickName;
        private int age;

        @JSONCreator(parameterNames = {"nickName", "age"})
        public DirectlyJSONCreatorPojo(String nickName, int age) {
            this.nickName = nickName;
            this.age = age;
        }
    }

    public static class CombinationJSONCreatorPojo {
        private String nickName;
        private int age;

        @JSONCreatorCombination
        public CombinationJSONCreatorPojo(String nickName, int age) {
            this.nickName = nickName;
            this.age = age;
        }
    }

    @JSONCreator(parameterNames = {"nickName", "age"})
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE})
    public @interface JSONCreatorCombination {
    }
}
