#!/usr/bin/env python3
"""
IvyBloom CLI - Main entry point
"""

import sys
from typing import Optional

import click
try:
    from click_didyoumean import DYMGroup
except ImportError:
    DYMGroup = click.Group
try:
    import click_completion
except ImportError:
    click_completion = None
try:
    from click_repl import repl
except ImportError:
    repl = None
try:
    from . import __version__
    from .utils.config import Config
    from .utils.welcome import show_welcome_screen
    from .utils.colors import get_console
    from .commands.auth import auth
    from .commands.jobs import jobs
    from .commands.projects import projects
    from .commands.tools import tools
    from .commands.run import run
    from .commands.account import account
    from .commands.config import config
    from .commands.workflows import workflows
    from .commands.batch import batch
    from .commands.reports import reports
    from .commands.exports import exports
    from .commands.data import data
    from .utils.auth import AuthManager
    from .utils.test_gate import TestGate
except ImportError:
    # Direct execution - use absolute imports
    import sys
    import os
    sys.path.insert(0, os.path.dirname(__file__))
    
    from ivybloom_cli import __version__
    from utils.config import Config
    from utils.welcome import show_welcome_screen
    from commands.auth import auth
    from commands.jobs import jobs
    from commands.projects import projects
    from commands.tools import tools
    from commands.run import run
    from commands.account import account
    from commands.config import config
    from commands.workflows import workflows
    from commands.batch import batch
    from commands.data import data
    from commands.reports import reports
    from commands.exports import exports
    from utils.colors import get_console
    from utils.auth import AuthManager

console = get_console()


def _build_config(
    config_file: Optional[str],
    api_url: Optional[str],
    debug: bool,
    verbose: bool,
    output_format: str,
    timeout: int,
    retries: int,
    quiet: bool,
    no_progress: bool,
    offline: bool,
    profile: bool,
    trace_id: Optional[str],
    use_keyring: Optional[bool],
) -> Config:
    """Create and populate the CLI configuration from options."""
    config = Config(config_file)
    if api_url:
        config.set("api_url", api_url)
    if debug:
        config.set("debug", True)
    if verbose:
        config.set("verbose", True)
    if output_format:
        config.set("output_format", output_format)
    if timeout:
        config.set("timeout", timeout)
    if retries:
        config.set("retries", retries)
    if quiet:
        config.set("quiet", True)
    if no_progress:
        config.set("no_progress", True)
    if offline:
        config.set("offline", True)
    if profile:
        config.set("profile", True)
    if trace_id:
        config.set("trace_id", trace_id)
    if use_keyring is not None:
        config.set("disable_keyring", not bool(use_keyring))
    return config


def _run_test_gate(config: Config) -> None:
    """Execute the test gate and surface failures without blocking unknown errors."""
    try:
        gate = TestGate()
        result = gate.run_sync()
        if not result.get("ok"):
            from .utils.colors import print_error, print_warning

            preview = (result.get("output") or "").strip()
            max_chars = int(config.get("cli_test_preview_max_chars", 2000))
            if len(preview) > max_chars:
                preview = preview[-max_chars:]
            header = result.get("summary_line") or "Tests failed"
            print_error(f"{header}. Fix issues and re-run.")
            if preview:
                console.print(f"[dim]{preview}[/dim]")
            warnings = int(result.get("warnings") or 0)
            if warnings > 0:
                print_warning(f"Warnings: {warnings}")
            raise SystemExit(2)
        warnings = int(result.get("warnings") or 0)
        summary = result.get("summary_line") or "All tests passed"
        if warnings > 0:
            console.print(
                f"[green]{summary}[/green]  [yellow](warnings: {warnings})[/yellow]"
            )
    except SystemExit:
        raise
    except Exception as exc:  # noqa: BLE001
        # Do not block CLI if gating infrastructure misbehaves; surface debug hint.
        if config.get("debug"):
            console.print(f"[yellow]Test gate skipped due to error: {exc}[/yellow]")


def _init_click_completion() -> None:
    """Initialize shell completion when available."""
    if click_completion is None:
        return
    try:
        click_completion.init()
    except Exception as exc:  # noqa: BLE001
        console.print(f"[yellow]Completion init failed: {exc}[/yellow]")

