package cz.moneta.test.harness.endpoints.openapi;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import cz.moneta.test.harness.connectors.rest.ExtendedRestConnector;
import cz.moneta.test.harness.connectors.rest.ExtendedRestResponse;
import cz.moneta.test.harness.connectors.rest.RestConnector;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.endpoints.Endpoint;
import cz.moneta.test.harness.endpoints.RestEndpoint;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.GenericType;
import org.apache.commons.lang3.tuple.Pair;

import java.util.Map;

public class OpenApiBaseEndpoint implements RestEndpoint, Endpoint {
    private static final ObjectMapper JACKSON = new ObjectMapper()
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
            .registerModules(new ParameterNamesModule(), new Jdk8Module(), new JavaTimeModule());

    protected RestConnector restConnector;
    protected StoreAccessor store;

    public OpenApiBaseEndpoint(StoreAccessor store, String key) {
        this.store = store;
        String url = store.getConfig(key);
        if (url == null) {
            throw new IllegalStateException("You need to configure " + key + " url to work with OpenAPI");
        }
        this.restConnector = new ExtendedRestConnector(url, "OpenApiRestLogger");
    }

    //TODO - make jackson pick up the correct generic type rather than a linked hash map
    @Override
    public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
        Pair<Integer, JsonNode> cblRestResponse = restConnector.get(path, properties, new GenericType<JsonNode>() { }, headers);
        JsonNode jsonNode = cblRestResponse.getRight();
        try {
            return Pair.of(cblRestResponse.getLeft(), JACKSON.convertValue(jsonNode.toString(), responseType));
        } catch (IllegalArgumentException possiblyErrorResponse) {
            throw new AssertionError(String.format("HTTP GET response looks different than expected: %s", cblRestResponse.getRight()), possiblyErrorResponse);
        }
    }

    //TODO - make jackson pick up the correct generic type rather than a linked hash map
    @Override
    public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
        Pair<Integer, JsonNode> cblRestResponse = restConnector.post(path, request, new GenericType<JsonNode>() { }, headers);
        try {
            JsonNode jsonNode = cblRestResponse.getRight();
            return Pair.of(cblRestResponse.getLeft(), JACKSON.convertValue(jsonNode.toString(), responseType));
        } catch (IllegalArgumentException possiblyErrorResponse) {
            throw new AssertionError(String.format("HTTP POST response looks different than expected: %s", cblRestResponse.getRight()), possiblyErrorResponse);
        }
    }

    //TODO - make jackson pick up the correct generic type rather than a linked hash map
    @Override
    public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
        Pair<Integer, JsonNode> cblRestResponse = restConnector.delete(path, properties, new GenericType<JsonNode>() { }, headers);
        JsonNode jsonNode = cblRestResponse.getRight();
        try {
            return Pair.of(cblRestResponse.getLeft(), JACKSON.convertValue(jsonNode.toString(), responseType));
        } catch (IllegalArgumentException possiblyErrorResponse) {
            throw new AssertionError(String.format("HTTP DELETE response looks different than expected: %s", cblRestResponse.getRight()), possiblyErrorResponse);
        }
    }

    //TODO - make jackson pick up the correct generic type rather than a linked hash map
    @Override
    public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
        Pair<Integer, JsonNode> cblRestResponse = restConnector.patch(path, request, new GenericType<JsonNode>() { }, headers);
        try {
            JsonNode jsonNode = cblRestResponse.getRight();
            return Pair.of(cblRestResponse.getLeft(), JACKSON.convertValue(jsonNode.toString(), responseType));
        } catch (IllegalArgumentException possiblyErrorResponse) {
            throw new AssertionError(String.format("HTTP PATCH response looks different than expected: %s", cblRestResponse.getRight()), possiblyErrorResponse);
        }
    }
    @Override
    public <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers) {
        return this.restConnector.postExtended(path, request, responseType, headers);
    }

    @Override
    public StoreAccessor getStore() {
        return store;
    }
}
