"""
Agent Bridge — Mirror agent handoff channels into the broker.

Reads the 9 agent-to-agent handoff channels (state/handoffs/*.jsonl) and
copies new messages into broker channels (state/broker/channels/agent-*.jsonl)
so any connected terminal can observe agent activity.  Also allows terminals
to post advisory messages back to agents.

All functions are synchronous.  Missing directories are created on first use.

Agent handoff channels:
  sigma_to_alpha, sigma_to_training, omega_broadcast, delta_to_sigma,
  alpha_to_lifecycle, theta_to_incident, incident_to_theta,
  sigma_to_omega, sigma_to_theta

Broker mirror naming:
  state/broker/channels/agent-sigma_to_alpha.jsonl  (etc.)

Cursor tracking:
  state/broker/cursors/agent_bridge_{channel}.cursor
  Stores integer line offset (number of lines already synced).
"""

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from brynq.server import BROKER_DIR, CHANNELS_DIR, CURSORS_DIR

# ── Paths ─────────────────────────────────────────────────────────────────

PROJECT_ROOT = Path(__file__).resolve().parents[2]
HANDOFFS_DIR = PROJECT_ROOT / "state" / "handoffs"

# All 9 registered agent handoff channels
AGENT_CHANNELS = [
    "sigma_to_alpha",
    "sigma_to_training",
    "omega_broadcast",
    "delta_to_sigma",
    "alpha_to_lifecycle",
    "theta_to_incident",
    "incident_to_theta",
    "sigma_to_omega",
    "sigma_to_theta",
]


# ── Internal helpers ──────────────────────────────────────────────────────


def _ensure_dirs() -> None:
    """Create broker directories if they do not exist."""
    for d in (CHANNELS_DIR, CURSORS_DIR):
        d.mkdir(parents=True, exist_ok=True)


def _read_bridge_cursor(channel: str) -> int:
    """Read the sync cursor for an agent channel.

    Returns the number of JSONL lines already mirrored into the broker
    channel.  Returns 0 if the cursor file does not exist.
    """
    path = CURSORS_DIR / f"agent_bridge_{channel}.cursor"
    try:
        return int(path.read_text("utf-8").strip())
    except (FileNotFoundError, ValueError, OSError):
        return 0


def _write_bridge_cursor(channel: str, position: int) -> None:
    """Write the sync cursor for an agent channel."""
    path = CURSORS_DIR / f"agent_bridge_{channel}.cursor"
    path.write_text(str(position), encoding="utf-8")


def _read_jsonl(path: Path) -> list[dict]:
    """Read all entries from a JSONL file.  Returns empty list on error."""
    if not path.exists():
        return []
    entries: list[dict] = []
    try:
        for line in path.read_text("utf-8").splitlines():
            line = line.strip()
            if not line:
                continue
            try:
                entries.append(json.loads(line))
            except json.JSONDecodeError:
                continue
    except OSError:
        pass
    return entries


def _append_jsonl(path: Path, obj: dict) -> None:
    """Append a single JSON object as one line to a JSONL file."""
    line = json.dumps(obj, default=str) + "\n"
    with path.open("a", encoding="utf-8") as f:
        f.write(line)


def _transform_agent_message(msg: dict) -> dict:
    """Transform an agent handoff message into broker channel format.

    Adds ``platform`` and ``session_name`` fields so the message renders
    correctly when read through broker_read / broker_history.
    """
    agent_name = msg.get("from_agent", "unknown-agent")
    return {
        "id": msg.get("msg_id", ""),
        "timestamp": msg.get("timestamp", datetime.now(timezone.utc).isoformat()),
        "session_id": f"agent-{agent_name}",
        "session_name": agent_name,
        "platform": "agent",
        "channel": f"agent-{msg.get('channel', 'unknown')}",
        "msg_type": msg.get("msg_type", "info"),
        "message": _summarise_payload(msg),
        "priority": msg.get("priority", "P3"),
        "source_channel": msg.get("channel", ""),
        "payload": msg.get("payload", {}),
    }


