# ontos/commands/agents.py
"""
AGENTS.md and .cursorrules generation command.

Generates AI assistant integration files per Spec v1.1.
"""

import os
import subprocess
import sys
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional, Tuple

import ontos


@dataclass
class AgentsOptions:
    """Configuration for agents command."""
    output_path: Optional[Path] = None
    force: bool = False
    format: str = "agents"  # agents|cursor
    all_formats: bool = False


# Template per Appendix A
AGENTS_TEMPLATE = '''# AGENTS.md

This project uses **Ontos** for documentation management.

Generated by Ontos v{ontos_version} on {generated_at}

## Ontos Activation
1. Run `ontos map` to regenerate the context map.
2. Read `Ontos_Context_Map.md` to understand the documentation graph.
3. Load only the relevant documents for the task.
4. Follow `depends_on` upward as needed.
5. Confirm: "Loaded: [ids]".

## Session End
1. Run `ontos log -e "slug"` to archive session work.
2. Fill in Goal, Key Decisions, Alternatives Considered, and Impacts.

## Quick Reference
| Command | Purpose |
|---------|---------|
| `ontos map` | Regenerate context map |
| `ontos agents` | Generate instruction files |
| `ontos doctor` | Health check and validation |
| `ontos log -e "slug"` | Archive session work |
| `ontos query <id>` | Find document by ID |

## Project Stats
- Doc Count: {doc_count}
- Last Updated: {last_updated}

## Core Invariants
- Do not edit `Ontos_Context_Map.md` manually; regenerate with `ontos map`.
- Do not edit `AGENTS.md` manually; regenerate with `ontos agents`.
- If a command fails, read the error message and avoid guessing fixes.

## Staleness
If `AGENTS.md` is older than the context map or logs, regenerate with `ontos agents`.
'''


def find_repo_root() -> Optional[Path]:
    """
    Find the repository root using 3-method fallback.
    
    1. Find .ontos.toml in current or parent dirs
    2. Use git rev-parse --show-toplevel
    3. Fall back to .git directory walk
    
    Returns:
        Path to repo root, or None if no repository found.
    """
    current = Path.cwd()
    
    # Method 1: Look for .ontos.toml
    for parent in [current] + list(current.parents):
        if (parent / ".ontos.toml").exists():
            return parent
    
    # Method 2: Try git rev-parse
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--show-toplevel"],
            capture_output=True,
            text=True,
            timeout=5
        )
        if result.returncode == 0:
            return Path(result.stdout.strip())
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass
    
    # Method 3: Look for .git
    for parent in [current] + list(current.parents):
        if (parent / ".git").exists():
            return parent
    
    # No repo found - return None instead of cwd to prevent arbitrary writes
    return None


def gather_stats(repo_root: Path) -> Tuple[str, str]:
    """
    Gather project stats for template.
    
    Returns:
        Tuple of (doc_count, last_updated)
    """
    doc_count = "Unknown"
    last_updated = "Unknown"
    
    try:
        # Load config to find docs directory
        config_path = repo_root / ".ontos.toml"
        docs_dir = repo_root / ".ontos-internal"  # Default
        logs_dir = repo_root / ".ontos-internal" / "logs"
        context_map = repo_root / "Ontos_Context_Map.md"
        
        if config_path.exists():
            try:
                from ontos.io.config import load_project_config
                config = load_project_config()
                docs_dir = repo_root / config.paths.docs_dir
                logs_dir = repo_root / config.paths.logs_dir
                context_map = repo_root / config.paths.context_map
            except Exception:
                pass  # Keep defaults
        
        # Count .md files (cap at 5000)
        count = 0
        if docs_dir.exists():
            for _ in docs_dir.rglob("*.md"):
                count += 1
                if count >= 5000:
                    doc_count = "5000+"
                    break
            else:
                doc_count = str(count)
        
        # Find max mtime
        mtimes = []
        for path in [context_map, config_path]:
            if path.exists():
                mtimes.append(path.stat().st_mtime)
        
        if logs_dir.exists():
            for log_file in logs_dir.glob("*.md"):
                mtimes.append(log_file.stat().st_mtime)
        
        if mtimes:
            max_mtime = max(mtimes)
            dt = datetime.fromtimestamp(max_mtime, tz=timezone.utc)
            last_updated = dt.strftime("%Y-%m-%d %H:%M:%S UTC")
    
    except Exception:
        pass  # Keep defaults on any error
    
    return doc_count, last_updated


