import copy
from typing import Any, Optional

from gentrace.providers.context import Context


class StepRun:
    def __init__(
            self,
            provider: str,
            invocation: str,
            elapsed_time: float,
            start_time: str,
            end_time: str,
            inputs: any,
            model_params: any,
            outputs: any,
            context: Optional[Context] = None,
            error: Optional[str] = None,
    ):
        self.provider = provider
        self.invocation = invocation
        self.elapsed_time = elapsed_time
        self.start_time = start_time
        self.end_time = end_time
        self.inputs = self._convert_to_dict(inputs)
        self.model_params = self._convert_to_dict(model_params)
        self.outputs = self._convert_to_dict(outputs)
        self.context = self._convert_to_dict(context or {})
        self.error = error

    def _convert_to_dict(self, obj: Any) -> Any:
        if hasattr(obj, '__dict__'):
            # Check for DynamicSchema Gentrace custom types
            if hasattr(obj, 'from_openapi_data_oapg'):
                # Primitive type
                if not hasattr(obj, 'items'):
                    return obj
                obj = {k: self._convert_to_dict(v) for k, v in obj.items()}
                return obj

            # Check for model_dump first (works for both v1 and v2)
            if hasattr(obj, 'model_dump'):
                return obj.model_dump()

            # Fallback to dict() if model_dump is not available
            elif hasattr(obj, 'dict'):
                return obj.dict()

            # If neither method is available, use vars() to extrace the object's attributes
            else:
                return vars(obj)
        elif isinstance(obj, dict):
            return {k: self._convert_to_dict(v) for k, v in obj.items()}
        elif isinstance(obj, list):
            return [self._convert_to_dict(item) for item in obj]
        return obj