@click.group(invoke_without_command=True, cls=DYMGroup)
@click.option('--config-file', type=click.Path(), help='Path to configuration file')
@click.option('--api-url', help='API base URL (overrides config)')
@click.option('--debug', is_flag=True, help='Enable debug output')
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
@click.option('--output-format', default='table', type=click.Choice(['json', 'yaml', 'table', 'csv']), help='Output format')
@click.option('--timeout', default=30, type=int, help='Request timeout in seconds')
@click.option('--retries', default=3, type=int, help='Number of retry attempts')
@click.option('--quiet', '-q', is_flag=True, help='Suppress non-essential output')
@click.option('--no-progress', is_flag=True, help='Disable progress bars and spinners')
@click.option('--offline', is_flag=True, help='Enable offline mode (use cached data)')
@click.option('--profile', is_flag=True, help='Enable performance profiling')
@click.option('--trace-id', help='Set a fixed trace id for all API requests (observability)')
@click.version_option(version=__version__, prog_name='ivybloom')
# Explicit keyring control flags
@click.option('--use-keyring/--no-keyring', default=None, help='Enable or disable system keyring for credential storage (default disabled).')
@click.pass_context
def cli(
    ctx: click.Context,
    config_file: Optional[str],
    api_url: Optional[str],
    debug: bool,
    verbose: bool,
    output_format: str,
    timeout: int,
    retries: int,
    quiet: bool,
    no_progress: bool,
    offline: bool,
    profile: bool,
    trace_id: Optional[str],
    use_keyring: Optional[bool],
) -> None:
    """🌿 IvyBloom CLI

    - Auth:      ivybloom auth login --browser
    - Tools:     ivybloom tools list | ivybloom tools info <tool>
    - Run:       ivybloom run <tool> key=value [key=value ...]
    - Jobs:      ivybloom jobs list | ivybloom jobs status [<job_id>] --follow
    - Workflows: ivybloom workflows run <file.yaml> --dry-run
    - Shell:     ivybloom shell  (interactive mode if available)

    Tip: use --help on any command for detailed options.
    Docs: https://docs.ivybiosciences.com/cli
    """
    ctx.ensure_object(dict)
    config = _build_config(
        config_file,
        api_url,
        debug,
        verbose,
        output_format,
        timeout,
        retries,
        quiet,
        no_progress,
        offline,
        profile,
        trace_id,
        use_keyring,
    )
    ctx.obj['config'] = config
    ctx.obj['debug'] = debug
    ctx.obj['verbose'] = verbose
    ctx.obj['output_format'] = output_format
    ctx.obj['quiet'] = quiet
    ctx.obj['no_progress'] = no_progress
    ctx.obj['offline'] = offline
    ctx.obj['profile'] = profile

    # Always-on test gating for all CLI invocations (non-TUI as well).
    # Run tests once before executing any subcommand other than the implicit root help/version.
    _run_test_gate(config)

    # Show welcome screen if no subcommand was invoked
    if ctx.invoked_subcommand is None:
        show_welcome_screen(__version__)
        click.echo(ctx.get_help())
        return

    # Initialize shell completion (if available) once CLI is invoked
    _init_click_completion()

@cli.command()
@click.pass_context
def version(ctx: click.Context) -> None:
    """Show version information with welcome screen."""
    show_welcome_screen(__version__)

@cli.command()
def shell() -> None:
    """Start an interactive CLI shell (if available)."""
    if repl is None:
        click.echo("Interactive shell not available (click-repl not installed)")
        return
    repl(cli)

@cli.command()
@click.option('--project-id', help='Filter initial view to a specific project')
@click.option('--header/--no-header', default=False, help='Show or hide the header (default hidden)')
@click.option('--footer/--no-footer', default=False, help='Show or hide the footer (default hidden)')
@click.pass_context
def tui(
    ctx: click.Context,
    project_id: Optional[str] = None,
    header: bool = False,
    footer: bool = False,
) -> None:
    """Launch the IvyBloom Text User Interface (TUI).

    A lightweight, brand-aligned terminal UI for monitoring jobs and viewing
    basic details. Think of this as a streamlined alternative to the web app.
    """
    config = ctx.obj.get("config")
    debug_enabled = bool(config.get("debug")) if config else False

    try:
        # Lazy import textual app to keep startup light
        from .tui.app import IvyBloomTUI
    except ImportError as exc:
        message = (
            "Textual TUI not available. Install the 'textual' extra "
            "(`pip install ivybloom-cli[textual]`)."
        )
        click.echo(message)
        if debug_enabled:
            console.print(f"[yellow]{exc}[/yellow]")
        return
    except Exception as exc:  # noqa: BLE001
        if debug_enabled:
            console.print(f"[red]Failed to initialize TUI: {exc}[/red]")
        raise click.ClickException(
            "Failed to initialize TUI. Re-run with --debug for details."
        ) from exc

    # Build app with current configuration and auth context
    auth_manager = AuthManager(config)

    app = IvyBloomTUI(
        config=config,
        auth_manager=auth_manager,
        initial_project_id=project_id,
        show_header=header,
        show_footer=footer,
    )
    app.run()

# Add command groups
cli.add_command(auth)
cli.add_command(jobs)
cli.add_command(projects) 
cli.add_command(tools)
cli.add_command(account)
cli.add_command(config)
cli.add_command(workflows)
cli.add_command(batch)
cli.add_command(data)
cli.add_command(reports)
cli.add_command(exports)

# Add the run command as a top-level command
cli.add_command(run)

def main():
    """Main CLI entry point."""
    try:
        cli()
    except KeyboardInterrupt:
        # Route to stderr to avoid contaminating stdout for JSON consumers
        click.echo("Operation cancelled by user", err=True)
        sys.exit(1)
    except Exception as e:
        # Route to stderr to avoid contaminating stdout for JSON consumers
        click.echo(f"Error: {e}", err=True)
        sys.exit(1)

if __name__ == '__main__':
    main()