"""Different fix generation strategies.

Provides various approaches to generating fixes, each with
different prompts and parameters for diverse solutions.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Callable, Dict, Optional


@dataclass
class GenerationStrategy:
    """A fix generation strategy with prompt modifications."""

    name: str
    prompt_modifier: Callable[[str], str]
    default_temperature: float = 0.2
    description: str = ""

    def modify_prompt(self, base_prompt: str) -> str:
        """Modify the base prompt for this strategy.

        Args:
            base_prompt: Original prompt

        Returns:
            Modified prompt
        """
        return self.prompt_modifier(base_prompt)

    def __str__(self) -> str:
        return f"Strategy({self.name}, temp={self.default_temperature})"


def _identity(prompt: str) -> str:
    """Identity function for direct strategy."""
    return prompt


def _alternative_modifier(prompt: str) -> str:
    """Modifier for alternative approach strategy."""
    return prompt + """

IMPORTANT: Generate an ALTERNATIVE fix approach, different from the obvious solution.
Consider different angles like:
- Fixing at a different location (caller vs callee)
- Using a different pattern or idiom
- Addressing a deeper root cause
"""


def _minimal_modifier(prompt: str) -> str:
    """Modifier for minimal change strategy."""
    return prompt + """

CONSTRAINT: Generate the MINIMAL fix possible.
- Change as few lines as possible
- Do not add defensive code unless absolutely necessary
- Do not refactor or improve style
- Just fix the immediate issue
"""


def _type_safe_modifier(prompt: str) -> str:
    """Modifier for type-safe strategy."""
    return prompt + """

CONSTRAINT: Ensure complete type safety.
- Add type checks where values might be None
- Validate types before operations
- Use isinstance() or type guards where appropriate
- The fix must pass mypy --strict
"""


def _defensive_modifier(prompt: str) -> str:
    """Modifier for defensive programming strategy."""
    return prompt + """

APPROACH: Generate a defensive fix.
- Add input validation
- Add precondition checks
- Handle edge cases explicitly
- Fail fast with clear error messages when preconditions aren't met
"""


def _root_cause_modifier(prompt: str) -> str:
    """Modifier for root cause focused strategy."""
    return prompt + """

CONSTRAINT: Focus on addressing the ROOT CAUSE, not just the symptom.
- Do NOT use try/except to catch and ignore the error
- Do NOT add fallback default values that hide the issue
- Instead, fix WHY the error occurs in the first place
- Trace back to where the bad data originates
"""


def _refactor_modifier(prompt: str) -> str:
    """Modifier for refactoring strategy."""
    return prompt + """

