from typing import Dict, Any
from api.assets import Asset
from api.io_port import InPort, OutPort


class ExecutionContext:
    """
    Runtime data container for a workflow execution.
    Stores produced assets for OutPorts and allows InPorts to retrieve them.
    """

    def __init__(self, execution_id: str | None = None):
        self.execution_id = execution_id

        # Key: OutPort, Value: Asset
        self._data: Dict[OutPort, Asset] = {}

        # Arbitrary metadata (optional)
        self._metadata: Dict[str, Any] = {}

    # Store produced data
    def put(self, output_port: OutPort, asset: Asset):
        """
        Register an asset produced by an OutPort.
        Also updates output_port.produced_asset.
        """
        self._data[output_port] = asset
        output_port.produced_asset = asset

    # Retrieve data
    def get(self, port):
        """
        Retrieve asset for:
        - OutPort → return its asset
        - InPort → return asset from its connected OutPort
        """
        if isinstance(port, OutPort):
            return self._data.get(port)

        if isinstance(port, InPort):
            if port.connected_output is None:
                return None
            return self._data.get(port.connected_output)

        raise TypeError("ExecutionContext.get() expects InPort or OutPort")

    # Check if data exists
    def has_data(self, port) -> bool:
        if isinstance(port, OutPort):
            return port in self._data

        if isinstance(port, InPort):
            if port.connected_output is None:
                return False
            return port.connected_output in self._data

        return False

    # Metadata management
    def set_meta(self, key: str, value: Any):
        self._metadata[key] = value

    def get_meta(self, key: str, default=None):
        return self._metadata.get(key, default)

    # Debug snapshot
    def status_view(self):
        """
        Lightweight snapshot of current execution state.
        Safe for GUI display.
        """
        return {
            "execution_id": self.execution_id,
            "assets": {
                f"{port.parent_task.name}.{port.name}": {
                    "dtype": port.dtype.__class__.__name__,
                    "preserved": asset.preserved,
                }
                for port, asset in self._data.items()
            },
            "metadata": self._metadata.copy(),
        }
