"""Project analyzer using Claude Agent SDK.

This module provides the ProjectAnalyzer class which uses Claude Agent SDK
to analyze a project and generate wiki documentation. The agent uses the
built-in Write tool to create markdown files directly in the .wiki directory.
TodoWrite tool is enabled to help the agent track progress.
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from rich.console import Console, Group
from rich.live import Live
from rich.panel import Panel
from rich.text import Text

from ..events import EventBus

if TYPE_CHECKING:
    from claude_agent_sdk import AssistantMessage
from ..storage.cache import WikiCacheManager
from ..tools.mcp_server import TOOL_NAMES, create_analysis_tools
from ..tui.components import StatusDisplay, TaskListDisplay, TaskTracker

console = Console()

# System prompt for the agent - agent uses Write tool directly
SYSTEM_PROMPT = """# Role
Senior software architect writing Wiki documentation for developers.

## Task Management
Use the TodoWrite tool to plan and track your progress. First explore the
codebase, then create a todo list based on what you find. Tailor the
documentation structure to this specific project's needs.

Update task status as you work through them.

---

# Required Output

Output directory: `.wiki/`

These two files are mandatory:
- `overview.md` - Project intro + architecture diagram
- `architecture.md` - Design decisions + patterns + data flow

Beyond these, create whatever documentation makes sense for this project:
- Module docs, subsystem docs, guides, etc.
- Structure should reflect the actual project structure
- Skip sections that don't apply; add sections that do

---

# DISCOVER Phase

Tools available:
- `list_modules` → Get project structure
- `get_module_info` → Understand each module
- `get_imports` → Map dependencies
- `get_class_hierarchy` → Trace inheritance
- `get_function_signature` → Get accurate signatures
- `get_function_calls` / `get_callers` → Trace data flow
- `Read` → Deep dive into source files

Use these tools to understand the codebase before writing documentation.

---

# Page Templates

## overview.md
- One-sentence description (what)
- Value proposition (why)
- Architecture diagram (mermaid)
- Quick navigation links to other wiki pages

## architecture.md
- Design philosophy (file:line)
- Architecture layers + examples
- Design patterns + locations (file:line)
- Data flow description

## Other pages (as needed)
- Purpose (problem solved)
- Mental model
- Key APIs (signature + file:line)
- Core call graph

---

# Citation Rules

## Allowed Sources
- Source code (`.py`, `.js`, `.ts`, `.go`, etc.)
- Config files (`pyproject.toml`, `package.json`, etc.)
- Docs (`README.md`, `docs/`, comments, docstrings)

## Forbidden Sources
- `.wiki/` folder (generated, not source)
- Training knowledge about similar projects
- Assumptions or guesses

## Citation Format (MANDATORY)
```
✅ "The `analyze()` function at `src/analyzer.py:42` processes..."
❌ "The analyzer processes data..."
```

## Uncertainty Handling
- Cannot find evidence? → Do NOT write it
- Partial evidence? → "Based on code at X, this appears to..."
- Uncertain? → Use "likely", "probably" with citations

---

# Mermaid Quick Reference

**ALWAYS use mermaid for diagrams. NEVER use ASCII art or text-based diagrams.**

## Valid Types
| Type | Use Case |
|------|----------|
| `flowchart TB/LR` | Components, data flow (default choice) |
| `sequenceDiagram` | API calls, service interactions |
| `classDiagram` | Class structures, inheritance |
| `stateDiagram` | State machines |
| `erDiagram` | Database schemas |
| `mindmap` | Hierarchical concepts |

## Invalid Types (NEVER USE)
`layered`, `component`, `deployment`, `package`, `c4`

## Syntax Rule
Always quote labels/names with special chars:
```
✅ A["Process Data"] --> B["Handle (async)"]
❌ A[Process Data] --> B[Handle (async)]
✅ A->>"Opt": update
❌ A->>Opt: update
```

---

# Writing Principles

| Do | Don't |
|----|-------|
| Specific with file:line | Vague ("handles data") |
| Explain WHY | Just list WHAT |
| Include gotchas | State only happy path |
| Use diagrams | Walls of text |
| Adapt to the project | Force arbitrary structure |

---

