"""Agent session management for tracking exploration state."""

import uuid
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional

from emdash.agent.tools.base import ToolResult


@dataclass
class ExplorationStep:
    """Record of a single exploration step."""

    timestamp: datetime
    tool_name: str
    params: dict
    result_summary: dict
    entities_discovered: list[dict]  # List of {qualified_name, file_path, entity_type}


@dataclass
class AgentSession:
    """Tracks agent exploration state across multiple tool calls."""

    session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    started_at: datetime = field(default_factory=datetime.now)
    steps: list[ExplorationStep] = field(default_factory=list)
    discovered_entities: set[str] = field(default_factory=set)
    visited_files: set[str] = field(default_factory=set)
    current_focus: Optional[str] = None

    def record_action(
        self,
        tool_name: str,
        params: dict,
        result: ToolResult,
    ) -> None:
        """Record an exploration action.

        Args:
            tool_name: Name of the tool executed
            params: Parameters passed to the tool
            result: Result from the tool execution
        """
        entities = self._extract_entities(result)
        files = self._extract_files(result)

        step = ExplorationStep(
            timestamp=datetime.now(),
            tool_name=tool_name,
            params=params,
            result_summary=self._summarize_result(result),
            entities_discovered=entities,  # Now list of dicts
        )

        self.steps.append(step)

        # Update discovered entities (extract qualified names for the set)
        for entity in entities:
            if isinstance(entity, dict):
                qn = entity.get("qualified_name")
                if qn:
                    self.discovered_entities.add(qn)
            elif isinstance(entity, str):
                self.discovered_entities.add(entity)

        self.visited_files.update(files)

        # Update current focus based on result
        if result.success and entities:
            first = entities[0]
            if isinstance(first, dict):
                self.current_focus = first.get("qualified_name")
            else:
                self.current_focus = first

    def get_context_summary(self) -> dict:
        """Get a summary of exploration context for the agent.

        Returns:
            Dict with session state summary
        """
        return {
            "session_id": self.session_id,
            "steps_taken": len(self.steps),
            "entities_discovered": len(self.discovered_entities),
            "files_visited": list(self.visited_files)[:20],
            "current_focus": self.current_focus,
            "recent_actions": [
                {"tool": s.tool_name, "params": s.params}
                for s in self.steps[-5:]
            ],
        }

    def get_discovered_entities(self, limit: int = 50) -> list[str]:
        """Get list of discovered entities.

        Args:
            limit: Maximum number to return

        Returns:
            List of qualified entity names
        """
        return list(self.discovered_entities)[:limit]

    def reset(self) -> None:
        """Reset the session state."""
        self.steps = []
        self.discovered_entities = set()
        self.visited_files = set()
        self.current_focus = None

    def _extract_entities(self, result: ToolResult) -> list[dict]:
        """Extract entities from a result.

        Returns list of dicts with keys:
        - qualified_name: str
        - file_path: str (optional)
        - entity_type: str (optional)
        """
        entities = []

        if not result.success:
            return entities

        data = result.data

        # Extract from results list (semantic_search, text_search, etc.)
        if "results" in data:
            for item in data["results"]:
                if isinstance(item, dict):
                    qn = item.get("qualified_name")
                    if qn:
                        entities.append({
                            "qualified_name": qn,
                            "file_path": item.get("file_path"),
                            "entity_type": item.get("type") or item.get("entity_type"),
                        })

        # Extract from functions
        if "functions" in data:
            for func in data["functions"]:
                if isinstance(func, dict):
                    qn = func.get("qualified_name")
                    if qn:
                        entities.append({
                            "qualified_name": qn,
                            "file_path": func.get("file_path"),
                            "entity_type": "Function",
                        })

        # Extract from classes
        if "classes" in data:
            for cls in data["classes"]:
                if isinstance(cls, dict):
                    qn = cls.get("qualified_name")
                    if qn:
                        entities.append({
                            "qualified_name": qn,
                            "file_path": cls.get("file_path"),
                            "entity_type": "Class",
                        })

        # Extract from root_node (expand_node)
        if "root_node" in data:
            qn = data["root_node"].get("qualified_name")
            if qn:
                entities.append({
                    "qualified_name": qn,
                    "file_path": data["root_node"].get("file_path"),
                    "entity_type": data["root_node"].get("type"),
                })

        # Extract file path from read_file, write_to_file, apply_diff, etc.
        if "path" in data and not entities:
            file_path = data["path"]
            if self._is_relevant_file(file_path):
                entities.append({
                    "qualified_name": f"file:{file_path}",
                    "file_path": file_path,
                    "entity_type": "File",
                })

        # Extract from entries (list_files) - skip, too noisy
        # list_files is exploratory, not targeted investigation

        # Extract from matches (grep)
        if "matches" in data:
            seen_files = set()
            for match in data["matches"][:10]:  # Limit to top 10 matches
                if isinstance(match, dict):
                    file_path = match.get("file") or match.get("path")
                    if file_path and file_path not in seen_files and self._is_relevant_file(file_path):
                        seen_files.add(file_path)
                        entities.append({
                            "qualified_name": f"file:{file_path}",
                            "file_path": file_path,
                            "entity_type": "File",
                        })

        return entities[:50]  # Limit to prevent memory issues

    def _is_relevant_file(self, file_path: str) -> bool:
        """Check if a file path is relevant for context tracking.

        Filters out:
        - Empty or trivial paths (., .., etc.)
        - Log files
        - Build artifacts
        - Non-code files that aren't useful for context
        """
        if not file_path or file_path in (".", "..", "/"):
            return False

        # Skip log files and build artifacts
        skip_patterns = (
            ".log", "/logs/", "/log/",
            "/node_modules/", "/__pycache__/",
            "/dist/", "/build/", "/.git/",
            ".pyc", ".pyo", ".so", ".dll",
            "/coverage/", "/.nyc_output/",
        )
        lower_path = file_path.lower()
        if any(pattern in lower_path for pattern in skip_patterns):
            return False

        # Prefer source code files
        code_extensions = (
            ".py", ".ts", ".tsx", ".js", ".jsx",
            ".java", ".go", ".rs", ".rb", ".php",
            ".c", ".cpp", ".h", ".hpp", ".cs",
            ".swift", ".kt", ".scala", ".vue",
            ".json", ".yaml", ".yml", ".toml",
            ".md", ".txt", ".sql", ".graphql",
        )

        # Allow files with code extensions or no extension (could be scripts)
        has_extension = "." in file_path.split("/")[-1]
        if has_extension:
            return any(lower_path.endswith(ext) for ext in code_extensions)

        # Files without extension are allowed (could be Makefile, Dockerfile, etc.)
        return True

    def _extract_files(self, result: ToolResult) -> list[str]:
        """Extract file paths from a result."""
        files = []

        if not result.success:
            return files

        data = result.data

        # Extract from files list
        if "files" in data:
            for f in data["files"]:
                if isinstance(f, dict):
                    path = f.get("path") or f.get("file_path")
                    if path:
                        files.append(path)

        # Extract file_path from results
        if "results" in data:
            for item in data["results"]:
                if isinstance(item, dict):
                    path = item.get("file_path")
                    if path:
                        files.append(path)

        return files[:50]

    def _summarize_result(self, result: ToolResult) -> dict:
        """Create a condensed summary of a result."""
        if not result.success:
            return {"error": result.error}

        data = result.data
        summary = {}

        if "count" in data:
            summary["count"] = data["count"]
        elif "results" in data:
            summary["count"] = len(data["results"])

        if "results" in data and data["results"]:
            top = data["results"][0]
            if isinstance(top, dict):
                summary["top_result"] = {
                    "name": top.get("name"),
                    "type": top.get("type"),
                    "score": top.get("score"),
                }

        return summary
