#!/usr/bin/env python3
"""
Base Agent Framework - Story 3
==============================
Common interface for all SCOUT and AUDIT agents.

Provides:
- Standard lifecycle (init, run, report)
- Registry and scoreboard integration
- Logging and metrics
- Result formatting
"""

import json
import asyncio
from abc import ABC, abstractmethod
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, field, asdict
from enum import Enum
import logging


class AgentTeam(Enum):
    SCOUT = "scout"
    AUDIT = "audit"


class AgentState(Enum):
    IDLE = "idle"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


@dataclass
class AgentConfig:
    """Configuration for an agent."""
    agent_id: str
    name: str
    team: AgentTeam
    description: str = ""
    model: str = "sonnet"  # haiku, sonnet, opus
    max_runtime_seconds: int = 3600
    retry_attempts: int = 3
    parallel_tasks: int = 3
    sources: List[str] = field(default_factory=list)
    output_dir: Optional[str] = None


@dataclass
class AgentResult:
    """Result from an agent run."""
    agent_id: str
    success: bool
    capabilities_found: List[Dict] = field(default_factory=list)
    capabilities_implemented: List[str] = field(default_factory=list)
    gaps_found: List[Dict] = field(default_factory=list)
    bugs_found: List[Dict] = field(default_factory=list)
    errors: List[str] = field(default_factory=list)
    metrics: Dict[str, Any] = field(default_factory=dict)
    runtime_seconds: float = 0.0
    timestamp: str = ""

    def to_dict(self) -> Dict:
        return asdict(self)


