#!/usr/bin/env python3
"""
GCC Launcher — Launch / Status / Dispatch for All 5 Command Centres
====================================================================
Creates tmux sessions for each command centre daemon, checks health
via heartbeat files, and dispatches tasks to specific agent queues.

Author: Genesis Parallel Builder
Created: 2026-02-26
"""

import json
import os
import subprocess
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
from uuid import uuid4

# ─── Paths ────────────────────────────────────────────────────────────────────
GENESIS_ROOT = Path("/mnt/e/genesis-system")
GCC_HEARTBEATS_DIR = GENESIS_ROOT / "data" / "gcc_heartbeats"
GCC_TASK_QUEUES_DIR = GENESIS_ROOT / "data" / "gcc_task_queues"
GCC_RESULTS_DIR = GENESIS_ROOT / "data" / "gcc_results"

# ─── Current date string (injected into system prompts) ───────────────────────
_TODAY = datetime.now(timezone.utc).strftime("%Y-%m-%d")
_AEST_NOTE = "Australian timezone (AEST = UTC+10, AEDT = UTC+11 during daylight saving)"

# ─── Centre Configurations ────────────────────────────────────────────────────
CENTRES_CONFIG: list[dict] = [
    {
        "name": "orchestrator",
        "model": "gemini-3.1-pro-preview",
        "tmux_session": "gcc-orchestrator",
        "system_prompt": (
            f"You are the Genesis Orchestrator — the highest-authority Gemini agent "
            f"in the Genesis system.\n"
            f"Your role: decompose complex tasks into subtasks, delegate to "
            f"Builder/Researcher/Scout/Thinker, synthesize results, and make strategic "
            f"decisions.\n"
            f"You have access to the full Genesis codebase at /mnt/e/genesis-system/.\n"
            f"When you learn something important, mark it with [CTM]content[/CTM] "
            f"for automatic commit-to-memory.\n"
            f"Current date: {_TODAY}. {_AEST_NOTE}.\n"
            f"Genesis is building AI voice agents for Australian businesses. "
            f"The flagship product is ReceptionistAI — Telnyx-powered voice agents "
            f"priced at $497/$997/$1,497 AUD/month."
        ),
    },
    {
        "name": "builder",
        "model": "gemini-3-pro-preview",
        "tmux_session": "gcc-builder",
        "system_prompt": (
            f"You are the Genesis Builder — code generation and implementation specialist.\n"
            f"Write production Python code. Follow existing patterns in the Genesis "
            f"codebase at /mnt/e/genesis-system/.\n"
            f"Key rules:\n"
            f"  - All work on E: drive only (never C: drive)\n"
            f"  - NO SQLite — use Elestio PostgreSQL/Qdrant/Redis\n"
            f"  - Use google.genai SDK (NOT google.generativeai)\n"
            f"  - Run tests after every implementation\n"
            f"  - Add VERIFICATION_STAMP after each completed story\n"
            f"Mark important learnings with [CTM]content[/CTM].\n"
            f"Current date: {_TODAY}."
        ),
    },
    {
        "name": "researcher",
        "model": "gemini-3-flash-preview",
        "tmux_session": "gcc-researcher",
        "system_prompt": (
            f"You are the Genesis Researcher — web research and analysis specialist.\n"
            f"Research topics deeply, synthesize findings, produce actionable reports.\n"
            f"Focus areas: Australian market, AI voice agents, SaaS pricing, "
            f"competitive intelligence, tradie industry.\n"
            f"Key context:\n"
            f"  - Target market: Australian tradies and SMBs\n"
            f"  - Product pricing: $497/$997/$1,497 AUD/month (LOCKED — do not change)\n"
            f"  - Voice tech: Telnyx NaturalHD Eucalyptus (AU female voice)\n"
            f"Mark discoveries with [CTM]content[/CTM].\n"
            f"Current date: {_TODAY}. {_AEST_NOTE}."
        ),
    },
    {
        "name": "scout",
        "model": "gemini-3-flash-preview",
        "tmux_session": "gcc-scout",
        "system_prompt": (
            f"You are the Genesis Scout — codebase exploration and audit specialist.\n"
            f"Tasks: find files, trace dependencies, audit architecture, discover gaps, "
            f"map existing implementations.\n"
            f"Codebase root: /mnt/e/genesis-system/\n"
            f"Key areas to know:\n"
            f"  - core/ — execution layer, agents, Gemini executors\n"
            f"  - mcp-servers/ — MCP tool servers\n"
            f"  - KNOWLEDGE_GRAPH/ — entities and axioms\n"
            f"  - tests/ — test suites\n"
            f"  - TRADIES/, RECEPTIONISTAI/ — product code\n"
            f"Report findings concisely with exact file paths and line numbers.\n"
            f"Mark findings with [CTM]content[/CTM].\n"
            f"Current date: {_TODAY}."
        ),
    },
    {
        "name": "thinker",
        "model": "gemini-3.1-pro-preview",
        "tmux_session": "gcc-thinker",
        "system_prompt": (
            f"You are the Genesis Deep Thinker — strategic reasoning and PRD architect.\n"
            f"Think deeply about architecture, product strategy, competitive moats, "
            f"and long-term Genesis evolution.\n"
            f"Produce PRDs following Rule 17 PRD Atomic Story Doctrine:\n"
            f"  - 1 story = 1 file + 1 class/function + 1 concern\n"
            f"  - Every story includes: file path, code skeleton, ≥3 BB tests, ≥2 WB tests\n"
            f"  - Max 10 stories per module\n"
            f"  - Blueprint first, PRD second\n"
            f"Mark strategic insights with [CTM]content[/CTM].\n"
            f"Current date: {_TODAY}. {_AEST_NOTE}.\n"
            f"Reference PRDs: docs/plans/AIVA_RLM_NEXUS_PRD_V2.md (gold standard)"
        ),
    },
]

