from __future__ import annotations
from typing import Any, Callable, TypeVar, cast, overload
from guayaba.app import app, route_exists
from guayaba.utils import base_path_for_post
from typing_extensions import dataclass_transform
from guayaba.endpoints import generate_get, generate_post, generate_put, generate_delete
from starlette.routing import compile_path
from pydantic.dataclasses import dataclass as _pydantic_dataclass

T = TypeVar("T")


# Make pydantic happy
@dataclass_transform()
@overload
def dataclass(route: str, /, **kwargs: Any) -> Callable[[type[T]], type[T]]: ...
@overload
def dataclass(**kwargs: Any) -> Callable[[type[T]], type[T]]: ...


def dataclass(route: str | None = None, /, **kwargs: Any):
    """
    @dataclass("/things/{id}") registers:
      - GET     /things/{id}
      - POST    /things
      - PUT     /things/{id}
      - DELETE  /things/{id}
    The key field ('id', 'name', etc.) is inferred from the route.
    """
    _, _, param_convertors = compile_path(route) if route else (None, None, None)
    key_field = param_convertors and next(iter(param_convertors.keys()))
    if not key_field or not isinstance(key_field, str):
        raise ValueError("Route must include a path parameter like '/path/{id}' or '/path/{name}'.")
    if not isinstance(route, str):
        raise ValueError("Route must be a string like '/path/{id}' or '/path/{name}'.")
    
    def decorator(cls: type[T], /) -> type[T]:
        dc = _pydantic_dataclass(cls, **kwargs)
        base_path = base_path_for_post(route)

        # --- CRUD endpoints ---
        get = generate_get(key_field)
        post = generate_post(key_field)
        put = generate_put(key_field)
        delete = generate_delete(key_field)

        # --- Register routes ---
        if not route_exists(route, "GET"):
            app.add_api_route(route, get, methods=["GET"], name=f"Get {dc.__name__}", response_model=dc)
        if not route_exists(base_path, "POST"):
            app.add_api_route(base_path, post, methods=["POST"], name=f"Create {dc.__name__}", response_model=dc)
        if not route_exists(route, "PUT"):
            app.add_api_route(route, put, methods=["PUT"], name=f"Update {dc.__name__}", response_model=dc)
        if not route_exists(route, "DELETE"):
            app.add_api_route(route, delete, methods=["DELETE"], name=f"Delete {dc.__name__}", response_model=dc)

        return cast(type[T], dc)

    return decorator

