"""
vr push command - Push agent code to the server.
"""

import base64
import typer

from dataclasses import dataclass
from pathlib import Path
from typing import Optional

from vr_cli.utils.utils import (
    console,
    make_request,
    print_success,
    print_error,
    print_info,
    confirm,
    package_function,
    calculate_project_checksum,
)
from vr_cli.utils.agent_lock import load_agent_lock, save_agent_lock
from vr_cli.utils.helm import load_agent_data
from vr_cli.commands.validate import (
    run_project_validation,
)


@dataclass
class PushResult:
    """Result of a push operation"""
    success: bool
    agent_id: Optional[str] = None
    function_id: Optional[str] = None
    error: Optional[str] = None


def run_validation(project_dir: Path) -> bool:
    """Run validation checks before pushing. Returns True if valid."""
    result = run_project_validation(project_dir, include_handler=True)

    if not result.is_valid:
        console.print()
        console.print("[bold red]Validation failed:[/bold red]")
        for check, error in result.failed:
            console.print(f"  [red]✗[/red] {check}: {error}")
        console.print()
        return False

    return True


def package_agent_code(project_dir: Path) -> Optional[str]:
    """Package the agent code from the project directory. Returns base64-encoded zip."""
    print_info("Packaging agent code...")
    try:
        zip_bytes, _ = package_function(str(project_dir))
        return base64.b64encode(zip_bytes).decode("utf-8")
    except Exception as e:
        print_error(f"Failed to package project: {e}")
        return None


def push_code(
    project_dir: Optional[Path] = None,
    name: Optional[str] = None,
    new_function: bool = False,
    silent: bool = False,
) -> PushResult:
    """
    Push agent code to the server.

    This is the core push logic that can be called programmatically.

    Args:
        project_dir: Project directory (defaults to cwd)
        name: Optional name for the function version
        new_function: Create a new function version instead of updating
        silent: Suppress output messages

    Returns:
        PushResult with success status and agent/function IDs
    """
    if project_dir is None:
        project_dir = Path.cwd()

    voicerun_dir = project_dir / ".voicerun"

    if not voicerun_dir.exists():
        return PushResult(
            success=False,
            error="Not a voicerun project (.voicerun directory not found)"
        )

    # Read agent name from agent.yaml if not provided
    if not name:
        agent_data = load_agent_data(voicerun_dir)
        name = agent_data.get("Name") or None

    # Validate before pushing
    if not silent:
        print_info("Validating project...")
    if not run_validation(project_dir):
        return PushResult(success=False, error="Validation failed")

    # Load agent lock if it exists
    agent_lock = load_agent_lock(voicerun_dir)
    agent_id = agent_lock.get("id") if agent_lock else None
    function_id = agent_lock.get("functionId") if agent_lock else None
    is_new_agent = agent_id is None
    is_new_function = new_function or function_id is None

    # Package the code
    code = package_agent_code(project_dir)
    if not code:
        return PushResult(success=False, error="Failed to package code")

    # Show summary
    if not silent:
        console.print()
        if is_new_agent:
            console.print("[bold]Push Summary:[/bold] [green](new agent)[/green]")
        elif is_new_function:
            console.print("[bold]Push Summary:[/bold] [green](new function version)[/green]")
            console.print(f"  Agent ID: [cyan]{agent_id[:8]}...[/cyan]")
        else:
            console.print("[bold]Push Summary:[/bold] [yellow](update function)[/yellow]")
            console.print(f"  Agent ID: [cyan]{agent_id[:8]}...[/cyan]")
            console.print(f"  Function ID: [cyan]{function_id[:8]}...[/cyan]")
        console.print()

    # Make the API request
    if not silent:
        print_info("Pushing to server...")

    payload = {"code": code}
    if agent_id:
        payload["agentId"] = agent_id
    if function_id and not new_function:
        payload["functionId"] = function_id
    if name:
        payload["name"] = name

    response = make_request(
        "/v1/agents/push",
        method="POST",
        json=payload,
    )

    if not response:
        return PushResult(success=False, error="Failed to push code to server")

    # Save agent lock with agent and function IDs
    response_agent_id = response.get("agentId")
    response_function_id = response.get("functionId")

    if not silent:
        print_info(f"Response - Agent ID: {response_agent_id}, Function ID: {response_function_id}")
    if response_agent_id:
        checksum = calculate_project_checksum(str(project_dir))
        save_agent_lock(voicerun_dir, {
            "id": response_agent_id,
            "functionId": response_function_id,
            "checksum": checksum,
        })
        if not silent and is_new_agent:
            print_info(f"Created agent.lock (agent: {response_agent_id[:8]}...)")

    if not silent:
        console.print()
        print_success("Code pushed successfully!")

    return PushResult(
        success=True,
        agent_id=response_agent_id,
        function_id=response_function_id,
    )


def push(
    name: str = typer.Option(
        None,
        "--name",
        help="Name for the function version.",
    ),
    new: bool = typer.Option(
        False,
        "--new",
        "-n",
        help="Create a new function version instead of updating the current one.",
    ),
    yes: bool = typer.Option(
        False,
        "--yes",
        "-y",
        help="Skip confirmation prompt.",
    ),
):
    """Push agent code to the server."""
    project_dir = Path.cwd()
    voicerun_dir = project_dir / ".voicerun"

    if not voicerun_dir.exists():
        print_error("Not a voicerun project (.voicerun directory not found)")
        print_info("Run 'vr init' to create a new project")
        raise typer.Exit(1)

    # Load agent lock to show confirmation prompt
    agent_lock = load_agent_lock(voicerun_dir)
    agent_id = agent_lock.get("id") if agent_lock else None
    function_id = agent_lock.get("functionId") if agent_lock else None
    is_new_agent = agent_id is None
    is_new_function = new or function_id is None

    # Confirm before push
    if not yes:
        if is_new_agent:
            action = "Create new agent and push code"
        elif is_new_function:
            action = "Create new function version"
        else:
            action = "Update current function"
        if not confirm(f"{action}?", default=True):
            print_info("Aborted")
            raise typer.Exit(0)

    # Use the shared push_code function
    result = push_code(
        project_dir=project_dir,
        name=name,
        new_function=new,
        silent=False,
    )

    if not result.success:
        print_error(result.error or "Push failed")
        raise typer.Exit(1)
