"""Memory Proxy service deployment commands."""

import subprocess
from pathlib import Path
from typing import Optional

import cyclopts

app = cyclopts.App(name="memory-proxy", help="Memory Proxy service deployment")

SILICA_DIR = Path.home() / ".silica"
MEMORY_PROXY_DIR = SILICA_DIR / "memory-proxy"
CONFIG_FILE = SILICA_DIR / "config.env"


def _ensure_silica_dir() -> Path:
    """Ensure ~/.silica directory exists."""
    SILICA_DIR.mkdir(exist_ok=True)
    return SILICA_DIR


def _ensure_memory_proxy_dir() -> Path:
    """Ensure ~/.silica/memory-proxy directory exists."""
    MEMORY_PROXY_DIR.mkdir(parents=True, exist_ok=True)
    return MEMORY_PROXY_DIR


def _read_config() -> dict[str, str]:
    """Read config from ~/.silica/config.env."""
    config = {}
    if CONFIG_FILE.exists():
        with open(CONFIG_FILE) as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith("#") and "=" in line:
                    key, value = line.split("=", 1)
                    config[key] = value
    return config


def _write_config(config: dict[str, str]) -> None:
    """Write config to ~/.silica/config.env."""
    with open(CONFIG_FILE, "w") as f:
        f.write("# Memory Proxy Configuration\n")
        f.write("# Generated by silica memory-proxy setup\n\n")
        for key, value in sorted(config.items()):
            f.write(f"{key}={value}\n")


def _prompt_for_config() -> dict[str, str]:
    """Prompt user for required configuration."""
    from rich.console import Console
    from rich.prompt import Prompt

    console = Console()
    console.print("\n[bold]Memory Proxy Configuration[/bold]\n")

    # Read existing config
    config = _read_config()

    # Required S3 settings
    config["AWS_ACCESS_KEY_ID"] = Prompt.ask(
        "AWS Access Key ID",
        default=config.get("AWS_ACCESS_KEY_ID", ""),
    )
    config["AWS_SECRET_ACCESS_KEY"] = Prompt.ask(
        "AWS Secret Access Key",
        default=config.get("AWS_SECRET_ACCESS_KEY", ""),
        password=True,
    )
    config["AWS_REGION"] = Prompt.ask(
        "AWS Region", default=config.get("AWS_REGION", "us-east-1")
    )
    config["S3_BUCKET"] = Prompt.ask(
        "S3 Bucket Name", default=config.get("S3_BUCKET", "")
    )
    config["S3_PREFIX"] = Prompt.ask(
        "S3 Prefix", default=config.get("S3_PREFIX", "memory")
    )

    # Optional S3 endpoint
    s3_endpoint = Prompt.ask(
        "S3 Endpoint URL (optional, for non-AWS S3)",
        default=config.get("S3_ENDPOINT_URL", ""),
    )
    if s3_endpoint:
        config["S3_ENDPOINT_URL"] = s3_endpoint
    elif "S3_ENDPOINT_URL" in config:
        del config["S3_ENDPOINT_URL"]

    # Auth settings
    config["HEARE_AUTH_URL"] = Prompt.ask(
        "Heare Auth URL", default=config.get("HEARE_AUTH_URL", "")
    )

    # Optional log level
    config["LOG_LEVEL"] = Prompt.ask(
        "Log Level", default=config.get("LOG_LEVEL", "INFO")
    )

    return config


def _create_procfile() -> None:
    """Create Procfile in ~/.silica/memory-proxy."""
    procfile = MEMORY_PROXY_DIR / "Procfile"
    procfile.write_text(
        "web: uvicorn silica.memory_proxy.app:app --host 0.0.0.0 --port $PORT\n"
    )


def _create_requirements(version: Optional[str] = None) -> None:
    """Create requirements.txt with specified pysilica version."""
    requirements = MEMORY_PROXY_DIR / "requirements.txt"

    if version:
        requirements.write_text(f"pysilica=={version}\n")
    else:
        # Use latest from PyPI or current installed version
        try:
            import silica

            current_version = silica.__version__
            requirements.write_text(f"pysilica=={current_version}\n")
        except (ImportError, AttributeError):
            # Fallback to latest
            requirements.write_text("pysilica\n")


