"""Flask blueprint for Genesis UI overlay.

Serves static assets at /genesis-ui/ and injects <script>/<link> tags
into the AZ index.html response via an after_request hook.  This lets
Genesis rebrand, hide, and rewire AZ UI elements without modifying any
AZ core files.
"""

from __future__ import annotations

import logging
from pathlib import Path

from flask import Blueprint, Response, jsonify, request, send_from_directory

logger = logging.getLogger("genesis.ui")

STATIC_DIR = Path(__file__).parent / "static"

blueprint = Blueprint(
    "genesis_ui",
    __name__,
)

# ── Static asset serving ─────────────────────────────────────────────


@blueprint.route("/genesis-ui/<path:filename>")
def serve_static(filename: str):
    """Serve Genesis UI overlay assets."""
    return send_from_directory(str(STATIC_DIR), filename)


# ── HTML injection ───────────────────────────────────────────────────

_INJECT_TAGS = (
    '<link rel="stylesheet" href="/genesis-ui/genesis-overlay.css">\n'
    '<script type="module" src="/genesis-ui/genesis-overlay.js"></script>\n'
)


def register_injection(app) -> None:
    """Register an after_request hook that injects Genesis overlay assets.

    Called once from the bootstrap extension after the blueprint is
    registered.  The hook only touches HTML responses that contain
    ``</head>`` and haven't already been injected.
    """

    @app.after_request
    def _inject_genesis_overlay(response: Response) -> Response:
        # Only inject into the main index page — component fragments and
        # static files must not be touched (they use direct passthrough mode).
        if request.path != "/":
            return response
        ct = response.content_type or ""
        if "text/html" not in ct:
            return response
        try:
            html = response.get_data(as_text=True)
        except RuntimeError:
            return response  # direct passthrough mode — skip
        if "/genesis-ui/" in html or "</head>" not in html:
            return response

        html = html.replace("</head>", _INJECT_TAGS + "</head>")
        response.set_data(html)
        return response

    logger.info("Genesis UI overlay injection registered")


# ── API endpoints for UI data ────────────────────────────────────────

def _async_route(f):
    """Decorator to run async Flask route handlers."""
    import asyncio
    from functools import wraps

    @wraps(f)
    def wrapper(*args, **kwargs):
        loop = asyncio.new_event_loop()
        try:
            return loop.run_until_complete(f(*args, **kwargs))
        finally:
            loop.close()

    return wrapper


@blueprint.route("/api/genesis/ui/sessions")
@_async_route
async def ui_sessions():
    """Return Genesis CC sessions shaped for the sidebar chats list."""
    from genesis.runtime import GenesisRuntime

    rt = GenesisRuntime.instance()
    if not rt.is_bootstrapped or rt.db is None:
        return jsonify([])

    import aiosqlite

    limit = min(request.args.get("limit", 30, type=int), 100)

    rt.db.row_factory = aiosqlite.Row
    rows = await rt.db.execute_fetchall(
        "SELECT * FROM cc_sessions ORDER BY started_at DESC LIMIT ?",
        (limit,),
    )

    results = []
    for row in rows:
        r = dict(row)
        # Shape to match AZ context format for sidebar rendering
        label = r.get("channel", "session")
        model = r.get("model", "")
        if model:
            label = f"{label} ({model})"
        results.append({
            "id": f"genesis-{r['id']}",
            "name": label,
            "status": r.get("status", "unknown"),
            "model": r.get("model"),
            "channel": r.get("channel"),
            "started_at": r.get("started_at"),
            "session_type": r.get("session_type", r.get("source_tag", "")),
        })
    return jsonify(results)


@blueprint.route("/api/genesis/ui/memory/stats")
@_async_route
async def ui_memory_stats():
    """Return counts per memory type for the Genesis memory browser."""
    from genesis.runtime import GenesisRuntime

    rt = GenesisRuntime.instance()
    if not rt.is_bootstrapped or rt.db is None:
        return jsonify({"episodic": 0, "procedural": 0, "observations": 0})

    stats = {}
    for table, key in [
        ("procedural_memory", "procedural"),
        ("observations", "observations"),
    ]:
        try:
            rows = await rt.db.execute_fetchall(f"SELECT COUNT(*) FROM {table}")
            stats[key] = rows[0][0] if rows else 0
        except Exception:
            stats[key] = 0
    return jsonify(stats)


@blueprint.route("/api/genesis/ui/memory/search")
@_async_route
async def ui_memory_search():
    """Search Genesis memory across types."""
    from genesis.runtime import GenesisRuntime

    rt = GenesisRuntime.instance()
    if not rt.is_bootstrapped or rt.db is None:
        return jsonify({"results": [], "total": 0})

    query = request.args.get("q", "")
    mem_type = request.args.get("type", "observations")
    limit = min(request.args.get("limit", 20, type=int), 100)

    table_map = {
        "procedural": ("procedural_memory", "procedure_text", "created_at"),
        "observations": ("observations", "content", "created_at"),
    }

    if mem_type not in table_map:
        return jsonify({"results": [], "total": 0})

    table, content_col, time_col = table_map[mem_type]

    import aiosqlite

    rt.db.row_factory = aiosqlite.Row

    if query:
        rows = await rt.db.execute_fetchall(
            f"SELECT * FROM {table} WHERE {content_col} LIKE ? ORDER BY {time_col} DESC LIMIT ?",
            (f"%{query}%", limit),
        )
    else:
        rows = await rt.db.execute_fetchall(
            f"SELECT * FROM {table} ORDER BY {time_col} DESC LIMIT ?",
            (limit,),
        )

    results = [dict(r) for r in rows]
    return jsonify({"results": results, "total": len(results)})


@blueprint.route("/api/genesis/ui/inbox")
@_async_route
async def ui_inbox():
    """Return Genesis inbox items."""
    from genesis.runtime import GenesisRuntime

    rt = GenesisRuntime.instance()
    if not rt.is_bootstrapped or rt.db is None:
        return jsonify([])

    import aiosqlite

    limit = min(request.args.get("limit", 30, type=int), 100)
    status = request.args.get("status")

    rt.db.row_factory = aiosqlite.Row
    if status:
        rows = await rt.db.execute_fetchall(
            "SELECT * FROM inbox_items WHERE status = ? ORDER BY created_at DESC LIMIT ?",
            (status, limit),
        )
    else:
        rows = await rt.db.execute_fetchall(
            "SELECT * FROM inbox_items ORDER BY created_at DESC LIMIT ?",
            (limit,),
        )

    return jsonify([dict(r) for r in rows])


@blueprint.route("/api/genesis/ui/tasks")
@_async_route
async def ui_tasks():
    """Return Genesis scheduled jobs and subsystem health for the tasks modal."""
    from genesis.runtime import GenesisRuntime

    rt = GenesisRuntime.instance()
    if not rt.is_bootstrapped:
        return jsonify({"jobs": []})

    # Collect subsystem heartbeats as scheduled job health
    jobs = []
    if rt.db is not None:
        import aiosqlite

        rt.db.row_factory = aiosqlite.Row
        try:
            rows = await rt.db.execute_fetchall(
                """SELECT subsystem, MAX(timestamp) as last_heartbeat, COUNT(*) as total_beats
                   FROM events
                   WHERE event_type = 'heartbeat'
                   GROUP BY subsystem
                   ORDER BY last_heartbeat DESC"""
            )
            for row in rows:
                r = dict(row)
                jobs.append({
                    "name": r["subsystem"],
                    "last_run": r["last_heartbeat"],
                    "total_runs": r["total_beats"],
                })
        except Exception:
            pass

    return jsonify({"jobs": jobs})
