"""
Umbra - The Shadow Architect

Main entry point and CLI interface.
"""

import os
import signal
import sys
import time
from pathlib import Path

import click
from dotenv import load_dotenv
from rich.console import Console
from rich.panel import Panel

from umbra.agents.orchestrator import build_graph
from umbra.agents.state import INITIAL_DIAGRAM
from umbra.agents.writer import load_current_mermaid
from umbra.watcher import FileChangeEvent, start_watching

# Load environment variables
load_dotenv()

console = Console(force_terminal=True)


@click.group()
@click.version_option(version="0.1.0")
def cli():
    """Umbra - The Shadow Architect

    A living architecture diagram that updates in real-time.
    """
    pass


def do_initial_scan(path: str, output_file: str, graph) -> str:
    """Scan all Python files and return the generated mermaid diagram."""
    from datetime import datetime
    from umbra.agents.summarizer import generate_summary
    
    console.print(f"\n[cyan]Initial scan of project...[/cyan]")
    
    # Find all Python files
    python_files = list(Path(path).rglob("*.py"))
    
    # Filter out venv, __pycache__, etc.
    ignore_patterns = {"__pycache__", ".git", ".venv", "venv", "node_modules", ".pytest_cache", "test", "tests"}
    python_files = [
        f for f in python_files 
        if not any(p in f.parts for p in ignore_patterns)
    ]
    
    console.print(f"[dim]Found {len(python_files)} Python files to analyze[/dim]")
    
    if not python_files:
        return INITIAL_DIAGRAM
    
    # Initialize diagram
    current_mermaid = INITIAL_DIAGRAM
    
    # Process each file
    for i, file_path in enumerate(python_files, 1):
        console.print(f"[dim]({i}/{len(python_files)}) {file_path.name}[/dim]", end=" ")
        
        try:
            content = file_path.read_text(encoding="utf-8", errors="ignore")
            
            result = graph.invoke({
                "file_path": str(file_path),
                "file_content": content,
                "diff": f"Initial scan: {file_path.name}",
                "current_mermaid": current_mermaid,
                "retry_count": 0,
            })
            
            # Update current diagram if changed
            if result.get("updated_mermaid"):
                current_mermaid = result["updated_mermaid"]
                console.print("[green]OK[/green]")
            else:
                console.print("[dim]skip[/dim]")
                
        except Exception as e:
            console.print(f"[red]error[/red]")
    
    # Generate summary
    console.print("\n[cyan]Generating project summary...[/cyan]")
    summary = generate_summary(path, current_mermaid, python_files)
    
    # Write initial diagram with summary
    final_content = f"""# Live Architecture

> **Auto-generated by Umbra** - Do not edit manually
> Last updated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
> Scanned: {len(python_files)} files

## Project Summary

{summary}

## System Overview

```mermaid
{current_mermaid}
```

## Recent Changes

| Time | File | Change |
|------|------|--------|
| {datetime.now().strftime("%H:%M")} | initial | Full project scan |
"""
    
    Path(output_file).parent.mkdir(parents=True, exist_ok=True)
    Path(output_file).write_text(final_content, encoding="utf-8")
    console.print(f"[green]Initial scan complete![/green]\n")
    
    return current_mermaid


