"""
CLI entry point for dxlabs.
Defines the command structure and handles user interaction for asset management.
"""

import os

import click
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from .config import load_config, save_config
from .core.env_manager import EnvManager

console = Console()

# Error Message Constants
ERROR_NOT_INITIALIZED = (
    "[red]Error: Project not initialized. Run 'dxlabs init' first.[/red]"
)
ERROR_CONFIG_INCOMPLETE = "[red]Error: Configuration is incomplete (missing bucket or project). Check .dxlabs.json.[/red]"


@click.group()
def main():
    """
    dxlabs: DX Labs CLI for project asset management.
    A tool to manage environment files and other project resources efficiently.
    """
    pass


@main.command()
@click.option("--bucket", default="", help="The AWS S3 bucket name to use for storage.")
@click.option(
    "--project",
    default="",
    help="A unique project name (used as a key prefix). Defaults to the current directory name.",
)
@click.option(
    "--region",
    default="ap-northeast-1",
    help="The AWS region to use. Defaults to ap-northeast-1.",
)
@click.option(
    "--profile", default=None, help="The AWS profile name to use for credentials."
)
def init(bucket, project, region, profile):
    """
    Initialize dxlabs configuration for the current project.
    Saves settings to a local .dxlabs.json file.
    If no options are provided, it creates a configuration with empty values.
    """
    if not project:
        project = os.path.basename(os.getcwd())

    save_config(bucket, project, region, profile)
    profile_display = profile if profile else "default/env-var"

    # Inform the user if some fields are empty
    status_msg = "Configuration saved to [bold cyan].dxlabs.json[/bold cyan]\n"
    status_msg += f"Bucket: [green]{bucket if bucket else '(empty)'}[/green]\n"
    status_msg += f"Project: [green]{project if project else '(empty)'}[/green]\n"
    status_msg += f"Region: [green]{region}[/green]\n"
    status_msg += f"Profile: [green]{profile_display}[/green]"

    if not bucket or not project:
        status_msg += "\n\n[yellow]Warning: Some required fields are empty. Please update .dxlabs.json before using push/pull.[/yellow]"

    console.print(Panel(status_msg, title="Success"))


@main.group()
def env():
    """
    Manage project environment (.env) files.
    Sub-commands for pushing to, pulling from, listing, and deleting from the cloud.
    """
    pass


def _get_manager_and_config():
    """Helper to load config and initialize EnvManager with validation."""
    config = load_config()
    if not config:
        console.print(ERROR_NOT_INITIALIZED)
        return None, None

    if not config.bucket or not config.project:
        console.print(ERROR_CONFIG_INCOMPLETE)
        return None, None

    return EnvManager(config), config


@env.command(name="push")
@click.option(
    "--env",
    "env_name",
    default=None,
    help="Environment name (e.g., 'staging'). If omitted, it scans and pushes ALL .env files found.",
)
@click.option("--file", default=None, help="Explicit path to a local file to upload.")
def env_push(env_name, file):
    """
    Upload .env file(s) to S3, preserving their local relative path.
    Files are stored under 'ENV/{project_id}/'.
    """
    manager, config = _get_manager_and_config()
    if not manager:
        return

    # CASE 1: Explicit file provided
    if file:
        if not os.path.exists(file):
            console.print(f"[red]Error: File '{file}' not found.[/red]")
            return
        _upload_file(manager, config, file)
        return

    # CASE 2: Specific environment requested but no file provided
    if env_name:
        target_file = manager.find_local_file(env_name)
        if not target_file:
            console.print(
                f"[red]Error: Could not find a .env file for environment '{env_name}'.[/red]"
            )
            return
        _upload_file(manager, config, target_file)
        return

    # CASE 3: No parameters provided -> Batch Discovery and Upload
    _handle_env_push_batch(manager, config)


def _handle_env_push_batch(manager, config):
    """Helper to perform batch discovery and upload."""
    console.print(
        "[yellow]No environment specified. Scanning for all .env files...[/yellow]"
    )
    env_map = manager.discover_all_env_files()

    if not env_map:
        console.print("[red]No .env files found in the project.[/red]")
        return

    console.print(
        f"Found [bold cyan]{len(env_map)}[/bold cyan] environment file(s). Starting batch upload..."
    )
    for name, path in env_map.items():
        _upload_file(manager, config, path)


def _upload_file(manager, config, local_path):
    """Helper to perform upload and print status."""
    remote_key = manager.get_remote_key(local_path)
    console.print(
        f"Uploading [cyan]{local_path}[/cyan] -> [bold]s3://{config.bucket}/{remote_key}[/bold]"
    )
    if manager.push(local_path):
        console.print("[green]✓ Uploaded successfully.[/green]")
    else:
        console.print("[red]✗ Upload failed.[/red]")