Begin analysis now. Explore first, plan with TodoWrite, then document."""


class ProjectAnalyzer:
    """Analyze a project using Claude Agent SDK and generate wiki documentation.

    The agent uses the built-in Write tool to create markdown files directly
    in the .wiki directory. File writes are restricted to only allow operations
    within the .wiki directory.
    """

    def __init__(self, project_path: Path):
        """Initialize analyzer.

        Args:
            project_path: Path to the project to analyze
        """
        self.project_path = project_path.resolve()
        self.project_root = str(self.project_path)
        self._wiki_dir: Path | None = None

        # Initialize event system and TUI components
        self._event_bus = EventBus()
        self._task_tracker = TaskTracker(self._event_bus)
        self._task_list_display = TaskListDisplay(self._event_bus)
        self._status_display = StatusDisplay(self._event_bus)

        # Current status message (for live display)
        self._current_status = "Starting analysis..."

    def _create_permission_handler(self, wiki_dir: Path):
        """Create a permission handler that restricts writes to .wiki directory.

        Args:
            wiki_dir: Path to the .wiki directory

        Returns:
            Async permission handler function
        """
        from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny

        wiki_dir_resolved = wiki_dir.resolve()

        async def can_use_tool(
            tool_name: str,
            input_data: dict,
            _context: dict,  # noqa: ARG001 - Required by SDK signature
        ) -> PermissionResultAllow | PermissionResultDeny:
            """Permission handler that restricts Write/Edit to .wiki directory."""
            if tool_name in ("Write", "Edit"):
                file_path = input_data.get("file_path", "")

                # Resolve the target path
                if not file_path:
                    return PermissionResultDeny(
                        message="No file path specified",
                        interrupt=False,
                    )

                # Handle relative paths
                target_path = Path(file_path)
                if not target_path.is_absolute():
                    target_path = self.project_path / target_path

                try:
                    target_path = target_path.resolve()
                except Exception:
                    return PermissionResultDeny(
                        message=f"Invalid file path: {file_path}",
                        interrupt=False,
                    )

                # Check if the path is within .wiki directory
                try:
                    target_path.relative_to(wiki_dir_resolved)
                except ValueError:
                    # Path is outside .wiki directory - redirect or deny
                    # Redirect to .wiki directory by extracting just the filename
                    filename = target_path.name
                    safe_path = wiki_dir_resolved / filename

                    console.print(
                        f"[yellow]Redirecting write:[/yellow] "
                        f"{file_path} -> {safe_path.relative_to(self.project_path)}"
                    )

                    return PermissionResultAllow(
                        updated_input={**input_data, "file_path": str(safe_path)}
                    )

                # Path is within .wiki - allow it
                return PermissionResultAllow(updated_input=input_data)

            # Allow all other tools
            return PermissionResultAllow(updated_input=input_data)

        return can_use_tool

    async def analyze(
        self,
        force: bool = False,
        wiki_dir: Path | None = None,
    ) -> list[Path]:
        """Analyze the project and generate wiki pages.

        Args:
            force: Force re-analysis even if cache exists
            wiki_dir: Custom wiki directory

        Returns:
            List of paths to generated wiki pages
        """
        self._wiki_dir = (wiki_dir or (self.project_path / ".wiki")).resolve()
        cache_manager = WikiCacheManager(self.project_path, wiki_dir)

        # Check cache
        if not force and cache_manager.is_cached():
            console.print("[yellow]Loading from cache...[/yellow]")
            cached = cache_manager.load_cache()
            if cached and cached.pages:
                # Return existing page paths
                return [cache_manager.wiki_dir / p.path for p in cached.pages]

        # Ensure wiki directory exists
        self._wiki_dir.mkdir(parents=True, exist_ok=True)

        # Analyze using Claude Agent SDK with MCP tools and Write tool
        console.print("[cyan]Analyzing project with Claude Agent...[/cyan]")
        page_paths = await self._analyze_with_agent()

        # Cache the generated pages
        if page_paths:
            # Load pages from disk for caching
            pages = cache_manager.load_pages()
            if not pages:
                # Create WikiPage objects from disk
                from datetime import datetime

                from ..storage.models import WikiPage

                pages = []
                for path in page_paths:
                    if path.exists():
                        content = path.read_text(encoding="utf-8")
                        rel_path = str(path.relative_to(cache_manager.wiki_dir))
                        title = self._extract_title(content, rel_path)
                        pages.append(
                            WikiPage(
                                title=title,
                                content=content,
                                path=rel_path,
                                created_at=datetime.now(),
                                updated_at=datetime.now(),
                            )
                        )
                cache_manager.save_pages(pages)

            console.print(f"[green]✓[/green] Generated {len(page_paths)} wiki pages")
        else:
            console.print("[red]✗[/red] No pages generated")

        return page_paths

    def _build_display(self) -> Group:
        """Build the live display with task list and status.

        Returns:
            Rich Group containing task panel and status panel
        """
        # Task list panel (above)
        task_panel = self._task_list_display.render(self._task_tracker.tasks)

        # Status panel (below) - shows current action
        status_text = Text(self._current_status, style="cyan")
        status_panel = Panel(
            status_text,
            title="[bold]Status[/bold]",
            border_style="green",
        )

        return Group(task_panel, status_panel)

    async def _analyze_with_agent(self) -> list[Path]:
        """Use Claude Agent SDK to analyze project and write wiki pages.

        The agent uses the Write tool to create files directly. We use
        a permission handler to restrict writes to the .wiki directory.

        Returns:
            List of paths to generated wiki pages
        """
        from claude_agent_sdk import (
            AssistantMessage,
            ClaudeAgentOptions,
            ClaudeSDKClient,
            ResultMessage,
        )

        # Create MCP server with analysis tools
        mcp_server = create_analysis_tools(self.project_path)

        # Create permission handler to restrict writes to .wiki
        permission_handler = self._create_permission_handler(self._wiki_dir)

        # Configure agent options with Write and TodoWrite tools enabled
        options = ClaudeAgentOptions(
            system_prompt=SYSTEM_PROMPT,
            permission_mode="default",
            cwd=self.project_root,
            mcp_servers={"analyzer": mcp_server},
            allowed_tools=["Read", "Write", "TodoWrite", *TOOL_NAMES],
            can_use_tool=permission_handler,
        )

        # Build the analysis prompt
        prompt = self._build_analysis_prompt()

        # Track generated files
        generated_files: list[Path] = []

        # Use Live display for real-time updates
        with Live(
            self._build_display(),
            console=console,
            refresh_per_second=4,
            transient=False,
        ) as live:
            async with ClaudeSDKClient(options=options) as client:
                await client.query(prompt)

                async for msg in client.receive_response():
                    if isinstance(msg, AssistantMessage):
                        await self._handle_assistant_message(
                            msg, live, generated_files
                        )
                    elif isinstance(msg, ResultMessage):
                        pass  # Final result message

        # Print task summary using display component
        self._status_display.print_summary(self._task_tracker.tasks)
        console.print("[green]  - Analysis complete[/green]")

        # Also scan wiki directory for any files that were written
        if self._wiki_dir.exists():
            for md_file in self._wiki_dir.rglob("*.md"):
                if md_file not in generated_files:
                    generated_files.append(md_file)

        return generated_files

    async def _handle_assistant_message(
        self,
        msg: AssistantMessage,
        live: Live,
        generated_files: list[Path],
    ) -> None:
        """Handle an assistant message from the agent.

        This method updates both the task list and status display.

        Args:
            msg: The assistant message to handle
            live: Rich Live object for display updates
            generated_files: List to append generated file paths to
        """
        from claude_agent_sdk import TextBlock, ToolUseBlock

        for block in msg.content:
            if isinstance(block, TextBlock):
                # Update status based on current task
                current = self._task_tracker.get_current_task()
                if current:
                    self._current_status = current.active_form or current.content
                else:
                    stats = self._task_tracker.get_stats()
                    if stats["total"] > 0 and stats["completed"] == stats["total"]:
                        self._current_status = "All tasks completed"
                    else:
                        self._current_status = "Processing..."

                live.update(self._build_display())

            elif isinstance(block, ToolUseBlock):
                tool_name = block.name
                tool_input = block.input or {}

                # Handle TodoWrite specially - update task tracker
                if tool_name == "TodoWrite":
                    todos = tool_input.get("todos", [])
                    self._task_tracker.update_from_todowrite(todos)

                    # Update status based on current task
                    current = self._task_tracker.get_current_task()
                    if current:
                        self._current_status = current.active_form or current.content
                    else:
                        self._current_status = "Processing..."
                else:
                    # Update status with tool action
                    self._current_status = self._status_display.format_tool_message(
                        tool_name, tool_input
                    )

                    # Track written files
                    if tool_name == "Write":
                        file_path = tool_input.get("file_path", "")
                        if file_path:
                            generated_files.append(Path(file_path))

                live.update(self._build_display())

    def _build_analysis_prompt(self) -> str:
        """Build the analysis prompt with project context.

        Returns:
            Analysis prompt string
        """
        wiki_path = (
            self._wiki_dir.relative_to(self.project_path)
            if self._wiki_dir
            else Path(".wiki")
        )

        prompt_parts = [
            f"# Project: {self.project_path.name}",
            f"\nPath: `{self.project_root}`",
            f"\nWiki output directory: `{wiki_path}/`",
            "",
            "Analyze this project and generate comprehensive Wiki documentation.",
            "",
            "Instructions:",
            "1. Use analysis tools (list_modules, get_module_info, etc.) to explore",
            "2. Use the Read tool to examine source files for deep understanding",
            "3. Use the Write tool to create wiki pages in the `.wiki/` directory",
            "4. Each page should be a separate file (e.g. `.wiki/overview.md`)",
            "5. Module pages should go in `.wiki/modules/<module-name>.md`",
            "6. Include file:line references for all technical claims",
            "7. Use mermaid diagrams for architecture visualization",
            "",
            "Start by exploring the project structure, then write the documentation.",
        ]

        return "\n".join(prompt_parts)

    def _extract_title(self, content: str, filename: str) -> str:
        """Extract title from markdown content.

        Args:
            content: Markdown content
            filename: Fallback filename

        Returns:
            Extracted or generated title
        """
        import re

        # Look for first # heading
        heading_match = re.search(r"^#\s+(.+)$", content, re.MULTILINE)
        if heading_match:
            return heading_match.group(1).strip()

        # Generate title from filename
        title = filename.replace(".md", "").replace("-", " ").replace("_", " ")
        return title.title()
