# File: src/noveler/application/services/stepwise_execution_service.py
# Purpose: Re-export base class and response wrapper from Domain layer
#          for backward-compatible application layer integration.
# Context: Acts as a bridge so legacy service imports continue to work
#          after moving BaseWritingStep to Domain layer.

from noveler.domain.interfaces.writing_step_protocol import BaseWritingStep
from noveler.domain.interfaces.writing_step_protocol import WritingStepResponse

import inspect
import time
from dataclasses import dataclass
from typing import Any


@dataclass
class WritingStepResponse:
    """Generic response container for stepwise services.

    Provides a tolerant constructor so legacy services can attach arbitrary
    metadata without raising errors (mirrors behaviour from the retired shim).
    """

    success: bool
    step_number: int
    step_name: str
    execution_time_ms: float = 0.0
    error_message: str | None = None
    data: dict[str, Any] | None = None
    metadata: dict[str, Any] | None = None

    def __init__(
        self,
        success: bool,
        step_number: int,
        step_name: str,
        execution_time_ms: float = 0.0,
        error_message: str | None = None,
        data: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
        **extra: Any,
    ) -> None:
        self.success = success
        self.step_number = step_number
        self.step_name = step_name
        self.execution_time_ms = execution_time_ms
        self.error_message = error_message
        self.data = data
        self.metadata = metadata or {}
        # Preserve unknown attributes for downstream compatibility.
        for key, value in extra.items():
            setattr(self, key, value)


class BaseWritingStep:
    """Minimal compatibility layer for legacy stepwise services.

    Legacy services expect to override `execute_step`, `get_step_name`, and
    `get_step_description`. The new shim wraps those methods and exposes the
    asynchronous `execute` coroutine needed by the updated use case.
    """

    STEP_NUMBER: int = -1
    STEP_NAME: str = "step"

    def __init__(
        self,
        step_number: int | None = None,
        step_name: str | None = None,
        **_: Any,
    ) -> None:
        self.step_number = step_number if step_number is not None else getattr(self, "STEP_NUMBER", -1)
        self.step_name = step_name if step_name is not None else getattr(self, "STEP_NAME", self.__class__.__name__)

    async def execute(
        self,
        episode_number: int,
        previous_results: dict[int, Any] | None = None,
    ) -> WritingStepResponse:
        """Async wrapper that delegates to the synchronous legacy implementation."""
        start_time = time.time()
        context = self._prepare_context(episode_number, previous_results or {})

        try:
            result = self.execute_step(context)
            if inspect.isawaitable(result):
                result = await result

            execution_time = (time.time() - start_time) * 1000

            if isinstance(result, WritingStepResponse):
                # Ensure timing metadata is populated for downstream consumers.
                if not getattr(result, "execution_time_ms", 0.0):
                    result.execution_time_ms = execution_time
                return result

            payload: dict[str, Any]
            if isinstance(result, dict):
                payload = result
            else:
                payload = {"result": result}

            return WritingStepResponse(
                success=True,
                step_number=self.step_number,
                step_name=self.step_name,
                execution_time_ms=execution_time,
                data=payload,
            )

        except Exception as exc:  # pragma: no cover - defensive compatibility path
            execution_time = (time.time() - start_time) * 1000
            return WritingStepResponse(
                success=False,
                step_number=self.step_number,
                step_name=self.step_name,
                execution_time_ms=execution_time,
                error_message=str(exc),
            )

    def get_step_name(self) -> str:
        """Return the human-readable step name."""
        return getattr(self, "STEP_NAME", self.step_name)

    def get_step_description(self) -> str:
        """Return the step description if provided by subclasses."""
        return getattr(self, "STEP_DESCRIPTION", "")

    def execute_step(self, context: dict[str, Any]) -> Any:  # pragma: no cover - abstract shim
        """Legacy synchronous hook implemented by subclasses."""
        raise NotImplementedError("execute_step must be implemented by subclasses.")

    def _prepare_context(
        self,
        episode_number: int,
        previous_results: dict[int, Any],
    ) -> dict[str, Any]:
        """Assemble the context dict expected by legacy services."""
        context: dict[str, Any] = {
            "episode_number": episode_number,
            "previous_results": previous_results,
        }

        # Attempt to recover commonly used data (e.g., project info) from prior steps.
        project = None
        for result in previous_results.values():
            if hasattr(result, "project_root"):
                project = result.project_root
                break
            if hasattr(result, "data") and isinstance(result.data, dict) and "project" in result.data:
                project = result.data["project"]
                break

        if project:
            context["project"] = project

        return context