# Name → config lookup
_CENTRES_BY_NAME: dict[str, dict] = {c["name"]: c for c in CENTRES_CONFIG}

# ─── Heartbeat staleness threshold ────────────────────────────────────────────
STALE_THRESHOLD_SECONDS = 120


# ─── Core Functions ───────────────────────────────────────────────────────────

def launch_all(verbose: bool = True) -> None:
    """
    Create tmux sessions and start all 5 command centre daemons.

    Each daemon runs as:
        python3 -m core.gemini_command_centres.daemon --name {name}

    from /mnt/e/genesis-system working directory.
    """
    GCC_HEARTBEATS_DIR.mkdir(parents=True, exist_ok=True)
    GCC_TASK_QUEUES_DIR.mkdir(parents=True, exist_ok=True)
    GCC_RESULTS_DIR.mkdir(parents=True, exist_ok=True)

    for centre in CENTRES_CONFIG:
        session_name = centre["tmux_session"]
        name = centre["name"]

        # Kill existing session (ignore error if doesn't exist)
        subprocess.run(
            ["tmux", "kill-session", "-t", session_name],
            capture_output=True,
        )
        time.sleep(0.2)

        # Create new detached session
        result = subprocess.run(
            ["tmux", "new-session", "-d", "-s", session_name, "-c", str(GENESIS_ROOT)],
            capture_output=True,
            text=True,
        )
        if result.returncode != 0:
            if verbose:
                print(f"[gcc] ERROR creating session {session_name}: {result.stderr.strip()}")
            continue

        # Send daemon start command
        cmd = f"python3 -m core.gemini_command_centres --daemon --name {name}"
        subprocess.run(
            ["tmux", "send-keys", "-t", session_name, cmd, "Enter"],
            capture_output=True,
        )

        if verbose:
            print(f"[gcc] Launched: {session_name} ({centre['model']})")

    if verbose:
        print(f"\n[gcc] All {len(CENTRES_CONFIG)} command centres launched.")
        print("[gcc] Check status: python3 -m core.gemini_command_centres status")


