from functools import wraps
from typing import Callable, Awaitable, Any, Coroutine

from starlette.responses import JSONResponse

from tonilib.fastapi.models import DataSuccess, ApiResponse, ApiInfoResponse, FailureData


def ok(data: DataSuccess, info: str = "") -> JSONResponse:
    return JSONResponse(ApiResponse[DataSuccess](data=data, info=ApiInfoResponse(info=info)).model_dump(),
                        status_code=200)


def created(data: DataSuccess, info: str = "") -> JSONResponse:
    return JSONResponse(ApiResponse[DataSuccess](data=data, info=ApiInfoResponse(info=info)).model_dump(),
                        status_code=201)


def no_content(info: str = "") -> JSONResponse:
    return JSONResponse(ApiResponse[None](data=None, info=ApiInfoResponse(info=info)).model_dump(), status_code=204)


def fail(
        title: str,
        msg: str,
        *,
        code: str = "",
        procedure: str = "",
        job: str = "",
        status: int = 400,
) -> JSONResponse:
    failure = FailureData(title=title, msg=msg, code=code, procedure=procedure, job=job)
    return JSONResponse(ApiResponse(error=failure).model_dump(), status_code=status)


def wrap_response(func: Callable[..., Awaitable[Any]]) -> Callable[..., Coroutine[Any, Any, JSONResponse]]:
    @wraps(func)
    async def _inner(*args, **kwargs) -> JSONResponse:
        try:
            result = await func(*args, **kwargs)
            status: int = 200
            data = result
            if isinstance(result, tuple) and len(result) == 2:
                data, status = result
            if status == 201:
                return created(data)
            if status == 204:
                return no_content()
            return ok(data)
        except Failure as exc:
            return JSONResponse(ApiResponse(error=exc.failure_data).model_dump(), status_code=500)

    return _inner