def _create_python_version() -> None:
    """Create .python-version file."""
    python_version = MEMORY_PROXY_DIR / ".python-version"
    python_version.write_text("3.11\n")


def _create_gitignore() -> None:
    """Create .gitignore for the synthetic repo."""
    gitignore = MEMORY_PROXY_DIR / ".gitignore"
    gitignore.write_text("config.env\n__pycache__/\n*.pyc\n")


def _init_git_repo() -> None:
    """Initialize git repository in ~/.silica/memory-proxy."""
    if not (MEMORY_PROXY_DIR / ".git").exists():
        subprocess.run(["git", "init"], cwd=str(MEMORY_PROXY_DIR), check=True)
        subprocess.run(["git", "add", "."], cwd=str(MEMORY_PROXY_DIR), check=True)
        subprocess.run(
            ["git", "commit", "-m", "Initial commit for memory-proxy deployment"],
            cwd=str(MEMORY_PROXY_DIR),
            check=True,
        )


def _add_dokku_remote(dokku_connection_string: str, app_name: str) -> None:
    """Add dokku remote to git repo."""
    remote_url = f"{dokku_connection_string}:{app_name}"

    # Check if remote already exists
    result = subprocess.run(
        ["git", "remote", "get-url", "dokku"],
        cwd=str(MEMORY_PROXY_DIR),
        capture_output=True,
        text=True,
    )

    if result.returncode == 0:
        # Remote exists, update it
        subprocess.run(
            ["git", "remote", "set-url", "dokku", remote_url],
            cwd=str(MEMORY_PROXY_DIR),
            check=True,
        )
    else:
        # Add new remote
        subprocess.run(
            ["git", "remote", "add", "dokku", remote_url],
            cwd=str(MEMORY_PROXY_DIR),
            check=True,
        )


def _git_force_push() -> None:
    """Force push to dokku remote."""
    subprocess.run(
        ["git", "push", "dokku", "main", "--force"],
        cwd=str(MEMORY_PROXY_DIR),
        check=True,
    )


def _check_dokku_app_exists(dokku_connection_string: str, app_name: str) -> bool:
    """Check if dokku app exists."""
    ssh_host = dokku_connection_string.split(":")[0]
    cmd = ["ssh", ssh_host, "apps:list"]

    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        return False

    # Check if app_name is in the output
    return app_name in result.stdout


def _dokku_create_app(dokku_connection_string: str, app_name: str) -> bool:
    """Create dokku app."""
    from rich.console import Console

    console = Console()
    ssh_host = dokku_connection_string.split(":")[0]
    cmd = ["ssh", ssh_host, "apps:create", app_name]
    result = subprocess.run(cmd, capture_output=True, text=True)

    if result.returncode == 0:
        console.print(f"[green]✓ Created app {app_name}[/green]")
    else:
        console.print(f"[red]Failed to create app:[/red] {result.stderr}")

    return result.returncode == 0


def _set_dokku_config(
    dokku_connection_string: str, app_name: str, config: dict[str, str]
) -> None:
    """Set dokku config variables via SSH."""
    from rich.console import Console

    console = Console()

    # Build config:set command
    config_parts = [f"{k}={v}" for k, v in config.items()]
    cmd = [
        "ssh",
        dokku_connection_string.split(":")[0],
        "config:set",
        app_name,
    ] + config_parts

    console.print(f"\n[bold]Setting dokku config for {app_name}...[/bold]")
    result = subprocess.run(cmd, capture_output=True, text=True)

    if result.returncode != 0:
        console.print(f"[red]Error setting config:[/red] {result.stderr}")
        raise RuntimeError("Failed to set dokku config")

    console.print("[green]✓ Config set successfully[/green]")


