package com.elastic.support.rest;

import com.elastic.support.Constants;
import com.elastic.support.util.SystemProperties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class RestResult implements Cloneable {

    private static final Logger logger = LogManager.getLogger(RestResult.class);

    String responseString = "Undetermined error = check logs";
    int status = -1;
    String reason;
    boolean isRetryable;
    String url = "";

    // Sending in a response object to be processed implicitly
    // closes the response as a result. The body is either streamed directly
    // to disk or the body is stored as a string and the status retained as well.
    public RestResult(HttpResponse response, String url) {
        this.url = url;
        try{
            processCodes(response);
            responseString = EntityUtils.toString(response.getEntity());
        }
        catch (Exception e){
            logger.error( "Error Processing Response", e);
            throw new RuntimeException();
        }
        finally {
            HttpClientUtils.closeQuietly(response);
        }
    }

    public RestResult(HttpResponse response, String fileName, String url) {

        this.url = url;

        // If the query got a success status stream the result immediately to the target file.
        // If not, the result should be small and contain diagnostic info so stgre it in the response string.
        File output = new File(fileName);
        if(output.exists()){
            FileUtils.deleteQuietly(output);
        }

        try(OutputStream out = new FileOutputStream(fileName)){
            processCodes(response);
            if (status == 200) {
                response.getEntity().writeTo(out);
            } else {
                responseString = EntityUtils.toString(response.getEntity());
                IOUtils.write(reason + SystemProperties.lineSeparator + responseString, out, Constants.UTF_8);
            }
        } catch (Exception e) {
            logger.error( "Error Streaming Response To OutputStream", e);
            throw new RuntimeException();
        }
        finally {
            HttpClientUtils.closeQuietly(response);
        }
    }

    private void processCodes(HttpResponse response){
        status = response.getStatusLine().getStatusCode();
        if (status == 400) {
            reason = "Bad Request. Rejected";
           isRetryable = true;
        } else if (status == 401) {
            reason = "Authentication failure. Invalid login credentials.";
            isRetryable = false;
        } else if (status == 403) {
            reason = "Authorization failure or invalid license.";
            isRetryable = false;
        } else if (status == 404) {
            reason = "Endpoint does not exist.";
            isRetryable = true;
        } else {
            reason = response.getStatusLine().getReasonPhrase();
            isRetryable = true;
        }
    }

    public String formatStatusMessage(String msg){

        if(StringUtils.isNotEmpty(msg)){
            msg = msg + " ";
        }

        return String.format("%sStatus: %d  Reason: %s",
                msg,
                status,
                reason);
    }

    public int getStatus(){
        return status;
    }

    public String getReason(){
        return reason;
    }

    public String toString() {
        return responseString;
    }

    public void toFile(String fileName){
        File output = new File(fileName);
        if(output.exists()){
            FileUtils.deleteQuietly(output);
        }

        try(FileOutputStream fs = new FileOutputStream(output)){
            IOUtils.write(reason + SystemProperties.lineSeparator + responseString, fs, Constants.UTF_8);
        }
        catch (Exception e){
            logger.error( "Error writing Response To OutputStream", e);
        }
    }

    public boolean isRetryable() {
        return isRetryable;
    }

    public boolean isValid(){
        if(status == 200){
            return true;
        }
        return false;
    }

}