from collections.abc import AsyncIterator
import contextlib
import logging

from pyodide.http import pyfetch  # type: ignore

from .common import (
    CONTENT_TYPE_HEADER,
    TRACE_HEADER,
    BinaryExecutorResult,
    Executor,
    ExecutorError,
    ExecutorResult,
    Headers,
    JsonExecutorResult,
    PlainTextExecutorResult,
)


_logger = logging.getLogger(__name__)


class PyodideExecutor(Executor):
    """`pyodide`-powered executor"""

    def __init__(
        self, endpoint: str, authorization: str | None = None
    ) -> None:
        super().__init__(
            variant="pyodide", endpoint=endpoint, authorization=authorization
        )

    @contextlib.asynccontextmanager
    async def _send(
        self, url: str, method: str, headers: Headers, body: bytes | None
    ) -> AsyncIterator[ExecutorResult]:
        res = await pyfetch(
            url=url,
            method=method,
            headers=headers,
            body=body,
        )
        status = res.status
        headers = res.js_response.headers
        trace = headers.get(TRACE_HEADER)
        ctype = headers.get(CONTENT_TYPE_HEADER)
        if JsonExecutorResult.is_eligible(ctype):
            text = await res.js_response.text()
            yield JsonExecutorResult(status=status, trace=trace, text=text)
        elif PlainTextExecutorResult.is_eligible(ctype):
            text = await res.js_response.text()
            yield PlainTextExecutorResult(
                status=status, trace=trace, reader=text
            )
        elif BinaryExecutorResult.is_eligible(ctype):
            data = await res.js_response.bytes()
            yield BinaryExecutorResult(status=status, trace=trace, reader=data)
        else:
            text = await res.js_response.text()
            raise ExecutorError(status=status, trace=trace, reason=text)