@app.command
def setup(
    dokku_host: str,
    app_name: str = "memory-proxy",
    version: Optional[str] = None,
    skip_config: bool = False,
) -> None:
    """
    Set up Memory Proxy deployment in ~/.silica.

    Creates synthetic git repository with Procfile and requirements.txt,
    prompts for configuration, and prepares for deployment to Dokku.

    Example:
        silica memory-proxy setup dokku@example.com
        silica memory-proxy setup dokku@example.com --app-name my-memory
        silica memory-proxy setup dokku@example.com --version 0.7.0
    """
    from rich.console import Console

    console = Console()

    console.print("\n[bold]Setting up Memory Proxy deployment...[/bold]\n")

    # Ensure directories exist
    _ensure_silica_dir()
    _ensure_memory_proxy_dir()

    # Prompt for or read config
    if not skip_config:
        config = _prompt_for_config()
        _write_config(config)
        console.print(f"\n[green]✓ Config saved to {CONFIG_FILE}[/green]")
    else:
        config = _read_config()
        if not config:
            console.print(
                "[red]Error:[/red] No existing config found. Run without --skip-config first."
            )
            raise SystemExit(1)

    # Create deployment files
    console.print("\n[bold]Creating deployment files...[/bold]")
    _create_procfile()
    console.print("  ✓ Procfile")
    _create_requirements(version)
    console.print(f"  ✓ requirements.txt (version: {version or 'current/latest'})")
    _create_python_version()
    console.print("  ✓ .python-version")
    _create_gitignore()
    console.print("  ✓ .gitignore")

    # Initialize git repo
    _init_git_repo()
    console.print("\n[green]✓ Git repository initialized[/green]")

    # Add dokku remote
    _add_dokku_remote(dokku_host, app_name)
    console.print(f"[green]✓ Dokku remote added: {dokku_host}:{app_name}[/green]")

    console.print("\n[bold green]Setup complete![/bold green]")
    console.print(f"\nDeployment directory: {MEMORY_PROXY_DIR}")
    console.print(f"Config file: {CONFIG_FILE}")
    console.print("\nTo deploy: [bold]silica memory-proxy deploy[/bold]")


