import json
import re
import threading

from pydantic import BaseModel
from werkzeug.routing import Map, Rule
from werkzeug.serving import BaseWSGIServer, make_server
from werkzeug.wrappers import Response

from core.contract import Contract
from serialization.writer import write_contract


class ExampleBuilder:
    def __init__(self, examples: dict[str, BaseModel], name: str):
        self.examples = examples
        self.name = name

    def example(self, model: BaseModel):
        self.examples[self.name] = model


class MockServer:
    def __init__(self, contract: type[Contract]):
        self.contract: type[Contract] = contract
        self.examples: dict[str, BaseModel] = {}

        self.server: BaseWSGIServer
        self.thread: threading.Thread
        self.port: int = 5000
        self.map: Map

    def given(self, name: str) -> ExampleBuilder:
        return ExampleBuilder(self.examples, name)

    def _wsgi_app(self, environ, start_response):
        adapter = self.map.bind_to_environ(environ)
        endpoint, kwargs = adapter.match()  # pyright: ignore[reportAny]
        example = self.examples[endpoint]
        response = Response(
            json.dumps(example.model_dump()), mimetype="application/json"
        )

        return response(environ, start_response)

    def __enter__(self):
        rules = []

        for name, interaction in self.contract.interactions.items():
            path_ = re.sub(r"{(.*)}", r"<string:\1>", interaction.path)
            rules.append(Rule(path_, endpoint=name, methods=[interaction.http_method]))

        self.map = Map(rules)

        self.server = make_server("127.0.0.1", self.port, self._wsgi_app, threaded=True)
        self.thread = threading.Thread(target=self.server.serve_forever)
        self.thread.start()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        write_contract(self.contract, self.examples)

        self.server.shutdown()
        self.thread.join()
