"""
Phase 28: Platform Analytics — Real-Time Metrics for the CEO Dashboard.

Aggregates data from every module into a single analytics layer:
agent activity, task throughput, swarm health, revenue, social engagement.

Designed to power a CEO-level dashboard where one glance tells you
how the entire platform is performing.

Storage:
  state/broker/analytics/
    snapshots/{timestamp}.json  — periodic snapshots
    metrics.json                — latest aggregated metrics
"""

import json
import os
import time
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Optional

from brynq.server import (
    BROKER_DIR,
    CHANNELS_DIR,
    SESSIONS_DIR,
    _write_json_sync,
)

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

ANALYTICS_DIR = BROKER_DIR / "analytics"
SNAPSHOTS_DIR = ANALYTICS_DIR / "snapshots"
METRICS_FILE = ANALYTICS_DIR / "metrics.json"

for _d in (ANALYTICS_DIR, SNAPSHOTS_DIR):
    _d.mkdir(parents=True, exist_ok=True)


# ── Core Metrics Collection ───────────────────────────────────────────────


def collect_agent_metrics() -> dict:
    """Count active sessions and platform distribution."""
    agents = {"total": 0, "by_platform": {}}
    if SESSIONS_DIR.exists():
        for f in SESSIONS_DIR.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                agents["total"] += 1
                plat = data.get("platform", "unknown")
                agents["by_platform"][plat] = agents["by_platform"].get(plat, 0) + 1
            except (json.JSONDecodeError, OSError):
                continue
    return agents


def collect_channel_metrics() -> dict:
    """Count channels and total messages."""
    channels = {"total": 0, "total_messages": 0, "by_channel": {}}
    if CHANNELS_DIR.exists():
        for f in CHANNELS_DIR.glob("*.jsonl"):
            name = f.stem
            try:
                count = sum(1 for _ in open(f, encoding="utf-8"))
            except OSError:
                count = 0
            channels["total"] += 1
            channels["total_messages"] += count
            channels["by_channel"][name] = count
    return channels


def collect_task_metrics() -> dict:
    """Count tasks by status."""
    tasks_dir = BROKER_DIR / "tasks"
    tasks = {"total": 0, "by_status": {}, "by_priority": {}}
    if tasks_dir.exists():
        for f in tasks_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                tasks["total"] += 1
                status = data.get("status", "unknown")
                priority = data.get("priority", "normal")
                tasks["by_status"][status] = tasks["by_status"].get(status, 0) + 1
                tasks["by_priority"][priority] = tasks["by_priority"].get(priority, 0) + 1
            except (json.JSONDecodeError, OSError):
                continue
    return tasks


def collect_profile_metrics() -> dict:
    """Aggregate profile stats: reputation distribution, skills."""
    profiles_dir = BROKER_DIR / "profiles"
    profiles = {"total": 0, "avg_reputation": 0.0, "top_skills": {}, "availability": {}}
    if not profiles_dir.exists():
        return profiles

    reps = []
    for f in profiles_dir.glob("*.json"):
        if f.name.startswith("_"):
            continue
        try:
            data = json.loads(f.read_text("utf-8"))
            profiles["total"] += 1
            reps.append(data.get("reputation", 5.0))
            avail = data.get("availability", "unknown")
            profiles["availability"][avail] = profiles["availability"].get(avail, 0) + 1
            for skill in data.get("skills", []):
                profiles["top_skills"][skill] = profiles["top_skills"].get(skill, 0) + 1
        except (json.JSONDecodeError, OSError):
            continue

    if reps:
        profiles["avg_reputation"] = round(sum(reps) / len(reps), 2)

    # Keep only top 20 skills
    profiles["top_skills"] = dict(
        sorted(profiles["top_skills"].items(), key=lambda x: x[1], reverse=True)[:20]
    )
    return profiles


def collect_social_metrics() -> dict:
    """Count social posts, follows, reactions."""
    social = {"total_posts": 0, "total_reactions": 0, "total_follows": 0}
    posts_dir = BROKER_DIR / "social" / "posts"
    follows_dir = BROKER_DIR / "social" / "follows"

    if posts_dir.exists():
        for f in posts_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                social["total_posts"] += 1
                social["total_reactions"] += sum(data.get("reaction_counts", {}).values())
            except (json.JSONDecodeError, OSError):
                continue

    if follows_dir.exists():
        for f in follows_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                social["total_follows"] += len(data.get("following", []))
            except (json.JSONDecodeError, OSError):
                continue

    return social


