from wire.response import Response, HTMLResponse, JsonResponse, PlainTextResponse
from wire.request import Request
from wire.static import StaticFiles
from . import Pluggable
import inspect
import typing
import pydantic


class Alternator(Pluggable):

    async def __call__(self, req: Request, func: typing.Callable[[], typing.Any], params: dict, deps: list) -> Response:
        # if function is none, return
        if func is None:
            return PlainTextResponse("Not Found", 404)
        # arguments
        arguments: dict = dict()
        # get args of function
        args = [(name, typ.annotation) for name, typ in list(inspect.signature(func).parameters.items())]

        # replace arg with request object, if arg[0][1] is instance of Request
        for arg in args:
            if arg[1] == Request:
                arguments[arg[0]] = req
            elif issubclass(arg[1], pydantic.BaseModel):
                cls = arg[1]
                arguments[arg[0]] = cls(**(await req.body()))

        # update arguments with params
        arguments.update(params)


        if inspect.iscoroutinefunction(func):
            response: typing.Union[Response, typing.Any] = await func(**arguments)
            if not isinstance(response, Response):
                if type(response) == str:
                    response = PlainTextResponse(response, 200)
                elif type(response) == dict:
                    response = JsonResponse(response, 200)
            return response
        
        # static files
        elif isinstance(func, StaticFiles):
            resp: Response = await func(arguments["filename"])
            return resp

        # sync function
        else:
            response: typing.Union[Response, typing.Any] = func(**arguments)
            if not isinstance(response, Response):
                if type(response) == str:
                    response = PlainTextResponse(response, 200)
                elif type(response) == dict:
                    response = JsonResponse(response, 200)
            return response
        

        
        