@cli.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--verbose", "-v", is_flag=True, help="Enable debug logging")
@click.option(
    "--output",
    "-o",
    default=None,
    help="Output file path (default: ./output/LIVE_ARCHITECTURE.md)",
)
@click.option(
    "--debounce",
    "-d",
    default=2.0,
    type=float,
    help="Debounce delay in seconds (default: 2.0)",
)
@click.option(
    "--no-scan",
    is_flag=True,
    help="Skip initial project scan",
)
def watch(path: str, verbose: bool, output: str | None, debounce: float, no_scan: bool):
    """Watch a directory for Python file changes."""
    # Set output path in environment if provided
    if output:
        os.environ["OUTPUT_FILE"] = output

    output_file = os.getenv("OUTPUT_FILE", "./output/LIVE_ARCHITECTURE.md")

    # Check for API key
    if not os.getenv("GOOGLE_API_KEY"):
        console.print(
            "[red]ERROR: GOOGLE_API_KEY not set. Please set it in .env or environment.[/red]"
        )
        sys.exit(1)

    # Display startup banner
    console.print(
        Panel.fit(
            "[bold cyan]UMBRA[/bold cyan] - The Shadow Architect\n"
            f"Watching: [green]{Path(path).absolute()}[/green]\n"
            f"Output: [yellow]{output_file}[/yellow]\n"
            f"Model: [dim]{os.getenv('GEMINI_MODEL', 'models/gemini-flash-latest')}[/dim]",
            border_style="cyan",
        )
    )

    # Build the graph
    graph = build_graph()
    
    # Ensure output directory exists
    Path(output_file).parent.mkdir(parents=True, exist_ok=True)

    # Initial scan (unless --no-scan)
    if not no_scan:
        do_initial_scan(path, output_file, graph)
    elif not Path(output_file).exists():
        # Create empty diagram if no scan and file doesn't exist
        console.print("[dim]Creating initial architecture file...[/dim]")
        Path(output_file).write_text(
            f"""# Live Architecture

> **Auto-generated by Umbra** - Do not edit manually
> Last updated: Starting...

## System Overview

```mermaid
{INITIAL_DIAGRAM}
```

## Recent Changes

| Time | File | Change |
|------|------|--------|
""",
            encoding="utf-8",
        )

    def process_change(event: FileChangeEvent):
        """Process a file change event through the graph (sync wrapper)."""
        console.print(f"\n[bold]>> Change detected:[/bold] {event.file_path.name}")

        try:
            # Read file content
            if event.file_path.exists():
                content = event.file_path.read_text(encoding="utf-8")
            else:
                content = ""
                console.print("[dim]   -> File deleted, skipping analysis[/dim]")
                return

            # Load current architecture
            current_mermaid = load_current_mermaid(output_file)

            # Invoke the graph synchronously
            result = graph.invoke(
                {
                    "file_path": str(event.file_path),
                    "file_content": content,
                    "diff": event.diff,
                    "current_mermaid": current_mermaid,
                    "retry_count": 0,
                }
            )

            if verbose:
                if result.get("analysis_result"):
                    ar = result["analysis_result"]
                    console.print(f"[dim]   Analysis: {ar.reasoning}[/dim]")

        except Exception as e:
            console.print(f"[red]   ERROR: {e}[/red]")
            if verbose:
                import traceback
                traceback.print_exc()

    def on_file_change(event: FileChangeEvent):
        """Callback for file changes."""
        process_change(event)

    # Setup graceful shutdown
    watcher = None

    def signal_handler(sig, frame):
        console.print("\n[yellow]Shutting down gracefully...[/yellow]")
        if watcher:
            watcher.stop()
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    # Start watching
    console.print("[dim]Watching for changes... (CTRL+C to stop)[/dim]\n")

    watcher = start_watching(
        path=path,
        callback=on_file_change,
        debounce_seconds=debounce,
    )

    # Keep alive
    try:
        while watcher.is_running():
            time.sleep(0.5)
    except KeyboardInterrupt:
        pass
    finally:
        if watcher:
            watcher.stop()


@cli.command()
@click.option(
    "--output",
    "-o",
    default="./output/LIVE_ARCHITECTURE.md",
    help="Output file path",
)
def init(output: str):
    """Initialize a new architecture diagram."""
    output_path = Path(output)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    if output_path.exists():
        if not click.confirm(f"{output} already exists. Overwrite?"):
            console.print("[yellow]Aborted.[/yellow]")
            return

    output_path.write_text(
        f"""# Live Architecture

> **Auto-generated by Umbra** - Do not edit manually
> Last updated: Initialized

## System Overview

```mermaid
{INITIAL_DIAGRAM}
```

## Recent Changes

| Time | File | Change |
|------|------|--------|
""",
        encoding="utf-8",
    )

    console.print(f"[green]OK: Created {output}[/green]")
    console.print("\nNext steps:")
    console.print(f"  1. Open {output} in VS Code with Mermaid preview")
    console.print("  2. Run: [cyan]umbra watch .[/cyan]")