def collect_economy_metrics() -> dict:
    """Summarize marketplace payment stats."""
    economy = {"total_credits": 0.0, "escrow_held": 0.0, "platform_revenue": 0.0}
    balances_file = BROKER_DIR / "payments" / "balances.json"
    escrow_dir = BROKER_DIR / "payments" / "escrow"

    if balances_file.exists():
        try:
            balances = json.loads(balances_file.read_text("utf-8"))
            economy["total_credits"] = round(sum(balances.values()), 2)
            economy["platform_revenue"] = round(balances.get("brynq_platform", 0.0), 2)
        except (json.JSONDecodeError, OSError):
            pass

    if escrow_dir.exists():
        for f in escrow_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                if data.get("status") == "held":
                    economy["escrow_held"] += data.get("amount", 0)
            except (json.JSONDecodeError, OSError):
                continue
    economy["escrow_held"] = round(economy["escrow_held"], 2)
    return economy


def collect_federation_metrics() -> dict:
    """Count federation peers and trust receipts."""
    federation = {"total_peers": 0, "total_receipts": 0, "active_peers": 0}
    peers_dir = BROKER_DIR / "federation" / "peers"
    receipts_dir = BROKER_DIR / "federation" / "receipts"

    if peers_dir.exists():
        for f in peers_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text("utf-8"))
                federation["total_peers"] += 1
                if data.get("status") == "active":
                    federation["active_peers"] += 1
            except (json.JSONDecodeError, OSError):
                continue

    if receipts_dir.exists():
        federation["total_receipts"] = len(list(receipts_dir.glob("*.json")))

    return federation


# ── Aggregate Dashboard ───────────────────────────────────────────────────


def collect_all_metrics() -> dict:
    """Collect all platform metrics into a single dashboard payload.

    This is the one-call-gets-everything function for the CEO dashboard.
    """
    metrics = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "agents": collect_agent_metrics(),
        "channels": collect_channel_metrics(),
        "tasks": collect_task_metrics(),
        "profiles": collect_profile_metrics(),
        "social": collect_social_metrics(),
        "economy": collect_economy_metrics(),
        "federation": collect_federation_metrics(),
    }

    # Compute health score (0-100)
    health = 100
    if metrics["agents"]["total"] == 0:
        health -= 30
    if metrics["tasks"]["by_status"].get("failed", 0) > metrics["tasks"]["by_status"].get("completed", 0):
        health -= 20
    if metrics["channels"]["total_messages"] == 0:
        health -= 10

    metrics["health_score"] = max(0, health)

    _write_json_sync(METRICS_FILE, metrics)
    return metrics


def get_latest_metrics() -> Optional[dict]:
    """Get the most recently collected metrics."""
    try:
        return json.loads(METRICS_FILE.read_text("utf-8"))
    except (FileNotFoundError, json.JSONDecodeError, OSError):
        return None


# ── Snapshots (Historical Tracking) ───────────────────────────────────────


def take_snapshot() -> dict:
    """Take a metrics snapshot for historical tracking."""
    metrics = collect_all_metrics()
    ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    _write_json_sync(SNAPSHOTS_DIR / f"{ts}.json", metrics)
    return {"snapshot_id": ts, "timestamp": metrics["timestamp"]}


def list_snapshots(limit: int = 20) -> list[dict]:
    """List available metric snapshots."""
    snapshots = []
    for f in sorted(SNAPSHOTS_DIR.glob("*.json"), reverse=True):
        snapshots.append({
            "snapshot_id": f.stem,
            "file": str(f),
            "size_bytes": f.stat().st_size,
        })
        if len(snapshots) >= limit:
            break
    return snapshots


def get_snapshot(snapshot_id: str) -> Optional[dict]:
    """Load a specific historical snapshot."""
    path = SNAPSHOTS_DIR / f"{snapshot_id}.json"
    try:
        return json.loads(path.read_text("utf-8"))
    except (FileNotFoundError, json.JSONDecodeError, OSError):
        return None


def compare_snapshots(snapshot_a: str, snapshot_b: str) -> dict:
    """Compare two snapshots and show deltas."""
    a = get_snapshot(snapshot_a)
    b = get_snapshot(snapshot_b)
    if not a or not b:
        return {"error": "One or both snapshots not found"}

    deltas = {}
    # Compare top-level numeric fields
    for section in ("agents", "channels", "tasks", "social", "economy", "federation"):
        a_sec = a.get(section, {})
        b_sec = b.get(section, {})
        section_delta = {}
        for key in set(list(a_sec.keys()) + list(b_sec.keys())):
            av = a_sec.get(key)
            bv = b_sec.get(key)
            if isinstance(av, (int, float)) and isinstance(bv, (int, float)):
                section_delta[key] = round(bv - av, 2)
        if section_delta:
            deltas[section] = section_delta

    health_a = a.get("health_score", 0)
    health_b = b.get("health_score", 0)
    deltas["health_score_delta"] = health_b - health_a

    return {
        "from": snapshot_a,
        "to": snapshot_b,
        "deltas": deltas,
    }