def generate_agents_content() -> str:
    """Generate AGENTS.md content with stats."""
    repo_root = find_repo_root()
    doc_count, last_updated = gather_stats(repo_root)
    
    generated_at = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
    
    return AGENTS_TEMPLATE.format(
        ontos_version=ontos.__version__,
        generated_at=generated_at,
        doc_count=doc_count,
        last_updated=last_updated,
    )


def transform_to_cursorrules(agents_content: str) -> str:
    """
    Transform AGENTS.md content to .cursorrules format.
    
    Per Appendix B transform rules.
    """
    lines = agents_content.split('\n')
    result = []
    
    in_section = None
    skip_until_next_section = False
    
    for line in lines:
        # Title replacement
        if line.startswith('# AGENTS.md'):
            result.append('# Ontos Protocol for Cursor')
            continue
        
        # Section detection
        if line.startswith('## '):
            section_name = line[3:].strip()
            skip_until_next_section = False
            
            if section_name == 'Ontos Activation':
                result.append('')
                result.append('When the user says "Ontos" or "Activate Ontos":')
                result.append('')
                result.append('## Activation')
                in_section = 'activation'
                continue
            elif section_name == 'Session End':
                result.append('')
                result.append('When ending a session:')
                result.append('')
                result.append('## Session End')
                in_section = 'session_end'
                continue
            elif section_name in ('Quick Reference', 'Project Stats', 'Core Invariants', 'Staleness'):
                result.append(line)
                in_section = section_name.lower().replace(' ', '_')
                continue
            else:
                # Remove unknown sections
                skip_until_next_section = True
                in_section = None
                continue
        
        if skip_until_next_section:
            continue
        
        result.append(line)
    
    return '\n'.join(result)


def create_backup(path: Path) -> None:
    """Create .bak backup of existing file."""
    if path.exists():
        backup_path = path.with_suffix(path.suffix + '.bak')
        backup_path.write_text(path.read_text(encoding='utf-8'), encoding='utf-8')


def agents_command(options: AgentsOptions) -> Tuple[int, str]:
    """
    Generate AGENTS.md and/or .cursorrules files.
    
    Returns:
        Tuple of (exit_code, message)
    
    Exit Codes:
        0: Success
        1: File exists (use --force)
        2: Configuration error
    """
    repo_root = find_repo_root()
    if repo_root is None:
        return 2, "Error: No repository found. Run from within a git repository or Ontos project."
    
    messages = []
    
    # Determine what to generate
    generate_agents = options.format == "agents" or options.all_formats
    generate_cursor = options.format == "cursor" or options.all_formats
    
    # Generate AGENTS.md
    if generate_agents:
        agents_path = options.output_path or repo_root / "AGENTS.md"
        
        # Path safety validation
        try:
            resolved_output = agents_path.resolve()
            resolved_root = repo_root.resolve()
            resolved_output.relative_to(resolved_root)
        except ValueError:
            return 2, f"Error: Output path must be within repository root ({repo_root})"
        
        if agents_path.exists() and not options.force:
            return 1, f"AGENTS.md already exists at {agents_path}. Use --force to overwrite."
        
        try:
            if options.force and agents_path.exists():
                create_backup(agents_path)
            
            agents_path.parent.mkdir(parents=True, exist_ok=True)
            content = generate_agents_content()
            agents_path.write_text(content, encoding='utf-8')
            messages.append(f"Created {agents_path}")
        except Exception as e:
            return 2, f"Error writing AGENTS.md: {e}"
    
    # Generate .cursorrules
    if generate_cursor:
        cursor_path = repo_root / ".cursorrules"
        
        if cursor_path.exists() and not options.force:
            if generate_agents:
                # Already created AGENTS, warn about cursor
                messages.append(f".cursorrules already exists. Use --force to overwrite.")
            else:
                return 1, f".cursorrules already exists at {cursor_path}. Use --force to overwrite."
        else:
            try:
                if options.force and cursor_path.exists():
                    create_backup(cursor_path)
                
                agents_content = generate_agents_content()
                cursor_content = transform_to_cursorrules(agents_content)
                cursor_path.write_text(cursor_content, encoding='utf-8')
                messages.append(f"Created {cursor_path}")
            except Exception as e:
                return 2, f"Error writing .cursorrules: {e}"
    
    return 0, '\n'.join(messages) if messages else "No files generated"
