"""Serve an owned dependency fixture from a loopback HTTPS origin; never dial a destination."""
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import ssl
import subprocess
import tempfile
import threading
import time

HOST = "127.0.0.1"


class Origin(BaseHTTPRequestHandler):
    """Answers from ``server.responses``; a body shorter than ``expected_size`` is a partial GET."""

    protocol_version = "HTTP/1.1"

    def do_GET(self):
        self.server.requests.append(f"GET {self.path} HTTP/1.1".encode())
        status, body, delay = self.server.responses.pop(0)
        time.sleep(delay)
        headers = (f"HTTP/1.1 {status} Fixture\r\nConnection: close\r\n"
                   f"Content-Length: {self.server.expected_size}\r\n\r\n").encode()
        self.wfile.write(headers + body)

    def log_message(self, *_args):
        pass


class Server(ThreadingHTTPServer):
    daemon_threads = True

    def handle_error(self, *_args):
        pass  # An untrusted fixture certificate must be rejected by the real client, quietly.


@contextmanager
def running():
    with tempfile.TemporaryDirectory(prefix="crashlytics-origin-") as directory:
        root = Path(directory)
        certificate, key = root / "cert.pem", root / "key.pem"
        subprocess.run(["openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-days", "1",
                        "-keyout", str(key), "-out", str(certificate), "-subj", "/CN=" + HOST,
                        "-addext", "subjectAltName=IP:" + HOST],
                       check=True, capture_output=True, timeout=10)
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        context.load_cert_chain(certificate, key)
        server = Server((HOST, 0), Origin)
        server.socket = context.wrap_socket(server.socket, server_side=True)
        server.requests, server.responses, server.expected_size = [], [], 0
        server.url = lambda path, _s=server: f"https://{HOST}:{_s.server_address[1]}{path}"
        thread = threading.Thread(target=server.serve_forever, daemon=True)
        thread.start()
        try:
            yield server, str(certificate)
        finally:
            server.shutdown()
            server.server_close()
            thread.join(timeout=2)