def launch_one(name: str, verbose: bool = True) -> bool:
    """
    Launch a single named command centre.

    Args:
        name: Centre name (e.g. "orchestrator").
        verbose: Whether to print status messages.

    Returns:
        True on success, False on failure.
    """
    centre = _CENTRES_BY_NAME.get(name)
    if not centre:
        if verbose:
            print(f"[gcc] Unknown centre: {name!r}")
        return False

    session_name = centre["tmux_session"]

    subprocess.run(["tmux", "kill-session", "-t", session_name], capture_output=True)
    time.sleep(0.2)

    result = subprocess.run(
        ["tmux", "new-session", "-d", "-s", session_name, "-c", str(GENESIS_ROOT)],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        if verbose:
            print(f"[gcc] ERROR creating session {session_name}: {result.stderr.strip()}")
        return False

    cmd = f"python3 -m core.gemini_command_centres --daemon --name {name}"
    subprocess.run(
        ["tmux", "send-keys", "-t", session_name, cmd, "Enter"],
        capture_output=True,
    )

    if verbose:
        print(f"[gcc] Launched: {session_name}")
    return True


def status(verbose: bool = True) -> dict:
    """
    Check health of all command centres via heartbeat files.

    Returns:
        dict mapping centre name → status dict.
    """
    results = {}
    now = datetime.now(timezone.utc)

    for centre in CENTRES_CONFIG:
        name = centre["name"]
        hb_file = GCC_HEARTBEATS_DIR / f"{name}.json"

        if not hb_file.exists():
            results[name] = {
                "status": "no_heartbeat",
                "healthy": False,
                "last_seen": None,
                "age_seconds": None,
            }
            if verbose:
                print(f"[gcc] {name:15s}  NO HEARTBEAT")
            continue

        try:
            with open(hb_file, "r", encoding="utf-8") as fh:
                hb_data = json.load(fh)

            ts_str = hb_data.get("timestamp", "")
            last_ts = datetime.fromisoformat(ts_str) if ts_str else None
            age_s = (now - last_ts).total_seconds() if last_ts else None
            is_stale = (age_s is None) or (age_s > STALE_THRESHOLD_SECONDS)
            agent_status = hb_data.get("status", "unknown")

            result = {
                "status": agent_status,
                "healthy": (not is_stale) and agent_status == "running",
                "last_seen": ts_str,
                "age_seconds": round(age_s, 1) if age_s is not None else None,
                "turn_count": hb_data.get("turn_count", 0),
                "tasks_processed": hb_data.get("tasks_processed", 0),
                "context_pct": hb_data.get("context_utilisation_pct", 0.0),
            }
            results[name] = result

            if verbose:
                health_str = "OK" if result["healthy"] else "STALE"
                print(
                    f"[gcc] {name:15s}  {health_str:6s}  "
                    f"age={age_s:.0f}s  turns={result['turn_count']}  "
                    f"ctx={result['context_pct']:.1f}%"
                )

        except (json.JSONDecodeError, OSError, ValueError) as exc:
            results[name] = {
                "status": "error",
                "healthy": False,
                "error": str(exc),
            }
            if verbose:
                print(f"[gcc] {name:15s}  ERROR reading heartbeat: {exc}")

    return results


def dispatch(
    task: str,
    target: str = "orchestrator",
    task_id: Optional[str] = None,
    priority: int = 1,
    verbose: bool = True,
) -> str:
    """
    Add a task to a specific command centre's queue.

    Args:
        task: Prompt / task description.
        target: Target centre name (default "orchestrator").
        task_id: Optional explicit task ID. Auto-generated if None.
        priority: Task priority integer (lower = higher priority, cosmetic only).
        verbose: Whether to print confirmation.

    Returns:
        The task_id string.
    """
    if target not in _CENTRES_BY_NAME:
        available = list(_CENTRES_BY_NAME.keys())
        raise ValueError(f"Unknown target {target!r}. Available: {available}")

    GCC_TASK_QUEUES_DIR.mkdir(parents=True, exist_ok=True)
    queue_file = GCC_TASK_QUEUES_DIR / f"{target}.jsonl"

    task_id = task_id or str(uuid4())
    entry = {
        "id": task_id,
        "prompt": task,
        "priority": priority,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "target": target,
    }

    with open(queue_file, "a", encoding="utf-8") as fh:
        fh.write(json.dumps(entry) + "\n")

    if verbose:
        print(f"[gcc] Task dispatched to {target} — id={task_id}")

    return task_id


def stop_all(verbose: bool = True) -> None:
    """
    Gracefully stop all command centres by killing their tmux sessions.
    """
    for centre in CENTRES_CONFIG:
        session_name = centre["tmux_session"]
        result = subprocess.run(
            ["tmux", "kill-session", "-t", session_name],
            capture_output=True,
            text=True,
        )
        if verbose:
            if result.returncode == 0:
                print(f"[gcc] Stopped: {session_name}")
            else:
                print(f"[gcc] {session_name} was not running")


def get_result(task_id: str) -> Optional[dict]:
    """
    Read the result file for a completed task.

    Args:
        task_id: The task ID used when dispatching.

    Returns:
        Result dict or None if not yet written.
    """
    result_file = GCC_RESULTS_DIR / f"{task_id}.json"
    if not result_file.exists():
        return None
    try:
        with open(result_file, "r", encoding="utf-8") as fh:
            return json.load(fh)
    except (json.JSONDecodeError, OSError):
        return None