@app.command
def deploy(
    set_config: bool = True,
) -> None:
    """
    Deploy Memory Proxy to Dokku.

    Pushes the synthetic repo from ~/.silica/memory-proxy to dokku (which creates
    the app if needed) and then sets config variables.

    Example:
        silica memory-proxy deploy
        silica memory-proxy deploy --no-set-config
    """
    from rich.console import Console

    console = Console()

    if not MEMORY_PROXY_DIR.exists() or not (MEMORY_PROXY_DIR / ".git").exists():
        console.print(
            "[red]Error:[/red] No deployment found. Run 'silica memory-proxy setup' first."
        )
        raise SystemExit(1)

    # Read config and dokku remote
    config = _read_config()
    if not config:
        console.print(
            "[red]Error:[/red] No config found. Run 'silica memory-proxy setup' first."
        )
        raise SystemExit(1)

    # Get dokku remote
    result = subprocess.run(
        ["git", "remote", "get-url", "dokku"],
        cwd=str(MEMORY_PROXY_DIR),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        console.print(
            "[red]Error:[/red] No dokku remote found. Run 'silica memory-proxy setup' first."
        )
        raise SystemExit(1)

    dokku_remote = result.stdout.strip()
    dokku_connection_string, app_name = dokku_remote.split(":")

    console.print(
        f"\n[bold]Deploying to {app_name} on {dokku_connection_string}...[/bold]\n"
    )

    # Commit any changes
    subprocess.run(["git", "add", "."], cwd=str(MEMORY_PROXY_DIR))
    subprocess.run(
        ["git", "commit", "-m", "Update deployment files", "--allow-empty"],
        cwd=str(MEMORY_PROXY_DIR),
        capture_output=True,
    )

    # If app does not exist, create it and set configuration
    if not _check_dokku_app_exists(dokku_connection_string, app_name):
        console.print(f"[yellow]App {app_name} does not exist, creating...[/yellow]")
        if not _dokku_create_app(dokku_connection_string, app_name):
            console.print("[red]Error:[/red] Failed to create app")
            raise SystemExit(1)
        _set_dokku_config(dokku_connection_string, app_name, config)
    elif set_config:
        _set_dokku_config(dokku_connection_string, app_name, config)

    # Push to dokku
    console.print("\n[bold]Pushing to dokku...[/bold]")
    _git_force_push()
    console.print("[green]✓ Code deployed[/green]")

    console.print("\n[bold green]Deployment complete![/bold green]")
    console.print(f"\nApp URL: https://{app_name}.{dokku_connection_string}")


@app.command
def upgrade(
    version: str,
) -> None:
    """
    Upgrade deployed Memory Proxy to a new pysilica version.

    Updates requirements.txt, commits, and deploys.

    Example:
        silica memory-proxy upgrade 0.8.0
    """
    from rich.console import Console

    console = Console()

    if not MEMORY_PROXY_DIR.exists() or not (MEMORY_PROXY_DIR / ".git").exists():
        console.print(
            "[red]Error:[/red] No deployment found. Run 'silica memory-proxy setup' first."
        )
        raise SystemExit(1)

    console.print(f"\n[bold]Upgrading to pysilica {version}...[/bold]\n")

    # Update requirements.txt
    _create_requirements(version)
    console.print(f"[green]✓ Updated requirements.txt to pysilica=={version}[/green]")

    # Commit changes
    subprocess.run(
        ["git", "add", "requirements.txt"], cwd=str(MEMORY_PROXY_DIR), check=True
    )
    subprocess.run(
        ["git", "commit", "-m", f"Upgrade to pysilica {version}"],
        cwd=str(MEMORY_PROXY_DIR),
        check=True,
    )

    # Deploy
    console.print("\n[bold]Deploying upgrade...[/bold]")
    _git_force_push()

    console.print(f"\n[bold green]Upgraded to pysilica {version}![/bold green]")


@app.command
def status() -> None:
    """
    Show Memory Proxy deployment status.

    Displays deployment directory info, current version, and dokku remote.
    """
    from rich.console import Console
    from rich.table import Table

    console = Console()

    if not MEMORY_PROXY_DIR.exists():
        console.print("[yellow]No deployment found.[/yellow]")
        console.print("Run 'silica memory-proxy setup' to get started.")
        return

    table = Table(title="Memory Proxy Deployment Status")
    table.add_column("Setting", style="cyan")
    table.add_column("Value", style="green")

    table.add_row("Deployment Directory", str(MEMORY_PROXY_DIR))
    table.add_row(
        "Config File", str(CONFIG_FILE) + (" ✓" if CONFIG_FILE.exists() else " ✗")
    )

    # Check for requirements.txt
    req_file = MEMORY_PROXY_DIR / "requirements.txt"
    if req_file.exists():
        version = req_file.read_text().strip().split("==")[-1]
        table.add_row("Pysilica Version", version)

    # Check for git repo
    if (MEMORY_PROXY_DIR / ".git").exists():
        table.add_row("Git Repository", "✓ Initialized")

        # Get dokku remote
        result = subprocess.run(
            ["git", "remote", "get-url", "dokku"],
            cwd=str(MEMORY_PROXY_DIR),
            capture_output=True,
            text=True,
        )

        if result.returncode == 0:
            dokku_remote = result.stdout.strip()
            table.add_row("Dokku Remote", dokku_remote)
        else:
            table.add_row("Dokku Remote", "✗ Not configured")
    else:
        table.add_row("Git Repository", "✗ Not initialized")

    console.print(table)

    # Show config summary (without secrets)
    if CONFIG_FILE.exists():
        config = _read_config()
        console.print("\n[bold]Configuration:[/bold]")
        for key in sorted(config.keys()):
            if "SECRET" in key or "KEY" in key:
                console.print(f"  {key}=***")
            else:
                console.print(f"  {key}={config[key]}")
