"""
This inline script can be used to dump flows as HAR files.
"""


import json
import base64
import zlib
import os
import socket
import argparse
import requests

import io
import uuid

from datetime import datetime
from datetime import timezone

import mitmproxy

from mitmproxy import version
from mitmproxy import ctx
from mitmproxy.utils import strutils
from mitmproxy.net.http import cookies

try:
    to_unicode = unicode
except NameError:
    to_unicode = str

HAR     = {}
CONTEXT = {}
START_PAGE = True
END_PAGE = False

# A list of server seen till now is maintained so we can avoid
# using 'connect' time for entries that use an existing connection.
SERVERS_SEEN = set()

referer = None
browser = None
url = None
#  url = "https://bebop/cdpweather/addhar.php"

def start():
    global referer
    global browser
    global url
    #print("============================ début script ============================")
    parser = argparse.ArgumentParser()
    parser.add_argument('-browser')
    parser.add_argument('-url')
    parser.add_argument('-server')
    args = parser.parse_args()
    referer = args.url
    #referer = uuid.uuid4()
    browser = args.browser
    url = args.server

def done():
    global liste
    global browser
    #print("============================ fin du script ============================")
    #  str_ = json.dumps(HAR, indent=4, sort_keys=True, separators=(',', ': '), ensure_ascii=False)
    #  outfile.write(to_unicode(str_))

    # payload = {'name': '59d888e4f2153', 'browser': browser, 'har': HAR}
    payload = {'name': socket.gethostname(), 'browser': browser, 'har': HAR}
    send_data(json.dumps(payload))

def send_data(data):
    global url
    #  print(data)
    requests.packages.urllib3.disable_warnings()
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    resp = requests.post(url, data=data, headers=headers, allow_redirects=True, verify=False) 
    #  print(resp.text)


def load(l):
    l.add_option(
        "hardump", str, "", "HAR dump path.",
    )


def configure(self, updated):
    HAR.update({
        "log": {
            "version": "1.2",
            "creator": {
                "name": "spike spiegel",
                "version": "0.1",
                "comment": "mitmproxy version %s" % version.MITMPROXY
            },
            "browser": {
                "name": "",
                "version": "",
                "comment":""
                },
        "pages": [],
        "entries": []
        }
    })

    CONTEXT.update({
        "user-agent": "",
        "start": 0,
        "start-end": 0,
        "end-end": 0,
        "onContentLoad": 0,
        "onLoad": 0
        })

def request(flow):
    #  print(flow.request.headers.get("User-Agent"))
    #  CONTEXT["user-agent"] = flow.request.headers.get("User-Agent")
    HAR["log"]["browser"]["name"] = flow.request.headers.get("User-Agent")