def _summarise_payload(msg: dict) -> str:
    """Build a human-readable message string from an agent handoff entry."""
    msg_type = msg.get("msg_type", "")
    payload = msg.get("payload", {})
    priority = msg.get("priority", "P3")
    agent = msg.get("from_agent", "?")

    # For ack messages, keep it short
    if msg_type == "ack":
        acked = payload.get("acked_msg_id", "?")
        return f"[{priority}] {agent} acknowledged {acked}"

    # Build a compact summary from the payload
    parts = [f"[{priority}] {msg_type}"]
    if isinstance(payload, dict):
        for key, value in payload.items():
            parts.append(f"{key}={value}")
    elif payload:
        parts.append(str(payload))

    return " | ".join(parts)


# ── Public API ────────────────────────────────────────────────────────────


def sync_agent_channels() -> dict[str, Any]:
    """Mirror new agent handoff messages into broker channels.

    For each of the 9 agent handoff channels, reads the source JSONL,
    checks a line-offset cursor, and copies any new entries into the
    corresponding ``agent-{channel}`` broker channel.

    Messages are transformed to include ``platform: "agent"`` and
    ``session_name: <agent_name>`` so they display correctly in broker
    reads.

    Returns:
        Dict with ``synced`` (per-channel counts) and ``total`` (sum).
        Example::

            {"synced": {"sigma_to_alpha": 3, "omega_broadcast": 0}, "total": 3}
    """
    _ensure_dirs()
    synced: dict[str, int] = {}
    total = 0

    for channel in AGENT_CHANNELS:
        source_path = HANDOFFS_DIR / f"{channel}.jsonl"
        if not source_path.exists():
            synced[channel] = 0
            continue

        # Read all lines from the source
        all_entries = _read_jsonl(source_path)
        cursor = _read_bridge_cursor(channel)

        # Determine new entries (lines beyond the cursor)
        new_entries = all_entries[cursor:]
        if not new_entries:
            synced[channel] = 0
            continue

        # Write transformed entries to the broker mirror channel
        broker_path = CHANNELS_DIR / f"agent-{channel}.jsonl"
        for entry in new_entries:
            transformed = _transform_agent_message(entry)
            _append_jsonl(broker_path, transformed)

        # Advance cursor
        _write_bridge_cursor(channel, cursor + len(new_entries))
        synced[channel] = len(new_entries)
        total += len(new_entries)

    return {"synced": synced, "total": total}


def list_agent_channels() -> list[dict[str, Any]]:
    """List all agent handoff channels with message counts and last activity.

    Scans each of the 9 registered agent handoff JSONL files and returns
    metadata about each.

    Returns:
        List of dicts, each with:
        - ``channel``: channel name (e.g. ``"sigma_to_alpha"``)
        - ``messages``: total message count
        - ``last_activity``: ISO-8601 timestamp of the most recent message,
          or ``None`` if the channel is empty / missing.
    """
    results: list[dict[str, Any]] = []

    for channel in AGENT_CHANNELS:
        source_path = HANDOFFS_DIR / f"{channel}.jsonl"
        entries = _read_jsonl(source_path)

        last_activity = None
        if entries:
            # Find latest timestamp across all entries
            for entry in reversed(entries):
                ts = entry.get("timestamp")
                if ts:
                    last_activity = ts
                    break

        results.append({
            "channel": channel,
            "messages": len(entries),
            "last_activity": last_activity,
        })

    return results


def get_agent_activity(since_hours: float = 24) -> list[dict[str, Any]]:
    """Get recent agent handoff messages across all channels.

    Reads every agent handoff channel and returns messages whose timestamp
    falls within the last ``since_hours`` hours, sorted oldest-first.

    Args:
        since_hours: Look-back window in hours.  Defaults to 24.

    Returns:
        List of raw agent handoff message dicts sorted by timestamp.
    """
    cutoff = datetime.now(timezone.utc)
    messages: list[dict[str, Any]] = []

    for channel in AGENT_CHANNELS:
        source_path = HANDOFFS_DIR / f"{channel}.jsonl"
        entries = _read_jsonl(source_path)

        for entry in entries:
            ts_str = entry.get("timestamp", "")
            if not ts_str:
                continue
            try:
                ts = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
                age_hours = (cutoff - ts).total_seconds() / 3600
                if age_hours <= since_hours:
                    messages.append(entry)
            except (ValueError, TypeError):
                continue

    # Sort by timestamp ascending
    messages.sort(key=lambda m: m.get("timestamp", ""))
    return messages
