import re
from pathlib import Path
from typing import Any, cast

import httpx

from serialization.reader import read_contract

JSON_TYPE_MAP = {
    "string": str,
    "integer": int,
    "number": (int, float),
    "boolean": bool,
    "array": list,
    "object": dict,
    "null": type(None),
}


class ContractVerifier:
    def __init__(self, contract_path: Path, base_url: str) -> None:
        self.contract_path = contract_path
        self.base_url = base_url

    def verify(self) -> None:
        pc = read_contract(self.contract_path)

        for interaction in pc.interactions:
            path = interaction.request["path"]
            example_body = cast(dict[str, Any], interaction.response.get("body", {}))

            for param_name, param_value_raw in example_body.items():  # pyright: ignore[reportAny]
                param_value = cast(str | int | float, param_value_raw)
                path = re.sub(rf"{{{param_name}}}", str(param_value), path)

            res = httpx.request(
                interaction.request["method"],
                self.base_url + path,
            )

            body: dict[str, Any] = cast(dict[str, Any], res.json())
            for path, rule in interaction.matching_rules.items():
                field = path.split(".")[-1]

                if rule["match"] not in JSON_TYPE_MAP:
                    raise ValueError(f"Unsupported match type '{rule['match']}'")
                expected_type = JSON_TYPE_MAP[rule["match"]]

                actual_value = cast(object, body[field])
                if not isinstance(actual_value, expected_type):
                    raise ValueError(
                        f"Field '{field}' expected type {expected_type}, got {type(actual_value)}"
                    )