def response(flow):
    """
       Called when a server response has been received.
    """

    global START_PAGE 
    global referer
    #  global end
    # -1 indicates that these values do not apply to current request
    ssl_time = 0
    connect_time = 0

    if flow.server_conn and flow.server_conn not in SERVERS_SEEN:
        connect_time = (flow.server_conn.timestamp_tcp_setup -
                        flow.server_conn.timestamp_start)

        SERVERS_SEEN.add(flow.server_conn)

    if flow.server_conn.timestamp_ssl_setup is not None:
        ssl_time = (flow.server_conn.timestamp_ssl_setup -
                    flow.server_conn.timestamp_tcp_setup)

    # Calculate raw timings from timestamps. DNS timings can not be calculated
    # for lack of a way to measure it. The same goes for HAR blocked.
    # mitmproxy will open a server connection as soon as it receives the host
    # and port from the client connection. So, the time spent waiting is actually
    # spent waiting between request.timestamp_end and response.timestamp_start
    # thus it correlates to HAR wait instead.
    timings_raw = {
        'blocked': 0,
        'dns': 0,
        'send': flow.request.timestamp_end - flow.request.timestamp_start,
        'receive': flow.response.timestamp_end - flow.response.timestamp_start,
        'wait': flow.response.timestamp_start - flow.request.timestamp_end,
        'connect': connect_time,
        'ssl': ssl_time,
    }

    # HAR timings are integers in ms, so we re-encode the raw timings to that format.
    timings = dict([(k, int(1000 * v)) for k, v in timings_raw.items()])

    #  print(json.dumps(timings, indent=2))

    # full_time is the sum of all timings.
    # Timings set to -1 will be ignored as per spec.
    full_time = sum(v for v in timings.values() if v > -1)

    #  print(full_time)

    started_date_time = datetime.fromtimestamp(flow.request.timestamp_start, timezone.utc).isoformat()

    # Response body size and encoding
    response_body_size = len(flow.response.raw_content)
    response_body_decoded_size = len(flow.response.content)
    response_body_compression = response_body_decoded_size - response_body_size

    entry = {
        "startedDateTime": started_date_time,
        "time": full_time,
        "request": {
            "method": flow.request.method,
            "url": flow.request.url,
            "httpVersion": flow.request.http_version,
            "cookies": format_request_cookies(flow.request.cookies.fields),
            "headers": name_value(flow.request.headers),
            "queryString": name_value(flow.request.query or {}),
            "headersSize": len(str(flow.request.headers)),
            "bodySize": len(flow.request.content),
        },
        "response": {
            "status": flow.response.status_code,
            "statusText": flow.response.reason,
            "httpVersion": flow.response.http_version,
            "cookies": format_response_cookies(flow.response.cookies.fields),
            "headers": name_value(flow.response.headers),
            "content": {
                "size": response_body_size,
                "compression": response_body_compression,
                "mimeType": flow.response.headers.get('Content-Type', '')
            },
            "redirectURL": flow.response.headers.get('Location', ''),
            "headersSize": len(str(flow.response.headers)),
            "bodySize": response_body_size,
        },
        "cache": {},
        "timings": timings,
        #"pageref": flow.request.url,
        "pageref": referer
    }

    is_new_page = flow.request.headers.get('Referer')

    if START_PAGE:
        #  print(json.dumps(HAR, indent=2))
        CONTEXT['start'] = int(1000 * flow.request.timestamp_start)
        CONTEXT['onLoad'] = CONTEXT['end-end'] - CONTEXT['start']
        CONTEXT['onContentLoad'] = CONTEXT['start-end'] - CONTEXT['start']
        pages = {
                "startedDateTime": datetime.fromtimestamp(flow.request.timestamp_start, timezone.utc).isoformat(),
                #  "id": flow.request.url,
                "id": referer,
                "title": "spike spiegel",
                "pageTimings": {
                        "onContentLoad": -1,
                        "onLoad": -1
                    }
            }
        #  print(json.dumps(pages, indent=2))
        HAR["log"]["pages"].append(pages)
        entry['pageref'] = referer
        CONTEXT['referer'] = entry['pageref']
        START_PAGE = False

    # Store binary data as base64
    if strutils.is_mostly_bin(flow.response.content):
        #  entry["response"]["content"]["text"] = base64.b64encode(flow.response.content).decode()
        entry["response"]["content"]["text"] = ""
        entry["response"]["content"]["encoding"] = "base64"
    else:
        #  entry["response"]["content"]["text"] = flow.response.get_text(strict=False)
        entry["response"]["content"]["text"] = ""

    if flow.request.method in ["POST", "PUT", "PATCH"]:
        params = [
            {"name": a, "value": b}
            for a, b in flow.request.urlencoded_form.items(multi=True)
        ]
        entry["request"]["postData"] = {
            "mimeType": flow.request.headers.get("Content-Type", ""),
            "text": flow.request.get_text(strict=False),
            "params": params
        }

    if flow.server_conn.connected():
        entry["serverIPAddress"] = str(flow.server_conn.ip_address).split(":")[0]
        #  print(str(flow.server_conn.ip_address))

    CONTEXT['end-end'] = int(1000 * flow.response.timestamp_end)
    CONTEXT['start-end'] = int(1000 * flow.request.timestamp_end)
    CONTEXT['onContentLoad'] = CONTEXT['start-end'] - CONTEXT['start']
    CONTEXT['onLoad'] = CONTEXT['end-end'] - CONTEXT['start']
    HAR['log']['pages'][0]['pageTimings']['onContentLoad'] = CONTEXT['onContentLoad']
    HAR['log']['pages'][0]['pageTimings']['onLoad'] = CONTEXT['onLoad']
    HAR["log"]["entries"].append(entry)
    #  print(json.dumps(CONTEXT, indent=2))


#  def _done():
    #  """
        #  Called once on script shutdown, after any other events.
    #  """
#      #  print(json.dumps(HAR, indent=2))
    #  print(json.dumps(PAGE, indent=2))
    #  with open('data.har', 'w') as outfile:
        #  json.dump(HAR, outfile)

#      with io.open('data.har', 'w', encoding='utf8') as outfile:
        #  str_ = json.dumps(HAR, indent=4, sort_keys=True, separators=(',', ': '), ensure_ascii=False)
        #  outfile.write(to_unicode(str_))

#      if ctx.options.hardump:
        #  json_dump = json.dumps(HAR, indent=2)  # type: str

        #  if ctx.options.hardump == '-':
            #  mitmproxy.ctx.log(json_dump)
        #  else:
            #  raw = json_dump.encode()  # type: bytes
            #  if ctx.options.hardump.endswith('.zhar'):
                #  raw = zlib.compress(raw, 9)

            #  with open(os.path.expanduser(ctx.options.hardump), "wb") as f:
                #  f.write(raw)

            #  mitmproxy.ctx.log("HAR dump finished (wrote %s bytes to file)" % len(json_dump))


def format_cookies(cookie_list):
    rv = []

    for name, value, attrs in cookie_list:
        cookie_har = {
            "name": name,
            "value": value,
        }

        # HAR only needs some attributes
        for key in ["path", "domain", "comment"]:
            if key in attrs:
                cookie_har[key] = attrs[key]

        # These keys need to be boolean!
        for key in ["httpOnly", "secure"]:
            cookie_har[key] = bool(key in attrs)

        # Expiration time needs to be formatted
        expire_ts = cookies.get_expiration_ts(attrs)
        if expire_ts is not None:
            cookie_har["expires"] = datetime.fromtimestamp(expire_ts, timezone.utc).isoformat()

        rv.append(cookie_har)

    return rv


def format_request_cookies(fields):
    return format_cookies(cookies.group_cookies(fields))


def format_response_cookies(fields):
    return format_cookies((c[0], c[1][0], c[1][1]) for c in fields)


def name_value(obj):
    """
        Convert (key, value) pairs to HAR format.
    """
    return [{"name": k, "value": v} for k, v in obj.items()]
