from functools import partial, wraps
from inspect import getmembers, isfunction
from typing import Any, Callable, cast, get_type_hints

from pydantic import BaseModel

from .models import EndpointFunction, EndpointMetadata


class Contract:
    consumer: str = ""
    producer: str = ""

    def __init_subclass__(cls) -> None:
        cls.interactions: dict[str, EndpointMetadata] = {}
        class_methods = getmembers(cls, predicate=isfunction)

        for name, method in class_methods:
            if hasattr(method, "_accord_endpoint"):
                obj_temp = cast(object, method)
                endpoint_fn = cast(EndpointFunction, obj_temp)

                metadata = endpoint_fn._accord_endpoint

                cls.interactions[name] = metadata


# https://stackoverflow.com/a/10300995/9693927
def endpoint[F: Callable[..., Any]](
    func: Callable[..., Any] | None = None,
    /,
    *,
    http_method: str | None = None,
    path: str | None = None,
):
    if not func:
        return partial(endpoint, http_method=http_method, path=path)  # pyright: ignore[reportUnknownVariableType]

    if not http_method or not path:
        raise ValueError("endpoint decorator requires http_method and path")

    @wraps(func)
    def wrapper(*args, **kwargs) -> F:
        return func(*args, **kwargs)  # pyright: ignore[reportAny]

    ret = get_type_hints(func).get("return")
    if ret is None:
        raise ValueError(f"Endpoint {func.__name__} must have a return type annotation")
    ret = cast(type[BaseModel], ret)

    obj_temp = cast(object, wrapper)
    endpoint_fn = cast(EndpointFunction, obj_temp)

    endpoint_fn._accord_endpoint = EndpointMetadata(http_method, path, ret)
    return endpoint_fn
