package jp.gr.java_conf.hhayakawa_jp.beehive_client;

import java.io.IOException;
import java.net.HttpCookie;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import jp.gr.java_conf.hhayakawa_jp.beehive_client.exception.BeeClientException;
import jp.gr.java_conf.hhayakawa_jp.beehive_client.exception.BeeClientIllegalInvokerStateException;

abstract class BeehiveInvoker {

    protected static final String BEEHIVE_API_ROOT =
            "https://stbeehive.oracle.com/comb/v1/d/";

    private final String endpoint;

    private final HttpMethod method;

    private HttpHeaders headers = new HttpHeaders();

    private Map<String, String> urlQueries = new HashMap<String, String>();

    private BeehiveApiPayload payload;

    BeehiveInvoker(
            BeehiveCredential credential, String endpoint, HttpMethod method) {
        this.endpoint = endpoint;
        this.method = method;
        setDefaultHeaders(credential);
        setDefaultUrlQueries(credential);
    }

    JsonNode invoke() throws JsonProcessingException, IOException, BeeClientException {
        if (!isPrepared()) {
            throw new BeeClientIllegalInvokerStateException(
                    "Invoker is not correctly prepared.");
        }
        // header, body
        HttpEntity<BeehiveApiPayload> entity =
                new HttpEntity<BeehiveApiPayload>(payload, headers);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> result = restTemplate.exchange(
                endpoint + makeUrlQueryString(), method, entity, String.class);
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readTree(result.getBody());
    }

    // TODO なんかスケルトンパターンぽくない
    protected void addHeader(Map<String, String> headers) {
        this.headers.setAll(headers);
    }

    // TODO なんかスケルトンパターンぽくない
    protected void addUrlQuery(Map<String, String> queries) {
        this.urlQueries.putAll(queries);
    }

    // TODO なんかスケルトンパターンぽくない
    protected void setPayload(BeehiveApiPayload payload) {
        this.payload = payload;
    }

    protected BeehiveApiPayload getBody() {
        return this.payload;
    }

    abstract protected boolean isPrepared();

    private void setDefaultHeaders(BeehiveCredential credential) {
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        headers.setContentType(MediaType.APPLICATION_JSON);
        Map<String, String> additionalHeaders = new HashMap<String, String>(1);
        HttpCookie sessionCookie = credential.getSession();
        additionalHeaders.put(HttpHeaders.COOKIE,
                sessionCookie.getName() + "=" + sessionCookie.getValue());
        addHeader(additionalHeaders);
    }

    private void setDefaultUrlQueries(BeehiveCredential credential) {
        Map<String, String> csrf = new HashMap<String, String>(1);
        csrf.put("anticsrf", credential.getAnticsrf());
        addUrlQuery(csrf);
    }
 
    private String makeUrlQueryString() {
        if (urlQueries == null || urlQueries.isEmpty()) {
            return "";
        }
        StringBuffer bf = new StringBuffer("?");
        Set<Entry<String, String>> entries = urlQueries.entrySet();
        int i = 0;
        for (Entry<String, String> entry : entries) {
            if (++i > 0) {
                bf.append("&");
            }
            bf.append(entry.getKey() + "=" + entry.getValue());
        }
        return bf.toString();
    }

}

@@ -2183,37 +2183,8 @@
  %7D%0A%0A
-    // TODO %E3%81%AA%E3%82%93%E3%81%8B%E3%82%B9%E3%82%B1%E3%83%AB%E3%83%88%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%BD%E3%81%8F%E3%81%AA%E3%81%84%0A
     
@@ -2235,24 +2235,24 @@
  headers) %7B%0A
+
         this
@@ -2288,37 +2288,8 @@
  %7D%0A%0A
-    // TODO %E3%81%AA%E3%82%93%E3%81%8B%E3%82%B9%E3%82%B1%E3%83%AB%E3%83%88%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%BD%E3%81%8F%E3%81%AA%E3%81%84%0A
     
@@ -2398,37 +2398,8 @@
  %7D%0A%0A
-    // TODO %E3%81%AA%E3%82%93%E3%81%8B%E3%82%B9%E3%82%B1%E3%83%AB%E3%83%88%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%BD%E3%81%8F%E3%81%AA%E3%81%84%0A
     

clean up.