package com.bkjk.platform.openfeign.decoder;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.springframework.util.Assert;

import com.fasterxml.jackson.databind.ObjectMapper;

import feign.Response;
import feign.Util;
import feign.codec.Decoder;

public class ApiResultDecoder implements Decoder {
    final ObjectMapper mapper;

    final List<ApiResultHandler> apiResultHandlers;

    final Decoder delegate;

    public ApiResultDecoder(Decoder delegate, List<ApiResultHandler> apiResultHandlers) {
        Assert.notEmpty(apiResultHandlers, "List<ApiResultHandler> must not be empty");
        Objects.requireNonNull(delegate, "Decoder must not be null. ");
        this.delegate = delegate;
        mapper = new ObjectMapper();
        this.apiResultHandlers = apiResultHandlers;
    }

    @Override
    public Object decode(Response response, Type type) throws IOException {
        for (ApiResultHandler apiResultHandler : apiResultHandlers) {
            if (apiResultHandler.support(response, type)) {
                Object obj = apiResultHandler.decode(response, type);
                if (null != obj) {
                    return obj;
                }
            }
        }
        if (!isOptional(type)) {
            return delegate.decode(response, type);
        }
        if (response.status() == 404 || response.status() == 204) {
            return Optional.empty();
        }
        Type enclosedType = Util.resolveLastTypeParameter(type, Optional.class);
        return Optional.of(delegate.decode(response, enclosedType));
    }

    static boolean isOptional(Type type) {
        if (!(type instanceof ParameterizedType)) {
            return false;
        }
        ParameterizedType parameterizedType = (ParameterizedType)type;
        return parameterizedType.getRawType().equals(Optional.class);
    }

    private Optional<String> getFirstHeader(Response response, String key) {
        return response.headers().getOrDefault(key, new ArrayList<>()).stream().findFirst();
    }

    private String getStringFromMap(Map map, String key) {
        Object value = map.get(key);
        return null == value ? "" : value.toString();
    }
}