"""
Base classes for two-phase agents.

Two-phase execution pattern:
1. Plan phase: Read-only analysis/classification (permission_mode="plan")
2. Execute phase: Apply changes after approval (permission_mode="acceptEdits")

This ensures deterministic behavior - the plan phase ALWAYS runs in read-only mode,
and execution ONLY happens after explicit approval.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any


@dataclass
class AgentPhaseResult:
    """Result from an agent phase.

    Attributes:
        success: Whether the phase completed successfully
        data: Arbitrary data to pass between phases
        messages: Human-readable status messages
        error: Error message if success is False
    """

    success: bool
    data: dict[str, Any] = field(default_factory=dict)
    messages: list[str] = field(default_factory=list)
    error: str | None = None


class TwoPhaseAgent(ABC):
    """Base class for two-phase agents (plan -> execute).

    Subclasses must implement:
    - plan(): Read-only analysis phase
    - execute(): Mutation phase (only after approval)

    The two-phase pattern ensures:
    1. Users always see what will happen before changes are made
    2. Execution is deterministic - same plan always produces same execution
    3. Approval can be automated for CI/CD or interactive for humans
    """

    @abstractmethod
    async def plan(self, **kwargs: Any) -> AgentPhaseResult:
        """Phase 1: Read-only analysis/classification.

        This phase MUST NOT make any changes to the codebase.
        It should:
        - Gather information (read files, fetch PR details, etc.)
        - Classify/categorize items
        - Build an execution plan

        Returns:
            AgentPhaseResult with classification data and plan summary
        """
        pass

    @abstractmethod
    async def execute(self, plan_result: AgentPhaseResult, **kwargs: Any) -> AgentPhaseResult:
        """Phase 2: Execute approved changes.

        This phase applies the changes identified in the plan phase.
        It should only be called after the user approves the plan.

        Args:
            plan_result: The result from the plan phase

        Returns:
            AgentPhaseResult with execution summary
        """
        pass

    async def run(self, auto_approve: bool = False, **kwargs: Any) -> AgentPhaseResult:
        """Run both phases with optional approval step.

        This is a convenience method that runs both phases sequentially.
        For more control, call plan() and execute() separately.

        Args:
            auto_approve: If True, skip approval prompt (for CI/CD)
            **kwargs: Additional arguments passed to both phases

        Returns:
            AgentPhaseResult from the execute phase, or plan phase if it failed
        """
        plan_result = await self.plan(**kwargs)

        if not plan_result.success:
            return plan_result

        # Note: Approval logic is handled by AgentRunner, not here
        # This method is for programmatic use where approval is pre-determined

        return await self.execute(plan_result, **kwargs)
