import os
import logging
import socket
import sys
import time
from timeit import default_timer as timer
from unittest import mock
from urllib.request import urlopen
from urllib.error import URLError, HTTPError

from black import format_str, FileMode

import django
from django.conf import settings

from .providers_tests_data import providers_tests_data


captured_responses = []


def get_logger():
    """
    Create logger to show to user whats being fetched
    """
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    f_format = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    c_handler = logging.StreamHandler()
    c_handler.setFormatter(f_format)
    logger.addHandler(c_handler)
    return logger


logger = get_logger()


class UnexpectedNetworkError(Exception):
    """
    On real network errors like timeouts or DNS problems we need to raise
    a different errors because URLError is captured inside the providers code.
    """


def patched_urlopen(req, timeout):
    """
    Captures request and response data and store
    """
    captured = {
        "request": {
            "full_url": req.full_url,
            "method": req.method,
            "headers": req.headers,
            "data": req.data,
        }
    }
    start_timer = timer()

    try:
        res = urlopen(req, timeout=timeout)
    except HTTPError as error:
        captured["response"] = {
            "type": "error",
            "status": error.status,
            "data": error.read(),
        }
    except (URLError, socket.timeout):
        elapsed_time = timer() - start_timer
        logger.exception(
            "Couldn't capture response (waited {elapsed_time:2.2f}s) from {method:<4} to {full_url}".format(
                elapsed_time=elapsed_time, **captured["request"]
            )
        )
        sys.exit(1)
    else:
        captured["response"] = {"type": "success", "data": res.read()}

    elapsed_time = timer() - start_timer
    logger.info(
        "Captured {response_type:<7} response in {elapsed_time:2.2f}s from "
        "{method:<4} to {full_url}".format(
            elapsed_time=elapsed_time,
            response_type=captured["response"]["type"],
            **captured["request"],
        )
    )

    captured_responses.append(captured)
    return urlopen(req, timeout=timeout)


def save_to_file(data):
    filename = "captured_responses.py"
    dir_path = os.path.dirname(os.path.realpath(__file__))
    path = os.path.join(dir_path, filename)

    formatted_str = format_str(repr(data), mode=FileMode())
    now_ts = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())

    content = f"""\
# This file was generated by the "{os.path.basename(__file__)}" script.
# On {now_ts}.
#
# To update it run:
# python -m tests.providers.capture_real_responses

captured_responses = {formatted_str}
"""
    with open(path, "w") as writer:
        writer.write(content)


def main():
    settings.configure(SIMPLECEP={"PROVIDERS_TIMEOUT": 4})
    django.setup()

    from simplecep.providers.fetcher import providers

    with mock.patch("simplecep.providers.base.urlopen", side_effect=patched_urlopen):
        for test_data in providers_tests_data:
            for provider in providers:
                provider.get_cep_data(test_data["input"])
    save_to_file(captured_responses)


if __name__ == "__main__":
    main()