@env.command(name="pull")
@click.option(
    "--env",
    "env_name",
    default=None,
    help="Specific environment to pull. If omitted, it pulls ALL files from S3.",
)
@click.option(
    "--file",
    default=None,
    help="Explicit path to download. Defaults to '.env' if --env is used.",
)
def env_pull(env_name, file):
    """
    Download .env file(s) from S3, preserving their local relative path.
    Files are retrieved from 'ENV/{project_id}/'.
    """
    manager, config = _get_manager_and_config()
    if not manager:
        return

    # CASE 1: Explicit file path provided
    if file:
        _download_file(manager, config, file)
        return

    # CASE 2: Specific environment requested
    if env_name:
        target_file = manager.find_local_file(env_name)
        if not target_file:
            # Fallback to a standard name if local file not found
            target_file = f".env.{env_name}"
        _download_file(manager, config, target_file)
        return

    # CASE 3: No parameters -> Pull ALL files found on S3 for this project
    _handle_env_pull_batch(manager, config)


def _handle_env_pull_batch(manager, config):
    """Helper to perform batch download from S3."""
    console.print(
        "[yellow]No environment specified. Scanning S3 for all project .env files...[/yellow]"
    )
    remote_files = manager.discover_remote_files()

    if not remote_files:
        console.print("[red]No files found on S3 for this project.[/red]")
        return

    console.print(
        f"Found [bold cyan]{len(remote_files)}[/bold cyan] file(s) on S3. Starting batch download..."
    )
    for rel_path in remote_files:
        _download_file(manager, config, rel_path)


def _download_file(manager, config, local_path):
    """Helper to perform download and print status."""
    remote_key = manager.get_remote_key(local_path)
    console.print(
        f"Downloading [bold]s3://{config.bucket}/{remote_key}[/bold] -> [cyan]{local_path}[/cyan]"
    )
    if manager.pull(local_path):
        console.print("[green]✓ Downloaded successfully.[/green]")
    else:
        console.print("[red]✗ Download failed.[/red]")


@env.command(name="list")
def env_list():
    """
    List all environment files for the current project stored on S3.
    """
    manager, config = _get_manager_and_config()
    if not manager:
        return

    remote_files = manager.discover_remote_files()

    if not remote_files:
        console.print(
            f"[yellow]No environment files found on S3 for project '{config.project}'.[/yellow]"
        )
        return

    table = Table(title=f"Environment files on S3 (Project: {config.project})")
    table.add_column("Relative Path", style="cyan")
    table.add_column("S3 URI", style="green")

    for rel_path in remote_files:
        # Construct S3 URI manually using forward slashes for S3 compatibility
        s3_path = rel_path.replace(os.sep, "/")
        s3_uri = f"s3://{config.bucket}/ENV/{config.project}/{s3_path}"
        table.add_row(rel_path, s3_uri)

    console.print(table)


@env.command(name="delete")
@click.option(
    "--env", "env_name", default=None, help="Specific environment to delete from S3."
)
@click.option(
    "--file", default=None, help="Explicit relative path of the file to delete from S3."
)
@click.option(
    "--all", is_flag=True, help="Delete ALL environment files for this project on S3."
)
def env_delete(env_name, file, all):
    """
    Delete .env file(s) from S3.
    Requires confirmation.
    """
    manager, config = _get_manager_and_config()
    if not manager:
        return

    if all:
        _handle_env_delete_all(manager, config)
        return

    target_path = file
    if not target_path and env_name:
        target_path = manager.find_local_file(env_name)
        if not target_path:
            target_path = f".env.{env_name}"

    if not target_path:
        console.print("[red]Error: Please specify --env, --file, or --all.[/red]")
        return

    if click.confirm(f"Are you sure you want to delete '{target_path}' from S3?"):
        _delete_file(manager, config, target_path)


def _handle_env_delete_all(manager, config):
    """Helper to handle the 'delete --all' logic with confirmation."""
    remote_files = manager.discover_remote_files()
    if not remote_files:
        console.print("[yellow]No files found on S3 to delete.[/yellow]")
        return

    console.print(
        f"[bold red]CRITICAL: You are about to delete ALL environment files for project '{config.project}':[/bold red]"
    )
    for f in remote_files:
        console.print(f" - [cyan]{f}[/cyan]")

    if click.confirm(
        f"\nAre you sure you want to delete these {len(remote_files)} files from S3?"
    ):
        for rel_path in remote_files:
            _delete_file(manager, config, rel_path)


def _delete_file(manager, config, local_path):
    """Helper to perform deletion and print status."""
    remote_key = manager.get_remote_key(local_path)
    console.print(f"Deleting [bold]s3://{config.bucket}/{remote_key}[/bold]")
    if manager.delete(local_path):
        console.print("[green]✓ Deleted successfully.[/green]")
    else:
        console.print("[red]✗ Deletion failed.[/red]")


if __name__ == "__main__":
    main()