class BaseAgent(ABC):
    """
    Base class for all capability discovery agents.

    Subclass this for SCOUT and AUDIT agents:

        class DocsScraperAgent(BaseAgent):
            def __init__(self):
                super().__init__(AgentConfig(
                    agent_id="scout_s1",
                    name="Documentation Crawler",
                    team=AgentTeam.SCOUT,
                    description="Crawls Claude Code documentation"
                ))

            async def run(self) -> AgentResult:
                # Implementation
                pass
    """

    def __init__(self, config: AgentConfig):
        self.config = config
        self.state = AgentState.IDLE
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None
        self.result: Optional[AgentResult] = None

        # Setup logging
        self.logger = logging.getLogger(f"agent.{config.agent_id}")

        # Registry and scoreboard (lazy loaded)
        self._registry = None
        self._scoreboard = None

        # Output directory
        if config.output_dir:
            self.output_dir = Path(config.output_dir)
        else:
            self.output_dir = Path(__file__).parent.parent / "data" / "agent_outputs" / config.agent_id
        self.output_dir.mkdir(parents=True, exist_ok=True)

    @property
    def registry(self):
        """Lazy load registry."""
        if self._registry is None:
            from ..registry import CapabilityRegistry
            self._registry = CapabilityRegistry()
        return self._registry

    @property
    def scoreboard(self):
        """Lazy load scoreboard."""
        if self._scoreboard is None:
            from ..scoreboard import Scoreboard
            self._scoreboard = Scoreboard()
            self._scoreboard.register_agent(
                self.config.agent_id,
                self.config.team.value,
                self.config.name
            )
        return self._scoreboard

    @abstractmethod
    async def run(self) -> AgentResult:
        """
        Execute the agent's main task.

        Returns:
            AgentResult with findings
        """
        pass

    async def execute(self) -> AgentResult:
        """
        Full execution lifecycle with error handling.

        Use this instead of run() directly.
        """
        self.state = AgentState.RUNNING
        self.start_time = datetime.now()
        self.logger.info(f"Agent {self.config.agent_id} starting...")

        try:
            # Run with timeout
            self.result = await asyncio.wait_for(
                self.run(),
                timeout=self.config.max_runtime_seconds
            )
            self.state = AgentState.COMPLETED
            self.logger.info(f"Agent {self.config.agent_id} completed successfully")

        except asyncio.TimeoutError:
            self.state = AgentState.FAILED
            self.result = AgentResult(
                agent_id=self.config.agent_id,
                success=False,
                errors=[f"Timeout after {self.config.max_runtime_seconds}s"]
            )
            self.logger.error(f"Agent {self.config.agent_id} timed out")

        except Exception as e:
            self.state = AgentState.FAILED
            self.result = AgentResult(
                agent_id=self.config.agent_id,
                success=False,
                errors=[str(e)]
            )
            self.logger.exception(f"Agent {self.config.agent_id} failed: {e}")

        finally:
            self.end_time = datetime.now()
            if self.result:
                self.result.timestamp = self.end_time.isoformat()
                self.result.runtime_seconds = (self.end_time - self.start_time).total_seconds()

            # Save result
            self._save_result()

        return self.result

    def _save_result(self):
        """Save agent result to file."""
        if not self.result:
            return

        result_file = self.output_dir / f"result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        with open(result_file, 'w') as f:
            json.dump(self.result.to_dict(), f, indent=2)

    # === Helper Methods for Subclasses ===

    def report_capability(self, capability_id: str, name: str, category: str,
                          description: str, **kwargs) -> bool:
        """
        Report a discovered capability.

        Returns True if new, False if already exists.
        """
        from ..registry import Capability

        if self.registry.exists(capability_id):
            self.logger.debug(f"Capability {capability_id} already exists")
            return False

        cap = Capability(
            id=capability_id,
            name=name,
            category=category,
            description=description,
            discovered_by=self.config.agent_id,
            discovered_at=datetime.now().isoformat(),
            discovery_confidence=kwargs.get('confidence', 0.8),
            **{k: v for k, v in kwargs.items() if k != 'confidence'}
        )

        if self.registry.add_capability(cap):
            # Award points
            points = self.scoreboard.award_discovery(
                self.config.agent_id,
                capability_id,
                f"Discovered: {name}"
            )
            self.logger.info(f"Reported capability {capability_id} (+{points} pts)")
            return True

        return False

    def report_implementation(self, capability_id: str, integration_path: str,
                              is_first: bool = False) -> bool:
        """Report a capability implementation."""
        if not self.registry.exists(capability_id):
            self.logger.warning(f"Capability {capability_id} not found")
            return False

        success = self.registry.update_capability(
            capability_id,
            {
                'status': 'implemented',
                'genesis_integration': integration_path,
                'implemented_by': self.config.agent_id,
                'implemented_at': datetime.now().isoformat()
            },
            self.config.agent_id
        )

        if success:
            points = self.scoreboard.award_implementation(
                self.config.agent_id,
                capability_id,
                is_first=is_first
            )
            self.logger.info(f"Implemented {capability_id} (+{points} pts)")

        return success

    def report_gap(self, capability_id: str, description: str,
                   scout_agent: str = "") -> None:
        """Report a gap found by AUDIT (missed by SCOUT)."""
        self.scoreboard.award_gap_found(
            self.config.agent_id,
            capability_id,
            description
        )

        # Apply penalty to scout team
        if scout_agent:
            self.scoreboard.apply_penalty(
                scout_agent,
                "missed",
                capability_id,
                description
            )

        self.logger.info(f"Gap reported: {capability_id}")

    def report_bug(self, capability_id: str, description: str,
                   implementer: str = "") -> None:
        """Report a bug found by AUDIT."""
        self.scoreboard.award_bug_found(
            self.config.agent_id,
            capability_id,
            description
        )

        if implementer:
            self.scoreboard.apply_penalty(
                implementer,
                "broken",
                capability_id,
                description
            )

        self.logger.info(f"Bug reported: {capability_id}")

    def get_status(self) -> Dict[str, Any]:
        """Get current agent status."""
        return {
            "agent_id": self.config.agent_id,
            "name": self.config.name,
            "team": self.config.team.value,
            "state": self.state.value,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "runtime_seconds": (datetime.now() - self.start_time).total_seconds() if self.start_time else 0
        }


class ScoutAgent(BaseAgent):
    """Base class for SCOUT team agents."""

    def __init__(self, config: AgentConfig):
        config.team = AgentTeam.SCOUT
        super().__init__(config)


class AuditAgent(BaseAgent):
    """Base class for AUDIT team agents."""

    def __init__(self, config: AgentConfig):
        config.team = AgentTeam.AUDIT
        super().__init__(config)
