"""
Run command - Execute automation locally.

This module provides functionality to run an automation locally
in a development environment with mock services.
"""

from __future__ import annotations

import asyncio
import json
import sys
from pathlib import Path
from typing import Any

from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.syntax import Syntax

console = Console()


def run_automation(
    input_file: str | None = None,
    input_json: str | None = None,
    mock_credentials: bool = True,
) -> bool:
    """
    Run the automation locally.

    Args:
        input_file: Path to JSON file with input data
        input_json: Input data as JSON string
        mock_credentials: Use mock credentials for testing

    Returns:
        True if successful, False otherwise
    """
    # Check we're in an automation project
    if not _is_automation_project():
        console.print("[red]Error:[/red] Not in an automation project directory")
        console.print("[dim]Run this command from your automation project root[/dim]")
        return False

    # Load input data
    input_data = _load_input(input_file, input_json)
    if input_data is None:
        return False

    console.print(
        Panel.fit(
            "[bold]Running Automation Locally[/bold]",
            subtitle="Press Ctrl+C to cancel",
        )
    )

    console.print("\n[dim]Input data:[/dim]")
    console.print(Syntax(json.dumps(input_data, indent=2), "json", theme="monokai"))
    console.print()

    try:
        # Run the automation
        result = asyncio.run(_execute_automation(input_data, mock_credentials))

        if result.get("error"):
            console.print(f"\n[red]Error:[/red] {result['error']}")
            return False

        if not result.get("success", False):
            errors = result.get("errors", [])
            console.print("\n[red]Execution failed[/red]")
            for err in errors:
                msg = (
                    err.get("message", str(err)) if isinstance(err, dict) else str(err)
                )
                console.print(f"  [red]•[/red] {msg}")
            return False

        elapsed = result.get("execution_time_seconds")
        elapsed_str = f" in {elapsed:.2f}s" if elapsed else ""
        console.print(
            f"\n[green]Execution completed successfully{elapsed_str}![/green]"
        )
        console.print("\n[dim]Output data:[/dim]")

        output = result.get("output_data", {})
        console.print(
            Syntax(json.dumps(output, indent=2, default=str), "json", theme="monokai")
        )

        return True

    except KeyboardInterrupt:
        console.print("\n[yellow]Execution cancelled[/yellow]")
        return False
    except Exception as e:
        console.print(f"\n[red]Error running automation:[/red] {e}")
        return False


async def _execute_automation(
    input_data: dict[str, Any],
    mock_credentials: bool,
) -> dict[str, Any]:
    """Execute the automation with the given input."""
    from torivers_sdk.automation import Automation
    from torivers_sdk.testing import LocalSandbox

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
    ) as progress:
        task = progress.add_task("Loading automation...", total=None)

        # Try to import the automation
        try:
            progress.update(task, description="Loading automation module...")

            # Add current directory to path
            cwd = Path.cwd()
            if str(cwd) not in sys.path:
                sys.path.insert(0, str(cwd))

            # Try to find and import the automation
            found = _find_automation()
            if found is None:
                return {"error": "Could not find automation class in project"}

            # Resolve to an Automation instance
            if isinstance(found, type) and issubclass(found, Automation):
                automation = found()
            elif isinstance(found, Automation):
                automation = found
            else:
                return {
                    "error": (
                        f"Expected an Automation class or instance, "
                        f"got {type(found).__name__}"
                    )
                }

            progress.update(task, description="Running automation...")

            # Use LocalSandbox which properly sets up context,
            # credentials, validates input, and handles lifecycle hooks
            sandbox = LocalSandbox()
            result = await sandbox.run(automation, input_data)

            progress.update(task, description="Execution complete!")

            return {
                "success": result.success,
                "output_data": result.output_data,
                "errors": result.errors,
                "execution_time_seconds": result.execution_time_seconds,
            }

        except ImportError as e:
            return {"error": f"Import error: {e}"}
        except Exception as e:
            return {"error": str(e)}


def _find_automation() -> Any:
    """Find the automation class in the current project."""
    # Look for main.py with automation class
    main_file = Path.cwd() / "main.py"
    if not main_file.exists():
        # Try looking in src directory
        src_main = Path.cwd() / "src" / "main.py"
        if src_main.exists():
            main_file = src_main
        else:
            return None

    # Import the module
    import importlib.util

    spec = importlib.util.spec_from_file_location("automation_main", main_file)
    if spec is None or spec.loader is None:
        return None

    module = importlib.util.module_from_spec(spec)
    sys.modules["automation_main"] = module
    spec.loader.exec_module(module)

    # Look for an automation attribute
    if hasattr(module, "automation"):
        return module.automation

    # Look for any Automation subclass instance
    from torivers_sdk.automation import Automation

    for attr_name in dir(module):
        attr = getattr(module, attr_name)
        if isinstance(attr, Automation):
            return attr

    return None


def _is_automation_project() -> bool:
    """Check if the current directory is an automation project."""
    cwd = Path.cwd()

    # Check for automation.yaml
    if (cwd / "automation.yaml").exists():
        return True

    # Check for main.py with typical structure
    if (cwd / "main.py").exists():
        return True

    return False


def _load_input(
    input_file: str | None,
    input_json: str | None,
) -> dict[str, Any] | None:
    """Load input data from file or JSON string."""
    if input_file and input_json:
        console.print("[red]Error:[/red] Cannot specify both --input and --input-json")
        return None

    if input_file:
        try:
            path = Path(input_file)
            if not path.exists():
                console.print(f"[red]Error:[/red] Input file not found: {input_file}")
                return None

            with open(path) as f:
                return json.load(f)
        except json.JSONDecodeError as e:
            console.print(f"[red]Error:[/red] Invalid JSON in input file: {e}")
            return None

    if input_json:
        try:
            return json.loads(input_json)
        except json.JSONDecodeError as e:
            console.print(f"[red]Error:[/red] Invalid JSON string: {e}")
            return None

    # Look for default input files
    default_files = ["sample_input.json", "input.json", "test_input.json"]
    cwd = Path.cwd()

    for filename in default_files:
        path = cwd / filename
        if path.exists():
            console.print(f"[dim]Using input file: {filename}[/dim]")
            with open(path) as f:
                return json.load(f)

    # No input provided, use empty dict
    console.print("[dim]No input file specified, using empty input[/dim]")
    return {}