@cli.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option(
    "--output",
    "-o",
    default="./output/LIVE_ARCHITECTURE.md",
    help="Output file path",
)
def scan(path: str, output: str):
    """Scan an existing project and generate architecture diagram."""
    from umbra.agents.orchestrator import build_graph
    from umbra.agents.state import INITIAL_DIAGRAM
    
    # Check for API key
    if not os.getenv("GOOGLE_API_KEY"):
        console.print(
            "[red]ERROR: GOOGLE_API_KEY not set. Please set it in .env or environment.[/red]"
        )
        sys.exit(1)
    
    console.print(f"[cyan]Scanning project: {Path(path).absolute()}[/cyan]")
    
    # Find all Python files
    python_files = list(Path(path).rglob("*.py"))
    
    # Filter out venv, __pycache__, etc.
    ignore_patterns = {"__pycache__", ".git", ".venv", "venv", "node_modules", ".pytest_cache"}
    python_files = [
        f for f in python_files 
        if not any(p in f.parts for p in ignore_patterns)
    ]
    
    console.print(f"[dim]Found {len(python_files)} Python files[/dim]")
    
    if not python_files:
        console.print("[yellow]No Python files found![/yellow]")
        return
    
    # Build graph
    graph = build_graph()
    
    # Initialize diagram
    current_mermaid = INITIAL_DIAGRAM
    
    # Ensure output directory exists
    output_path = Path(output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    # Process each file
    for i, file_path in enumerate(python_files, 1):
        console.print(f"[dim]({i}/{len(python_files)}) Analyzing {file_path.name}...[/dim]")
        
        try:
            content = file_path.read_text(encoding="utf-8", errors="ignore")
            
            result = graph.invoke({
                "file_path": str(file_path),
                "file_content": content,
                "diff": f"Full file scan: {file_path.name}",
                "current_mermaid": current_mermaid,
                "retry_count": 0,
            })
            
            # Update current diagram if changed
            if result.get("updated_mermaid"):
                current_mermaid = result["updated_mermaid"]
                
        except Exception as e:
            console.print(f"[red]   Error: {e}[/red]")
    
    # Write final diagram
    from datetime import datetime
    final_content = f"""# Live Architecture

> **Auto-generated by Umbra** - Do not edit manually
> Last updated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
> Scanned: {len(python_files)} files

## System Overview

```mermaid
{current_mermaid}
```

## Recent Changes

| Time | File | Change |
|------|------|--------|
| {datetime.now().strftime("%H:%M")} | scan | Full project scan |
"""
    
    output_path.write_text(final_content, encoding="utf-8")
    console.print(f"\n[green]OK: Architecture saved to {output}[/green]")


@cli.command()
@click.argument("output_file", type=click.Path())
@click.option(
    "--input", "-i",
    default="./output/LIVE_ARCHITECTURE.md",
    help="Input markdown file",
)
@click.option(
    "--name", "-n",
    default=None,
    help="Project name",
)
def export(output_file: str, input: str, name: str | None):
    """Export architecture to HTML file."""
    from umbra.export import export_html
    
    # Determine project name
    if name is None:
        name = Path.cwd().name
    
    try:
        export_html(input, output_file, name)
        console.print(f"[green]OK: Exported to {output_file}[/green]")
        console.print(f"[dim]Open in browser to view interactive diagram[/dim]")
    except FileNotFoundError as e:
        console.print(f"[red]ERROR: {e}[/red]")
        console.print("[dim]Run 'umbra scan' or 'umbra watch' first to generate architecture[/dim]")


@cli.command()
@click.argument("file", type=click.Path(exists=True))
def validate(file: str):
    """Validate a Mermaid diagram file."""
    from umbra.validators.mermaid import validate_mermaid

    content = Path(file).read_text(encoding="utf-8")

    # Extract mermaid if in markdown
    if "```mermaid" in content:
        start = content.index("```mermaid") + len("```mermaid")
        end = content.index("```", start)
        mermaid = content[start:end].strip()
    else:
        mermaid = content

    result = validate_mermaid(mermaid)

    if result.is_valid:
        console.print("[green]OK: Diagram is valid[/green]")
    else:
        console.print("[red]ERROR: Diagram has errors:[/red]")
        for error in result.errors:
            console.print(f"   - {error}")

    if result.warnings:
        console.print("[yellow]WARNINGS:[/yellow]")
        for warning in result.warnings:
            console.print(f"   - {warning}")


if __name__ == "__main__":
    cli()
