import asyncio
from typing import TYPE_CHECKING, Any

from rdagent.components.workflow.rd_loop import RDLoop
from rdagent.core.exception import CoderError
from rdagent.log import rdagent_logger as logger
from rdagent.scenarios.rl.proposal.trace import RLTrace

if TYPE_CHECKING:
    from rdagent.scenarios.rl.scen.scenario import RLPostTrainingScen


class RLPostTrainingRDLoop(RDLoop):
    """RL post-training loop using standard RDLoop workflow"""

    skip_loop_error = (CoderError,)
    skip_loop_error_stepname = "feedback"
    withdraw_loop_error = ()

    def __init__(self, PROP_SETTING: "RLPostTrainingScen"):
        # Store rl-specific settings
        self.rl_rd_setting = PROP_SETTING
        # Initialize using base class
        super().__init__(PROP_SETTING)

        # Replace generic Trace with RLTrace for SOTA tracking
        self.trace = RLTrace(scen=PROP_SETTING)

    async def direct_exp_gen(self, prev_out: dict[str, Any]):
        """Generate RL post-training experiment"""
        exp = await self.hypothesis_gen.async_gen(self.trace, self)
        logger.log_object(exp.hypothesis, tag="hypothesis")
        logger.log_object(exp.sub_tasks, tag="experiment generation")
        return exp

    def coding(self, prev_out: dict[str, Any]):
        """Generate rl post-training code"""
        exp = prev_out["direct_exp_gen"]
        exp = self.coder.develop(exp)
        logger.log_object(exp.sub_workspace_list, tag="coder result")
        return exp

    def feedback(self, prev_out: dict[str, Any]):
        """Generate feedback for RL post-training experiment - always call LLM"""

        # Get experiment from available sources
        exp = prev_out.get("running") or prev_out.get("coding") or prev_out.get("direct_exp_gen")
        e = prev_out.get(self.EXCEPTION_KEY, None)
        feedback = self.summarizer.generate_feedback(exp, self.trace, exception=e)

        logger.log_object(feedback, tag="feedback")
        return feedback

    def record(self, prev_out: dict[str, Any]):
        """Record the experiment and feedback into trace"""
        feedback = prev_out["feedback"]
        exp = prev_out.get("running") or prev_out.get("coding") or prev_out.get("direct_exp_gen")
        self.trace.sync_dag_parent_and_hist((exp, feedback), prev_out[self.LOOP_IDX_KEY])

    def dump(self, path):
        """Skip dump if the loop contains unpicklable objects."""
        try:
            super().dump(path)
        except TypeError as e:
            logger.warning(f"Skip dump due to pickling error: {e}")
