package org.commcare.util.screen;

import static org.javarosa.core.model.Constants.EXTRA_POST_SUCCESS;

import com.google.common.collect.Multimap;

import org.commcare.modern.session.SessionWrapper;
import org.commcare.session.CommCareSession;
import org.commcare.suite.model.Entry;
import org.commcare.suite.model.PostRequest;

import java.io.IOException;
import java.io.PrintStream;

import datadog.trace.api.Trace;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * Screen to make a sync request to HQ after a case claim. Unlike other all other screens,
 * SyncScreen does not take input - it simply processes the PostRequest object, makes the request,
 * and displays the result (if necessary)
 */
public class SyncScreen extends Screen {

    protected SessionWrapper sessionWrapper;
    private String username;
    private String password;
    private PrintStream printStream;
    private boolean syncSuccessful;

    public SyncScreen(String username, String password, PrintStream printStream) {
        super();
        this.username = username;
        this.password = password;
        this.printStream = printStream;
    }

    @Override
    public void init (SessionWrapper sessionWrapper) throws CommCareSessionException {
        this.sessionWrapper = sessionWrapper;
        parseMakeRequest();
    }

    private void parseMakeRequest() throws CommCareSessionException {
        String command = sessionWrapper.getCommand();
        Entry commandEntry = sessionWrapper.getPlatform().getEntry(command);

        PostRequest syncPost = commandEntry.getPostRequest();
        if (syncPost == null) {
            // expected a sync entry; clear session and show vague 'session error' message to user
            throw new CommCareSessionException("Initialized sync request while not on sync screen");
        }

        try {
            Response response = makeSyncRequest(syncPost);
            if (!response.isSuccessful()) {
                printStream.println(String.format("Sync request failed with response code %s and message %s", response.code(), response.body()));
                printStream.println("Press 'enter' to retry.");
                return;
            }
            syncSuccessful = true;
            if (response.code() != 204) {
                SessionUtils.restoreUserToSandbox(sessionWrapper.getSandbox(),
                    sessionWrapper,
                    sessionWrapper.getPlatform(),
                    username,
                    password,
                    printStream);

                printStream.println(String.format("Sync successful with response %s", response));
            } else {
                printStream.println("Did not sync because case was already claimed.");
            }
            printStream.println("Press 'enter' to continue.");
        } catch (IOException e) {
            e.printStackTrace();
            printStream.println(String.format("Sync failed with exception %s", e.getMessage()));
            printStream.println("Press 'enter' to retry.");
        }
    }

    private static String buildUrl(String baseUrl) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
        return urlBuilder.build().toString();
    }

    private static MultipartBody buildPostBody(Multimap<String, String> params) {
        MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);
        // Add buffer param since this is necessary for some reason
        requestBodyBuilder.addFormDataPart("buffer", "buffer");
        params.forEach(requestBodyBuilder::addFormDataPart);
        return requestBodyBuilder.build();
    }

    private Response makeSyncRequest(PostRequest syncPost) throws CommCareSessionException, IOException {
        Multimap<String, String> params = syncPost.getEvaluatedParams(sessionWrapper.getEvaluationContext());
        String url = buildUrl(syncPost.getUrl().toString());
        printStream.println(String.format("Syncing with url %s and parameters %s", url, params));
        MultipartBody postBody = buildPostBody(params);
        String credential = Credentials.basic(username, password);

        Request request = new Request.Builder()
                .url(url)
                .method("POST", RequestBody.create(null, new byte[0]))
                .header("Authorization", credential)
                .post(postBody)
                .build();
        OkHttpClient client = new OkHttpClient();
        return client.newCall(request).execute();
    }

    @Override
    public boolean prompt(PrintStream printStream) throws CommCareSessionException {
        if (syncSuccessful) {
            printStream.println("Sync complete, press Enter to continue");
        } else {
            printStream.println("Sync failed, press Enter to retry");
        }
        return true;
    }

    @Trace
    @Override
    public boolean handleInputAndUpdateSession(CommCareSession commCareSession, String s, boolean allowAutoLaunch) throws CommCareSessionException {
        if (syncSuccessful) {
            sessionWrapper.addExtraToCurrentFrameStep(EXTRA_POST_SUCCESS, true);
            Entry commandEntry = sessionWrapper.getCurrentEntry();
            if (commandEntry.getXFormNamespace() != null) {
                // session is not complete, keep going
                return true;
            }

            commCareSession.syncState();
            if (commCareSession.finishExecuteAndPop(sessionWrapper.getEvaluationContext())) {
                sessionWrapper.clearVolatiles();
            }
            return true;
        } else {
            parseMakeRequest();
            return false;
        }
    }

    @Override
    public String[] getOptions() {
        return new String[0];
    }
}

@@ -1530,62 +1530,8 @@
 n %7B%0A
-        String command = sessionWrapper.getCommand();%0A
     
@@ -1577,39 +1577,24 @@
 .get
-Platform().getEntry(command
+CurrentEntry(
 );%0A
-%0A
     

use session function