// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.processing.processors;

import com.yahoo.component.chain.dependencies.Provides;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.jdisc.handler.*;
import com.yahoo.processing.Processor;
import com.yahoo.processing.Request;
import com.yahoo.processing.Response;
import com.yahoo.processing.execution.Execution;

import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Provides("User")
public class MockUserDatabaseClient extends Processor {

    @Override
    public Response process(Request request, Execution execution) {
        try {
            Dispatch.CompleteResponse response =
                    new Dispatch("pio://endpoint/parameters",request).get(request.properties().getInteger("timeout"), TimeUnit.MILLISECONDS);
            User user = decodeResponseToUser(response);
            request.properties().set("User", user);
            return execution.process(request);
        }
        catch (InterruptedException | ExecutionException | TimeoutException e) {
            throw new RuntimeException("Exception waiting for database", e);
        }
    }

    private User decodeResponseToUser(Dispatch.CompleteResponse response) {
        // Just a mock implementation ...
        String responseData = response.nextString();
        if ( ! responseData.startsWith("id="))
            throw new IllegalArgumentException("Unexpected response "  + responseData);
        int newLine = responseData.indexOf("\n");
        if (newLine<0)
            throw new IllegalArgumentException("Unexpected response "  + responseData);
        String id = responseData.substring(3,newLine);

        // Make sure to always consume all
        while ( (responseData=response.nextString()) !=null) { }

        return new User(id);
    }

    // TODO: Move this to a top-level class
    public static class User {

        // TODO: OO model of users

        private String id;

        public User(String id) {
            this.id = id;
        }

        public String getId() { return id; }

    }

    private static class Dispatch {

        private final SimpleRequestDispatch requestDispatch;

        public Dispatch(String requestUri,Request request) {
            this.requestDispatch = new SimpleRequestDispatch(requestUri, request);
        }

        public CompleteResponse get(int timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
            return new CompleteResponse(requestDispatch.get(timeout, timeUnit),
                                        requestDispatch.getResponseContent());
        }

        public static class CompleteResponse {

            private final com.yahoo.jdisc.Response response;
            private final ReadableContentChannel responseData;

            public CompleteResponse(com.yahoo.jdisc.Response response, ReadableContentChannel responseData) {
                this.response = response;
                this.responseData = responseData;
            }

            public com.yahoo.jdisc.Response response() { return response; }

            public ReadableContentChannel responseData() { return responseData; }

            /**
             * Convenience which returns the next piece of content from the response data of this as a string, or
             * null if there is no more data. The channel must always be consumed until there is no more data.
             */
            private String nextString() {
                ByteBuffer nextBuffer = responseData.read();
                if (nextBuffer == null) return null; // end of transmission
                return Charset.forName("utf-8").decode(nextBuffer).toString();
            }

        }

        private static class SimpleRequestDispatch extends RequestDispatch {

            private final URI requestUri;
            private final com.yahoo.jdisc.Request parentRequest;
            private final ReadableContentChannel responseData = new ReadableContentChannel();

            public SimpleRequestDispatch(String requestUri,Request request) {
                this.requestUri = URI.create(requestUri);
                this.parentRequest = ((HttpRequest)request.properties().get("jdisc.request")).getJDiscRequest();
                dispatch();
            }

            @Override
            protected com.yahoo.jdisc.Request newRequest() {
                return new com.yahoo.jdisc.Request(parentRequest, requestUri);
            }

            @Override
            public ContentChannel handleResponse(com.yahoo.jdisc.Response response) {
                return responseData;
            }

            public ReadableContentChannel getResponseContent() {
                return responseData;
            }

        }

    }

}
