import utils.path_utils
from core.algo.schema import DebiasStrategy
from core.domain.schema import ProblemDomain
from core.policy.schema import Policy
from core.reasoning.schema import ReasoningMode

from core.algo.martingale import MartingaleStrategy
from core.domain.forecasting import Forecasting
from core.domain.changemyview import CMV
from core.domain.openreview import OpenReview
from core.reasoning.debate import SelfDebate
from core.reasoning.cot import ChainOfThought
from core.policy.apimodel import APIModel

from dataclasses import dataclass


@dataclass
class Setup:
    """
    A setup is a collection of a domain, reasoning mode, policy, and debias strategy.
    For performance reasons, objects may be stored as strings instead of instances.
    """
    identifier: str
    reasoning_mode: ReasoningMode | str
    domain: ProblemDomain | str
    policy: Policy | str
    algo: DebiasStrategy | str
    
    def __str__(self) -> str:
        assert "-" not in self.identifier, f"Identifier cannot contain '-': {self.identifier}"
        return f"{self.identifier}-" \
            f"{self.reasoning_mode if isinstance(self.reasoning_mode, str) else self.reasoning_mode.__class__.__name__}-" \
            f"{self.domain if isinstance(self.domain, str) else self.domain.__class__.__name__}-" \
            f"{self.policy if isinstance(self.policy, str) else self.policy.colloquial_name}-" \
            f"{self.algo if isinstance(self.algo, str) else self.algo.__class__.__name__}"
    
    @classmethod
    def from_str(cls, setup_str: str, instantiate_objects: bool = False) -> "Setup":
        """
        Parse a setup string into a Setup object.
        """
        # First parse the identifier, reasoning_mode, domain, and algo, since these don't contain any '-'
        parts = setup_str.split("-")
        identifier = parts[0]
        reasoning_mode_name = parts[1]
        domain_name = parts[2]
        policy_name = "-".join(parts[3:-1])
        algo_name = parts[-1]
        
        # Initialize the reasoning mode, domain, and algo
        if instantiate_objects:
            reasoning_mode = eval(f"{reasoning_mode_name}()")
            domain = eval(f"{domain_name}()")
            algo = eval(f"{algo_name}()")
        else:
            reasoning_mode = reasoning_mode_name
            domain = domain_name
            algo = algo_name
        
        return cls(identifier, reasoning_mode, domain, policy_name, algo)
