package ru.yandex.webmaster3.core.http.autodoc;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import ru.yandex.autodoc.common.doc.annotation.Description;

import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * @author avhaliullin
 */
public class RequestFieldsRetriever implements ObjectFieldsRetriever {
    private final ObjectMapper objectMapper;

    public RequestFieldsRetriever(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public List<FieldRepresentation> getFields(FullTypeInfo type) {
        List<FieldRepresentation> result = new ArrayList<>();
        BeanDescription beanDesc = objectMapper.getDeserializationConfig().introspect(objectMapper.constructType(type.getClazz()));
        for (BeanPropertyDefinition property : beanDesc.findProperties()) {
            if (couldDeserialize(property)) {
                Description descAnnotation = property.getPrimaryMember().getAnnotation(Description.class);
                String description = descAnnotation == null ? null : descAnnotation.value();
                result.add(new FieldRepresentation(property.getName(), getType(type, property.getMutator()),
                        property.isRequired(), description, property.getMutator().getMember(), property.getMutator()));
            }
        }
        return result;
    }

    private FullTypeInfo getType(FullTypeInfo ownerType, AnnotatedMember member) {
        Type result;
        if (member instanceof AnnotatedMethod) {
            result = ((AnnotatedMethod) member).getGenericParameterType(0);
        } else {
            result = member.getGenericType();
        }
        if (member.getDeclaringClass() == ownerType.getClazz()) {
            return ownerType.memberFullType(result);
        } else {
            return ownerType.ancestorFullType(member.getDeclaringClass()).memberFullType(result);
        }
    }

    private boolean couldDeserialize(BeanPropertyDefinition propertyDefinition) {
        AnnotatedMember mutator = propertyDefinition.getMutator();
        return mutator != null && Modifier.isPublic(mutator.getMember().getModifiers()) &&
                !Modifier.isFinal(mutator.getMember().getModifiers());
    }
}
