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.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 ResponseFieldsRetriever implements ObjectFieldsRetriever {
    private final ObjectMapper objectMapper;

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

    @Override
    public List<FieldRepresentation> getFields(FullTypeInfo type) {
        List<FieldRepresentation> result = new ArrayList<>();
        BeanDescription beanDescription = objectMapper.getSerializationConfig().introspect(objectMapper.constructType(type.getClazz()));
        for (BeanPropertyDefinition property : beanDescription.findProperties()) {
            if (couldSerialize(property)) {
                Description descAnnotation = property.getPrimaryMember().getAnnotation(Description.class);
                String description = descAnnotation == null ? null : descAnnotation.value();
                // По поводу nullable - по дефолту у нас все nullable, смотрится стремно.
                result.add(new FieldRepresentation(property.getName(), getType(type, property.getAccessor()), false,
                        description, property.getAccessor().getMember(), property.getAccessor()));
            }
        }
        return result;
    }

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

    private boolean couldSerialize(BeanPropertyDefinition property) {
        AnnotatedMember accessor = property.getAccessor();
        return accessor != null && Modifier.isPublic(accessor.getMember().getModifiers());
    }
}
