import html
import traceback
import json
import re
import requests
import urllib3
from requests.exceptions import Timeout, ConnectTimeout, ReadTimeout

EXECUTION_CONFIG_PARAMETER_NAME = "vadalog"
EXECUTION_MODE_PARAMETER_NAME = "mode"
RUN_PROGRAM_MODE = "run"

ERROR_HTTP_REQUEST = "httpRequestError"
ERROR_NO_VADALOG_SERVER = "noVadalogServer"
ERROR_UNKNOWN_EXECUTION_MODE = "unknownRunMode"
ERROR_HTTP_TIMEOUT = "httpTimeout"
ERROR_HTTP_RESPONSE = "httpResponseError"
ERROR_INVALID_RESPONSE_FORMAT = "invalidResponseFormat"
ERROR_UNKNOWN = "unknownError"

RESPONSE_TIMEOUT_DEFAULT = 180000

class VadalogRunner:

    def __init__(self, vadalog_endpoint):
        self.vadalog_endpoint = vadalog_endpoint

    def run(self, jupyter_data):
        try:
            run_config = json.loads(jupyter_data)
        except json.JSONDecodeError:
            # we get a plain code
            run_config = {"code":jupyter_data}

        vadalog_endpoint = run_config.get("vadalog_endpoint", self.vadalog_endpoint)
        run_mode = run_config.get(EXECUTION_MODE_PARAMETER_NAME, RUN_PROGRAM_MODE).lower()
        execution_mode = None
        post_data_req_format = "form-encoded"

        post_data_req = {}
        code = run_config.get("code", "")
        other_code = run_config.get("other_code", [])

        if not isinstance(other_code, list):
            other_code = [other_code]

        if isinstance(other_code, list) and len(other_code) == 1:
            other_code.append("")

        # Only support RUN_PROGRAM_MODE (normal cell execution)
        execution_mode = RUN_PROGRAM_MODE
        endpoint_req = vadalog_endpoint + "/evaluate?measureElapsedTime=true"
        post_data_req["program"] = code
        post_data_req["otherPrograms"] = other_code

        if execution_mode is not None:
            try:
                response_timeout = run_config.get("response_timeout", RESPONSE_TIMEOUT_DEFAULT)
                if post_data_req_format == "form-encoded":
                    response = requests.post(endpoint_req, data=post_data_req, timeout=response_timeout)
                else: #json
                    response = requests.post(endpoint_req, json=post_data_req, timeout=response_timeout)
                try:
                    json_response = json.loads(response.text)
                except:
                    # return {"error": ERROR_INVALID_RESPONSE_FORMAT, "message": "Invalid format of the response."}
                    return {"error": ERROR_INVALID_RESPONSE_FORMAT, "message": "Invalid format of the response. [RESPONSE]> {}".format(str(response.text))}

            except (Timeout, ConnectTimeout, ReadTimeout, TimeoutError) as e:
                return {"error": ERROR_HTTP_TIMEOUT, "message": "Timeout {} sec reached".format(response_timeout)}
            except (requests.exceptions.ConnectionError, urllib3.exceptions.ConnectionError) as e:
                return {"error": ERROR_NO_VADALOG_SERVER, "message": "Can't connect to Vadalog server."}
            except (requests.exceptions.RequestException, urllib3.exceptions.HTTPError) as e:
                return {"error": ERROR_HTTP_REQUEST, "message": "HTTP Request error."}
            except Exception as ex:
                if response.ok:
                    return {"error": ERROR_UNKNOWN, "message": "Unknown error."}
                else:
                    return {"error": ERROR_HTTP_RESPONSE, "message": "Status code {}".format(response.status_code)}
                    # return {"error": ERROR_HTTP_RESPONSE, "message": "Status code {}".format(ex)}

            return {"response": json_response, EXECUTION_MODE_PARAMETER_NAME: execution_mode}
        else:
            return {"error": ERROR_UNKNOWN_EXECUTION_MODE, "message": "Unknown execution mode"}