APPROACH: If appropriate, refactor the problematic code.
- Extract complex logic into well-named functions
- Simplify conditionals
- Improve code clarity
- The fix should make the code more maintainable
"""


# Standard strategies available for use
STRATEGIES: Dict[str, GenerationStrategy] = {
    "direct": GenerationStrategy(
        name="direct",
        prompt_modifier=_identity,
        default_temperature=0.2,
        description="Direct fix without modifications",
    ),
    "alternative": GenerationStrategy(
        name="alternative",
        prompt_modifier=_alternative_modifier,
        default_temperature=0.7,
        description="Alternative approach, different from obvious solution",
    ),
    "minimal": GenerationStrategy(
        name="minimal",
        prompt_modifier=_minimal_modifier,
        default_temperature=0.1,
        description="Minimal change fix",
    ),
    "type_safe": GenerationStrategy(
        name="type_safe",
        prompt_modifier=_type_safe_modifier,
        default_temperature=0.3,
        description="Type-safe fix with explicit type checks",
    ),
    "defensive": GenerationStrategy(
        name="defensive",
        prompt_modifier=_defensive_modifier,
        default_temperature=0.4,
        description="Defensive fix with input validation",
    ),
    "root_cause": GenerationStrategy(
        name="root_cause",
        prompt_modifier=_root_cause_modifier,
        default_temperature=0.3,
        description="Focus on fixing root cause, not symptom",
    ),
    "refactor": GenerationStrategy(
        name="refactor",
        prompt_modifier=_refactor_modifier,
        default_temperature=0.5,
        description="Refactoring-based fix for cleaner code",
    ),
}


def get_strategy(name: str) -> Optional[GenerationStrategy]:
    """Get a strategy by name.

    Args:
        name: Strategy name

    Returns:
        GenerationStrategy or None if not found
    """
    return STRATEGIES.get(name)


def list_strategies() -> list[str]:
    """List available strategy names.

    Returns:
        List of strategy names
    """
    return list(STRATEGIES.keys())


def get_default_strategies() -> list[GenerationStrategy]:
    """Get the default set of strategies for the pipeline.

    Returns:
        List of default strategies
    """
    return [
        STRATEGIES["direct"],
        STRATEGIES["alternative"],
        STRATEGIES["minimal"],
        STRATEGIES["type_safe"],
        STRATEGIES["defensive"],
    ]


def create_custom_strategy(
    name: str,
    prompt_addition: str,
    temperature: float = 0.3,
    description: str = "",
) -> GenerationStrategy:
    """Create a custom strategy.

    Args:
        name: Strategy name
        prompt_addition: Text to add to prompt
        temperature: Default temperature
        description: Strategy description

    Returns:
        New GenerationStrategy
    """

    def modifier(prompt: str) -> str:
        return prompt + "\n\n" + prompt_addition

    return GenerationStrategy(
        name=name,
        prompt_modifier=modifier,
        default_temperature=temperature,
        description=description or f"Custom strategy: {name}",
    )


class StrategySelector:
    """Selects strategies based on context.

    Uses exception type and other context to choose
    appropriate fix generation strategies.
    """

    # Exception type to recommended strategies
    EXCEPTION_STRATEGIES: Dict[str, list[str]] = {
        "KeyError": ["direct", "defensive", "type_safe"],
        "TypeError": ["type_safe", "direct", "defensive"],
        "AttributeError": ["type_safe", "defensive", "root_cause"],
        "ValueError": ["defensive", "direct", "root_cause"],
        "IndexError": ["defensive", "direct", "minimal"],
        "NoneType": ["type_safe", "defensive", "root_cause"],
    }

    def __init__(self, available_strategies: Optional[Dict[str, GenerationStrategy]] = None):
        """Initialize the selector.

        Args:
            available_strategies: Available strategies (defaults to STRATEGIES)
        """
        self.strategies = available_strategies or STRATEGIES

    def select_for_exception(
        self,
        exception_type: str,
        limit: int = 5,
    ) -> list[GenerationStrategy]:
        """Select strategies appropriate for an exception type.

        Args:
            exception_type: Type of exception
            limit: Maximum strategies to return

        Returns:
            List of appropriate strategies
        """
        # Check if we have specific recommendations
        recommended_names = self.EXCEPTION_STRATEGIES.get(exception_type, [])

        result = []

        # Add recommended strategies first
        for name in recommended_names:
            if name in self.strategies:
                result.append(self.strategies[name])

        # Fill with default strategies
        for name in ["direct", "minimal", "alternative"]:
            if name in self.strategies and self.strategies[name] not in result:
                result.append(self.strategies[name])

        return result[:limit]

    def select_for_context(
        self,
        exception_type: str,
        has_tests: bool = False,
        is_api_code: bool = False,
        limit: int = 5,
    ) -> list[GenerationStrategy]:
        """Select strategies based on broader context.

        Args:
            exception_type: Type of exception
            has_tests: Whether tests are available
            is_api_code: Whether this is API/public code
            limit: Maximum strategies

        Returns:
            List of appropriate strategies
        """
        result = self.select_for_exception(exception_type, limit=limit + 2)

        # If we have tests, prioritize testable fixes
        if has_tests:
            # Move "direct" and "minimal" up since they're easier to test
            result.sort(key=lambda s: 0 if s.name in ("direct", "minimal") else 1)

        # For API code, prioritize defensive and type-safe
        if is_api_code:
            defensive_strategies = [s for s in result if s.name in ("defensive", "type_safe")]
            other_strategies = [s for s in result if s.name not in ("defensive", "type_safe")]
            result = defensive_strategies + other_strategies

        return result[:limit]
