import os
import logging
from pathlib import Path
from typing import Text, List, Union, Optional, Dict, Any

logger = logging.getLogger(__name__)


class DPL:
    async def generate_thoughts(self, dst: 'DST', action: 'Action') -> Optional[Text]:
        """Generate thought for the next action. Return None if no thought is generated."""
        return None

    async def generate_actions(self, dst: 'DST') -> Optional[List[Text]]:
        """Generate next action names. Return None if no action is determined."""
        return None


class CompositeDPL(DPL):
    """
    Composite DPL - manages multiple DPL strategies internally.
    Implements true factory pattern: returns single object with internal strategy composition.
    """
    
    def __init__(self, strategies: List[DPL]):
        self.strategies = strategies
        logger.info(f"CompositeDPL initialized with {len(strategies)} strategies: "
                   f"{[s.__class__.__name__ for s in strategies]}")
    
    async def generate_thoughts(self, dst: 'DST', action: 'Action') -> Optional[Text]:
        """Iterate through strategies to generate thoughts, return first non-empty result."""
        for strategy in self.strategies:
            try:
                result = await strategy.generate_thoughts(dst, action)
                if result:
                    logger.debug(f"Thoughts generated by {strategy.__class__.__name__}")
                    return result
            except Exception as e:
                logger.error(f"Strategy {strategy.__class__.__name__} failed: {e}")
                continue
        return None
    
    async def generate_actions(self, dst: 'DST') -> Optional[List[Text]]:
        """Iterate through strategies to generate actions, return first non-empty result."""
        for strategy in self.strategies:
            try:
                result = await strategy.generate_actions(dst)
                if result:
                    logger.debug(f"Actions generated by {strategy.__class__.__name__}: {result}")
                    return result
            except Exception as e:
                logger.error(f"Strategy {strategy.__class__.__name__} failed: {e}")
                continue
        return None
    
    def add_strategy(self, strategy: DPL) -> None:
        """Dynamically add strategy."""
        self.strategies.append(strategy)
    
    def remove_strategy(self, strategy_class: type) -> bool:
        """Remove strategy of specified type."""
        for i, strategy in enumerate(self.strategies):
            if isinstance(strategy, strategy_class):
                del self.strategies[i]
                return True
        return False

class DPLFactory:
    @staticmethod
    def create(agent_config: Dict[Text, Any], path: Text) -> DPL:
        """
        Create single DPL object based on configuration.
        Returns CompositeDPL for multiple policies or single DPL for one policy.
        
        Args:
            agent_config: Agent configuration dictionary
            path: Path to policy files
            
        Returns:
            DPL: Single DPL object (may be CompositeDPL containing multiple strategies)
        """
        # Try to get policies from different configuration formats
        policies = []
        
        # New format: top-level "policies" (list)
        if "policies" in agent_config:
            policies = agent_config.get("policies", [])
                    
        if not policies:
            raise ValueError("No policies configured in configuration")
        
        # If only one policy, return single DPL instance
        if len(policies) == 1:
            policy = policies[0]
            return DPLFactory._create_single_dpl(
                policy.get('type'),
                policy,
                agent_config,
                path
            )
        
        # Multiple policies: create CompositeDPL
        strategies = []
        for policy in policies:
            policy_type = policy.get('type') or policy.get('name')
            strategy = DPLFactory._create_single_dpl(policy_type, policy, agent_config, path)
            strategies.append(strategy)
        
        return CompositeDPL(strategies)
    
    @staticmethod
    def _create_single_dpl(policy_type: str, policy: Dict, agent_config: Dict, path: str) -> DPL:
        """Create single DPL strategy instance."""
        if policy_type == 'trigger':
            from cota.dpl.trigger import TriggerDPL
            actions_config = agent_config.get("actions", {})
            return TriggerDPL(
                path=path,
                actions_config=actions_config
            )
            
        elif policy_type == 'match':
            from cota.dpl.match import MatchDPL
            return MatchDPL(
                path=path
            )
            
        elif policy_type in ('llm'):
            from cota.dpl.llm import LLMDPL
            # Support nested config structure
            policy_config = policy.get('config', policy)
            return LLMDPL(
                path=path,
                llms=policy_config.get('llms')
            )
        else:
            raise ValueError(f"Unknown dialogue policy type: {policy_type}")