"""cortex/cli/lifecycle_cmds.py — dream, health, init, stats."""
from __future__ import annotations

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

import click

import cortex.cli.app as _app

main = _app.main
logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# evaos init
# ---------------------------------------------------------------------------

@main.command("init")
@click.option("--profile", default="companion",
              type=click.Choice(["companion", "coding", "enterprise"]),
              show_default=True, help="Configuration profile to use.")
@click.pass_context
def cmd_init(ctx: click.Context, profile: str) -> None:
    """Initialise Cortex — create cortex.toml and cortex.db.

    Writes a cortex.toml with sensible defaults for the chosen profile and
    initialises the SQLite database schema.
    """
    db_path = ctx.obj["db"]
    config_path = ctx.obj["config"] or "cortex.toml"

    p = _app._PROFILES[profile]

    toml_content = _app._TOML_TEMPLATE.format(
        profile=profile,
        description=p["description"],
        db_path=db_path,
        extraction_model=p.get("extraction_model", "gemini-2.5-flash"),
        coding_noise_filter=str(p.get("extraction_coding_noise_filter", True)).lower(),
        token_budgets_cornerstone=p.get("token_budgets_cornerstone", 800),
        token_budgets_retrieval=p.get("token_budgets_retrieval", 3000),
        token_budgets_total_memory=p.get("token_budgets_total_memory", 4000),
        drift_check_threshold=p.get("drift_check_threshold", 0.3),
        drift_restore_threshold=p.get("drift_restore_threshold", 0.5),
    )

    config_file = Path(config_path)
    if config_file.exists():
        _app._warn(f"Config already exists: {config_file} — skipping overwrite.")
    else:
        config_file.write_text(toml_content)
        _app._ok(f"Config written: {config_file}")

    # Initialise DB schema via CortexPlugin (storage adapter creates tables on connect)
    plugin = _app._make_plugin(db_path, config_path if config_file.exists() else None)

    async def _init_db() -> None:
        if plugin.storage is not None:
            await plugin.storage.initialize()
        _app._ok(f"Database initialised: {db_path}")
        _app._print(f"\n[bold]Profile:[/bold] {profile}" if _app._RICH else f"\nProfile: {profile}")
        _app._print(f"[dim]{p['description']}[/dim]" if _app._RICH else p["description"])
        _app._print("\nRun [bold]evaos health[/bold] to verify the installation." if _app._RICH
                     else "\nRun 'evaos health' to verify the installation.")

    _app._run(_init_db())


# ---------------------------------------------------------------------------
# evaos health
# ---------------------------------------------------------------------------

@main.command("health")
@click.pass_context
def cmd_health(ctx: click.Context) -> None:
    """Check Cortex system health — storage, modules, connectivity."""
    plugin = _app._make_plugin(ctx.obj["db"], ctx.obj["config"])

    async def _check() -> None:
        if plugin.storage:
            await plugin.storage.initialize()
        report = await plugin.health()

        rows: list[tuple[str, str]] = [
            ("Overall", "✓ OK" if report.ok else "✗ DEGRADED"),
            ("Storage", report.storage),
            ("Timestamp", report.timestamp),
        ]
        for mod, status in report.modules.items():
            rows.append((f"Module: {mod}", status))
        if report.errors:
            for e in report.errors:
                rows.append(("Error", e))

        _app._kv_table(rows, title="Cortex Health")

        if not report.ok:
            sys.exit(1)

    _app._run(_check())


# ---------------------------------------------------------------------------
# evaos dream
# ---------------------------------------------------------------------------

