"""cortex/cli/app.py — main Click group, helpers, and plugin factory.

Everything shared across command modules lives here.
"""
from __future__ import annotations

import asyncio
import logging
import sys
from pathlib import Path
from typing import Any, Optional

import click

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Rich helpers (graceful fallback to plain text)
# ---------------------------------------------------------------------------

try:
    from rich.console import Console
    from rich.table import Table
    from rich import print as rprint
    _RICH = True
    _console = Console()
except ImportError:
    _RICH = False
    _console = None  # type: ignore


def _print(msg: str) -> None:
    """Print a message — uses rich if available."""
    if _RICH:
        _console.print(msg)  # type: ignore[union-attr]
    else:
        click.echo(msg)


def _ok(msg: str) -> None:
    _print(f"[green]✓[/green] {msg}" if _RICH else f"✓ {msg}")


def _err(msg: str) -> None:
    _print(f"[red]✗[/red] {msg}" if _RICH else f"✗ {msg}")


def _warn(msg: str) -> None:
    _print(f"[yellow]⚠[/yellow]  {msg}" if _RICH else f"⚠  {msg}")


def _section(title: str) -> None:
    if _RICH:
        _console.rule(f"[bold]{title}[/bold]")  # type: ignore[union-attr]
    else:
        click.echo(f"\n── {title} ──")


def _kv_table(rows: list[tuple[str, str]], title: str = "") -> None:
    """Print key/value pairs as a table."""
    if _RICH:
        table = Table(title=title or None, show_header=True, header_style="bold cyan")
        table.add_column("Key", style="dim")
        table.add_column("Value")
        for k, v in rows:
            table.add_row(str(k), str(v))
        _console.print(table)  # type: ignore[union-attr]
    else:
        if title:
            click.echo(f"\n{title}")
        for k, v in rows:
            click.echo(f"  {k}: {v}")


# ---------------------------------------------------------------------------
# Async helper
# ---------------------------------------------------------------------------

def _run(coro: Any) -> Any:
    """Run an async coroutine from sync click command."""
    return asyncio.run(coro)


# ---------------------------------------------------------------------------
# Plugin factory
# ---------------------------------------------------------------------------

def _make_plugin(db: str, config: Optional[str]) -> Any:
    """Instantiate CortexPlugin (lazy import — optional heavy deps)."""
    try:
        from cortex.api.plugin import CortexPlugin
    except ImportError as exc:
        _err(f"Cannot import CortexPlugin: {exc}")
        sys.exit(1)
    return CortexPlugin(config_path=config, db_path=db)


async def _init_plugin(db: str, config: Optional[str]) -> Any:
    """Create plugin and initialize storage (async-ready for CLI commands)."""
    plugin = _make_plugin(db, config)
    if plugin.storage is not None:
        await plugin.storage.initialize()
    return plugin


# ---------------------------------------------------------------------------
# Profile templates
# ---------------------------------------------------------------------------

_PROFILES: dict[str, dict[str, Any]] = {
    "companion": {
        "description": "Warm, conversational companion focused on long-term memory",
        "extraction_model": "gemini-2.5-flash",
        "token_budgets_cornerstone": 1200,
        "token_budgets_retrieval": 4000,
        "token_budgets_total_memory": 5500,
        "drift_check_threshold": 0.2,
    },
    "coding": {
        "description": "Engineering copilot focused on code patterns and decisions",
        "extraction_model": "gemini-2.5-flash",
        "token_budgets_cornerstone": 600,
        "token_budgets_retrieval": 6000,
        "token_budgets_total_memory": 7000,
        "drift_check_threshold": 0.4,
        "extraction_coding_noise_filter": False,
    },
    "enterprise": {
        "description": "High-fidelity enterprise deployment with strict identity protection",
        "extraction_model": "sonnet",
        "token_budgets_cornerstone": 2000,
        "token_budgets_retrieval": 8000,
        "token_budgets_total_memory": 10000,
        "drift_check_threshold": 0.1,
        "drift_restore_threshold": 0.3,
    },
}

_TOML_TEMPLATE = """\
# cortex.toml — generated by evaos init
# Profile: {profile}
# {description}

[cortex]
version = "3.0"
storage_backend = "sqlite"
owner_id = "default"

[cortex.storage]
db_path = "{db_path}"
enable_wal = true
busy_timeout_ms = 5000

[cortex.extraction]
model = "{extraction_model}"
coding_noise_filter = {coding_noise_filter}

[cortex.token_budgets]
cornerstone = {token_budgets_cornerstone}
retrieval = {token_budgets_retrieval}
total_memory = {token_budgets_total_memory}

[cortex.drift]
check_threshold = {drift_check_threshold}
restore_threshold = {drift_restore_threshold}
"""


# ---------------------------------------------------------------------------
# CLI root
# ---------------------------------------------------------------------------

@click.group()
@click.option("--db", default=None,
              envvar="CORTEX_DB", help="Path to Cortex SQLite database (default: from config, or ./cortex.db).")
@click.option("--config", default=None, envvar="CORTEX_CONFIG",
              help="Path to cortex.toml config file.")
@click.option("--verbose", "-v", is_flag=True, default=False,
              help="Enable verbose output (sets log level to DEBUG).")
@click.option("--log-level", "log_level",
              type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"], case_sensitive=False),
              default=None,
              help="Set logging level (overrides --verbose).")
@click.pass_context
def main(ctx: click.Context, db: str, config: Optional[str],
         verbose: bool, log_level: Optional[str]) -> None:
    """evaos — Cortex memory CLI.

    Query, manage, and operate Cortex cognitive memory from the command line.

    Common options (available on all commands):

    \b
        --db PATH       SQLite database path (default: ./cortex.db)
        --config PATH   cortex.toml config file
        -v, --verbose   Enable verbose/debug output
        --log-level LVL Set logging level (DEBUG, INFO, WARNING, ERROR)

    Environment variables: CORTEX_DB, CORTEX_CONFIG
    """
    ctx.ensure_object(dict)
    ctx.obj["db"] = db

    # Configure logging based on --verbose / --log-level
    if log_level is not None:
        level = getattr(logging, log_level.upper(), logging.INFO)
    elif verbose:
        level = logging.DEBUG
    else:
        level = logging.WARNING
    logging.basicConfig(level=level, format="%(levelname)s: %(name)s: %(message)s")

    # Auto-detect cortex.toml in cwd if --config not specified
    if config is None and Path("cortex.toml").exists():
        config = "cortex.toml"
    ctx.obj["config"] = config
