package com.remy.graphql.builder;

import com.remy.common.module.GoodsDTO;
import com.remy.common.module.OrderDTO;
import com.remy.common.module.UserDTO;
import com.remy.graphql.evn.RestAPIEnv;
import graphql.kickstart.execution.context.DefaultGraphQLContext;
import graphql.kickstart.execution.context.GraphQLContext;
import graphql.kickstart.servlet.context.DefaultGraphQLServletContext;
import graphql.kickstart.servlet.context.DefaultGraphQLWebSocketContext;
import graphql.kickstart.servlet.context.GraphQLServletContextBuilder;
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.concurrent.CompletableFuture.supplyAsync;

@Configuration
public class GraphqlContextBuilder implements GraphQLServletContextBuilder {

    @Autowired
    RestAPIEnv apiEnv;

    @Override
    public GraphQLContext build(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        return DefaultGraphQLServletContext.createServletContext(buildDataLoaderRegistry(), null)
                .with(httpServletRequest)
                .with(httpServletResponse)
                .build();
    }

    @Override
    public GraphQLContext build(Session session, HandshakeRequest handshakeRequest) {
        return DefaultGraphQLWebSocketContext.createWebSocketContext(buildDataLoaderRegistry(), null)
                .with(session)
                .with(handshakeRequest)
                .build();
    }

    @Override
    public GraphQLContext build() {
        return new DefaultGraphQLContext(buildDataLoaderRegistry(), null);
    }

    private DataLoaderRegistry buildDataLoaderRegistry() {
        DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
        dataLoaderRegistry.register("goodsDataLoader", goodsDataLoader());
        dataLoaderRegistry.register("userDataLoader", userDataLoader());
        dataLoaderRegistry.register("orderGoodsDataLoader", orderGoodsDataLoader());
        dataLoaderRegistry.register("orderUserDataLoader", orderUserDataLoader());
        return dataLoaderRegistry;
    }

    private DataLoader<String, GoodsDTO> goodsDataLoader() {
        RestTemplate restTemplate = new RestTemplate();
        return new DataLoader<>(
                goodsSerialNos -> supplyAsync(
                        () -> goodsSerialNos.stream().map(goodsSerialNo -> restTemplate.getForObject(
                                apiEnv.getGoodsEndpoint() + goodsSerialNo, GoodsDTO.class))
                                .collect(Collectors.toList())));
    }

    private DataLoader<Long, UserDTO> userDataLoader() {
        RestTemplate restTemplate = new RestTemplate();
        return new DataLoader<>(
                userIDs -> supplyAsync(
                        () -> userIDs.stream().map(userID -> restTemplate.getForObject(
                                apiEnv.getUsersEndpoint() + userID, UserDTO.class))
                                .collect(Collectors.toList())));
    }

    private DataLoader<String, List<OrderDTO>> orderGoodsDataLoader() {
        RestTemplate restTemplate = new RestTemplate();
        return new DataLoader<>(
                goodsSerialNos -> supplyAsync(
                        () -> goodsSerialNos.stream().map(goodsSerialNo ->
                                restTemplate.exchange(
                                        apiEnv.getOrdersEndpoint() + "query?goodsSerialNo={goodsSerialNo}",
                                        HttpMethod.GET,
                                        new HttpEntity(null),
                                        new ParameterizedTypeReference<List<OrderDTO>>() {
                                        }, goodsSerialNo).getBody())
                                .collect(Collectors.toList())));
    }

    private DataLoader<Long, List<OrderDTO>> orderUserDataLoader() {
        RestTemplate restTemplate = new RestTemplate();
        return new DataLoader<>(
                userIDs -> supplyAsync(
                        () -> userIDs.stream().map(userID ->
                                restTemplate.exchange(
                                        apiEnv.getOrdersEndpoint() + "query?userID={userID}",
                                        HttpMethod.GET,
                                        new HttpEntity(null),
                                        new ParameterizedTypeReference<List<OrderDTO>>() {
                                        }, userID).getBody())
                                .collect(Collectors.toList())));
    }
}
