from collections.abc import Generator
from typing import Any, cast, override

from core.app.apps.base_app_generate_response_converter import AppGenerateResponseConverter
from core.app.entities.task_entities import (
    AppStreamResponse,
    ErrorStreamResponse,
    NodeFinishStreamResponse,
    NodeStartStreamResponse,
    PingStreamResponse,
    WorkflowAppBlockingResponse,
    WorkflowAppPausedBlockingResponse,
    WorkflowAppStreamResponse,
)


class WorkflowAppGenerateResponseConverter(
    AppGenerateResponseConverter[WorkflowAppBlockingResponse | WorkflowAppPausedBlockingResponse]
):
    @classmethod
    @override
    def convert_blocking_full_response(
        cls, blocking_response: WorkflowAppBlockingResponse | WorkflowAppPausedBlockingResponse
    ) -> dict[str, Any]:
        """
        Convert blocking full response.
        :param blocking_response: blocking response
        :return:
        """
        return dict(blocking_response.model_dump())

    @classmethod
    @override
    def convert_blocking_simple_response(
        cls, blocking_response: WorkflowAppBlockingResponse | WorkflowAppPausedBlockingResponse
    ) -> dict[str, Any]:
        """
        Convert blocking simple response.
        :param blocking_response: blocking response
        :return:
        """
        return cls.convert_blocking_full_response(blocking_response)

    @classmethod
    @override
    def convert_stream_full_response(
        cls, stream_response: Generator[AppStreamResponse, None, None]
    ) -> Generator[dict[str, Any] | str, None, None]:
        """
        Convert stream full response.
        :param stream_response: stream response
        :return:
        """
        for chunk in stream_response:
            chunk = cast(WorkflowAppStreamResponse, chunk)
            sub_stream_response = chunk.stream_response

            match sub_stream_response:
                case PingStreamResponse():
                    yield "ping"
                    continue
                case ErrorStreamResponse():
                    response_chunk: dict[str, object] = {
                        "event": sub_stream_response.event.value,
                        "workflow_run_id": chunk.workflow_run_id,
                    }
                    data = cls._error_to_stream_response(sub_stream_response.err)
                    response_chunk.update(data)
                case _:
                    response_chunk = {
                        "event": sub_stream_response.event.value,
                        "workflow_run_id": chunk.workflow_run_id,
                    }
                    response_chunk.update(sub_stream_response.model_dump(mode="json"))

            yield response_chunk

    @classmethod
    @override
    def convert_stream_simple_response(
        cls, stream_response: Generator[AppStreamResponse, None, None]
    ) -> Generator[dict[str, Any] | str, None, None]:
        """
        Convert stream simple response.
        :param stream_response: stream response
        :return:
        """
        for chunk in stream_response:
            chunk = cast(WorkflowAppStreamResponse, chunk)
            sub_stream_response = chunk.stream_response

            match sub_stream_response:
                case PingStreamResponse():
                    yield "ping"
                    continue
                case ErrorStreamResponse():
                    response_chunk: dict[str, object] = {
                        "event": sub_stream_response.event.value,
                        "workflow_run_id": chunk.workflow_run_id,
                    }
                    data = cls._error_to_stream_response(sub_stream_response.err)
                    response_chunk.update(data)
                case NodeStartStreamResponse() | NodeFinishStreamResponse():
                    response_chunk = {
                        "event": sub_stream_response.event.value,
                        "workflow_run_id": chunk.workflow_run_id,
                    }
                    response_chunk.update(sub_stream_response.to_ignore_detail_dict())
                case _:
                    response_chunk = {
                        "event": sub_stream_response.event.value,
                        "workflow_run_id": chunk.workflow_run_id,
                    }
                    response_chunk.update(sub_stream_response.model_dump(mode="json"))

            yield response_chunk
