package com.telephoners.krakyournet.ctf.resources;

import com.google.common.collect.Sets;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.beans.tasks.TaskRequestContext;
import com.telephoners.krakyournet.ctf.beans.tasks.TaskResponse;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.helpers.StreamUtils;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import io.dropwizard.auth.Auth;
import org.glassfish.jersey.server.ContainerRequest;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Singleton
@Path(value = "/task")
public class TaskResource
{
    private final ApplicationConfiguration applicationConfiguration;
    private final TasksRepository tasksRepository;

    @Inject
    public TaskResource(ApplicationConfiguration applicationConfiguration, TasksRepository tasksRepository)
    {
        this.applicationConfiguration = applicationConfiguration;
        this.tasksRepository = tasksRepository;
    }

    //todo: name
    private TaskResponse getTaskResponse(int taskLevel, TaskRequestContext taskRequestContext) throws IOException
    {
        Task task = tasksRepository.getByLevel(taskLevel);
        return task.getTaskResponse(taskRequestContext);
    }

    private Map<String, String> extractProxiedHeaders(MultivaluedMap<String, String> headers) {
        Set<String> proxiedHeaders = applicationConfiguration.getProxiedHeaders();
        return headers.entrySet()
                .stream()
                .filter(header -> proxiedHeaders.contains(header.getKey()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().get(0) //todo: check
                ));
    }

    @Path("{task_level}/{path: .*}")
    @GET
    public Response getTaskGet(@Auth User user,
                                final @PathParam("task_level") int taskLevel,
                                final @PathParam("path") String path,
                                @Context ContainerRequestContext containerRequestContext) throws IOException
    {
        String fullPath = path;
        String query = ((ContainerRequest)containerRequestContext).getRequestUri().getQuery();
        if(query != null) {
            fullPath += query;
        }
        Map<String, String> headers = extractProxiedHeaders(containerRequestContext.getHeaders());//todo: inline
        TaskRequestContext taskRequestContext = new TaskRequestContext()
                .withHttpMethod("POST")
                .withUser(user)
                .withPath(fullPath)
                .withHeaders(headers);

        TaskResponse taskResponse = getTaskResponse(taskLevel, taskRequestContext);


        Response.ResponseBuilder responseBuilder = Response.ok()
                .entity(taskResponse.getText());

        taskResponse.getHeaders().entrySet()
                .stream()
                .forEach(headerEntry -> responseBuilder.header(headerEntry.getKey(), headerEntry.getValue()));
        return responseBuilder.build();
    }

    @Path("{task_level}/{path: .*}")
    @POST
    public Response getTaskPost(@Auth User user,
                            final @PathParam("task_level") int taskLevel,
                            final @PathParam("path") String path,
                            @Context ContainerRequestContext containerRequestContext) throws IOException
    {
        String fullPath = path;
        String query = ((ContainerRequest)containerRequestContext).getRequestUri().getQuery();
        if(query != null) {
            fullPath += query;
        }
        String body = StreamUtils.readStream(containerRequestContext.getEntityStream());
        //todo: TaskContextFrom

        Map<String, String> headers = extractProxiedHeaders(containerRequestContext.getHeaders());//todo: inline

        TaskRequestContext taskRequestContext = new TaskRequestContext()
                .withHttpMethod("POST")
                .withUser(user)
                .withPath(fullPath)
                .withBody(body)
                .withHeaders(headers);

        TaskResponse taskResponse = getTaskResponse(taskLevel, taskRequestContext);

        Response.ResponseBuilder responseBuilder = Response.ok()
                .entity(taskResponse.getText());

        taskResponse.getHeaders().entrySet()
                .stream()
                .forEach(headerEntry -> responseBuilder.header(headerEntry.getKey(), headerEntry.getValue()));

        return responseBuilder.build();
    }
}