@main.command("dream")
@click.option("--session", default=None, help="Session ID to close before dreaming.")
@click.pass_context
def cmd_dream(ctx: click.Context, session: Optional[str]) -> None:
    """Trigger a dream cycle — consolidate memories and run curiosity engine."""
    plugin = _app._make_plugin(ctx.obj["db"], ctx.obj["config"])
    sid = session or f"cli-dream-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}"

    async def _dream() -> None:
        if plugin.storage:
            await plugin.storage.initialize()
        _app._print("[cyan]Starting dream cycle…[/cyan]" if _app._RICH else "Starting dream cycle…")
        wake_report = await plugin.wake(session_id=sid)
        _app._ok(f"Session open: {wake_report.session_id} "
                  f"({wake_report.cornerstones_loaded} cornerstone(s) loaded)")

        sleep_report = await plugin.sleep(session_id=sid)
        rows = [
            ("Session", sleep_report.session_id),
            ("Memories summarised", str(sleep_report.memories_summarised)),
        ]
        if sleep_report.errors:
            for e in sleep_report.errors:
                rows.append(("Error", e))
        _app._kv_table(rows, title="Dream Cycle Complete")

    _app._run(_dream())


# ---------------------------------------------------------------------------
# evaos stats
# ---------------------------------------------------------------------------

@main.command("stats")
@click.pass_context
def cmd_stats(ctx: click.Context) -> None:
    """Show memory statistics — claim counts, cornerstones, entities, drift."""
    plugin = _app._make_plugin(ctx.obj["db"], ctx.obj["config"])

    async def _stats() -> None:
        if plugin.storage:
            await plugin.storage.initialize()
        health = await plugin.health()

        rows: list[tuple[str, str]] = [
            ("Health", "OK" if health.ok else "DEGRADED"),
            ("Storage backend", health.storage),
        ]

        # Pull storage-level stats
        storage = getattr(plugin, "storage", None)
        if storage is not None:
            # Try various stats method names
            for method in ("get_stats", "stats"):
                fn = getattr(storage, method, None)
                if callable(fn):
                    try:
                        raw = fn()
                        if asyncio.iscoroutine(raw):
                            raw = await raw
                        if isinstance(raw, dict):
                            for k, v in raw.items():
                                rows.append((k.replace("_", " ").title(), str(v)))
                    except Exception as e:
                        logger.warning("cortex_cli: unhandled exception: %s", e)
                    break

            # Try direct count queries via SQL
            try:
                async with storage._get_connection() as conn:  # type: ignore[attr-defined]
                    for table, label in [
                        ("semantic_claims", "Total claims"),
                        ("cornerstone_memory", "Cornerstones"),
                        ("entities", "Entities"),
                        ("sessions", "Sessions"),
                    ]:
                        try:
                            async with conn.execute(f"SELECT COUNT(*) FROM {table}") as cur:  # noqa: S608
                                row = await cur.fetchone()
                                if row:
                                    rows.append((label, str(row[0])))
                        except Exception as e:
                            logger.warning("cortex_cli: unhandled exception: %s", e)

                    # Last dream timestamp
                    try:
                        async with conn.execute(
                            "SELECT ended_at FROM sessions WHERE status='sleeping' "
                            "ORDER BY ended_at DESC LIMIT 1"
                        ) as cur:
                            row = await cur.fetchone()
                            rows.append(("Last dream", str(row[0]) if row else "never"))
                    except Exception as e:
                        logger.warning("cortex_cli: unhandled exception: %s", e)
            except Exception as e:
                logger.warning("cortex_cli: unhandled exception: %s", e)

            # Cornerstone drift scores
            try:
                cornerstones = await plugin.get_cornerstones()
                rows.append(("Cornerstones (sealed)", str(len(cornerstones))))
                drifted = [
                    cs for cs in cornerstones
                    if getattr(cs, "drift_score", 0) and getattr(cs, "drift_score", 0) > 0.1
                ]
                if drifted:
                    _app._warn(f"{len(drifted)} cornerstone(s) with elevated drift")
                    for cs in drifted:
                        rows.append((
                            f"  Drift [{getattr(cs, 'label', '?')}]",
                            f"{getattr(cs, 'drift_score', '?'):.3f}",
                        ))
            except Exception as e:
                logger.warning("cortex_cli: unhandled exception: %s", e)

        _app._kv_table(rows, title="Cortex Stats")

    _app._run(_stats())
