"""Spark advisor — generates ranked recommendations from accumulated intelligence.

The advisor examines Spark's event history, consolidated memories, and EIDOS
prediction accuracy to produce actionable recommendations for the swarm.

Categories
----------
- agent_performance  — "Agent X excels at Y, consider routing more Y tasks"
- bid_optimization   — "Bids on Z tasks are consistently high, room to save"
- failure_prevention — "Agent A has failed 3 recent tasks, investigate"
- system_health      — "No events in 30 min, swarm may be idle"
"""

import json
import logging
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Optional

from spark import eidos as spark_eidos
from spark import memory as spark_memory

logger = logging.getLogger(__name__)

# Minimum events before the advisor starts generating recommendations
_MIN_EVENTS = 3


@dataclass
class Advisory:
    """A single ranked recommendation."""

    category: str  # agent_performance, bid_optimization, etc.
    priority: float  # 0.0–1.0 (higher = more urgent)
    title: str  # Short headline
    detail: str  # Longer explanation
    suggested_action: str  # What to do about it
    subject: Optional[str] = None  # agent_id or None for system-level
    evidence_count: int = 0  # Number of supporting events


def generate_advisories() -> list[Advisory]:
    """Analyse Spark data and produce ranked recommendations.

    Returns advisories sorted by priority (highest first).
    """
    advisories: list[Advisory] = []

    event_count = spark_memory.count_events()
    if event_count < _MIN_EVENTS:
        advisories.append(
            Advisory(
                category="system_health",
                priority=0.3,
                title="Insufficient data",
                detail=f"Only {event_count} events captured. "
                f"Spark needs at least {_MIN_EVENTS} events to generate insights.",
                suggested_action="Run more swarm tasks to build intelligence.",
                evidence_count=event_count,
            )
        )
        return advisories

    advisories.extend(_check_failure_patterns())
    advisories.extend(_check_agent_performance())
    advisories.extend(_check_bid_patterns())
    advisories.extend(_check_prediction_accuracy())
    advisories.extend(_check_system_activity())

    advisories.sort(key=lambda a: a.priority, reverse=True)
    return advisories


def _check_failure_patterns() -> list[Advisory]:
    """Detect agents with recent failure streaks."""
    results: list[Advisory] = []
    failures = spark_memory.get_events(event_type="task_failed", limit=50)

    # Group failures by agent
    agent_failures: dict[str, int] = {}
    for ev in failures:
        aid = ev.agent_id
        if aid:
            agent_failures[aid] = agent_failures.get(aid, 0) + 1

    for aid, count in agent_failures.items():
        if count >= 2:
            results.append(
                Advisory(
                    category="failure_prevention",
                    priority=min(1.0, 0.5 + count * 0.15),
                    title=f"Agent {aid[:8]} has {count} failures",
                    detail=f"Agent {aid[:8]}... has failed {count} recent tasks. "
                    f"This pattern may indicate a capability mismatch or "
                    f"configuration issue.",
                    suggested_action=f"Review task types assigned to {aid[:8]}... "
                    f"and consider adjusting routing preferences.",
                    subject=aid,
                    evidence_count=count,
                )
            )

    return results


def _check_agent_performance() -> list[Advisory]:
    """Identify top-performing and underperforming agents."""
    results: list[Advisory] = []
    completions = spark_memory.get_events(event_type="task_completed", limit=100)
    failures = spark_memory.get_events(event_type="task_failed", limit=100)

    # Build success/failure counts per agent
    agent_success: dict[str, int] = {}
    agent_fail: dict[str, int] = {}

    for ev in completions:
        aid = ev.agent_id
        if aid:
            agent_success[aid] = agent_success.get(aid, 0) + 1

    for ev in failures:
        aid = ev.agent_id
        if aid:
            agent_fail[aid] = agent_fail.get(aid, 0) + 1

    all_agents = set(agent_success) | set(agent_fail)
    for aid in all_agents:
        wins = agent_success.get(aid, 0)
        fails = agent_fail.get(aid, 0)
        total = wins + fails
        if total < 2:
            continue

        rate = wins / total
        if rate >= 0.8 and total >= 3:
            results.append(
                Advisory(
                    category="agent_performance",
                    priority=0.6,
                    title=f"Agent {aid[:8]} excels ({rate:.0%} success)",
                    detail=f"Agent {aid[:8]}... has completed {wins}/{total} tasks "
                    f"successfully. Consider routing more tasks to this agent.",
                    suggested_action="Increase task routing weight for this agent.",
                    subject=aid,
                    evidence_count=total,
                )
            )
        elif rate <= 0.3 and total >= 3:
            results.append(
                Advisory(
                    category="agent_performance",
                    priority=0.75,
                    title=f"Agent {aid[:8]} struggling ({rate:.0%} success)",
                    detail=f"Agent {aid[:8]}... has only succeeded on {wins}/{total} tasks. "
                    f"May need different task types or capability updates.",
                    suggested_action="Review this agent's capabilities and assigned task types.",
                    subject=aid,
                    evidence_count=total,
                )
            )

    return results


def _check_bid_patterns() -> list[Advisory]:
    """Detect bid optimization opportunities."""
    results: list[Advisory] = []
    bids = spark_memory.get_events(event_type="bid_submitted", limit=100)

    if len(bids) < 5:
        return results

    # Extract bid amounts
    bid_amounts: list[int] = []
    for ev in bids:
        try:
            data = json.loads(ev.data)
            sats = data.get("bid_sats", 0)
            if sats > 0:
                bid_amounts.append(sats)
        except (json.JSONDecodeError, TypeError):
            continue

    if not bid_amounts:
        return results

    avg_bid = sum(bid_amounts) / len(bid_amounts)
    max_bid = max(bid_amounts)
    min_bid = min(bid_amounts)
    spread = max_bid - min_bid

    if spread > avg_bid * 1.5:
        results.append(
            Advisory(
                category="bid_optimization",
                priority=0.5,
                title=f"Wide bid spread ({min_bid}–{max_bid} sats)",
                detail=f"Bids range from {min_bid} to {max_bid} sats "
                f"(avg {avg_bid:.0f}). Large spread may indicate "
                f"inefficient auction dynamics.",
                suggested_action="Review agent bid strategies for consistency.",
                evidence_count=len(bid_amounts),
            )
        )

    if avg_bid > 70:
        results.append(
            Advisory(
                category="bid_optimization",
                priority=0.45,
                title=f"High average bid ({avg_bid:.0f} sats)",
                detail=f"The swarm average bid is {avg_bid:.0f} sats across "
                f"{len(bid_amounts)} bids. This may be above optimal.",
                suggested_action="Consider adjusting base bid rates for persona agents.",
                evidence_count=len(bid_amounts),
            )
        )

    return results


def _check_prediction_accuracy() -> list[Advisory]:
    """Report on EIDOS prediction accuracy."""
    results: list[Advisory] = []
    stats = spark_eidos.get_accuracy_stats()

    if stats["evaluated"] < 3:
        return results

    avg = stats["avg_accuracy"]
    if avg < 0.4:
        results.append(
            Advisory(
                category="system_health",
                priority=0.65,
                title=f"Low prediction accuracy ({avg:.0%})",
                detail=f"EIDOS predictions have averaged {avg:.0%} accuracy "
                f"over {stats['evaluated']} evaluations. The learning "
                f"model needs more data or the swarm behaviour is changing.",
                suggested_action="Continue running tasks; accuracy should improve "
                "as the model accumulates more training data.",
                evidence_count=stats["evaluated"],
            )
        )
    elif avg >= 0.75:
        results.append(
            Advisory(
                category="system_health",
                priority=0.3,
                title=f"Strong prediction accuracy ({avg:.0%})",
                detail=f"EIDOS predictions are performing well at {avg:.0%} "
                f"average accuracy over {stats['evaluated']} evaluations.",
                suggested_action="No action needed. Spark intelligence is learning effectively.",
                evidence_count=stats["evaluated"],
            )
        )

    return results


def _check_system_activity() -> list[Advisory]:
    """Check for system idle patterns."""
    results: list[Advisory] = []
    recent = spark_memory.get_events(limit=5)

    if not recent:
        results.append(
            Advisory(
                category="system_health",
                priority=0.4,
                title="No swarm activity detected",
                detail="Spark has not captured any events. "
                "The swarm may be idle or Spark event capture is not active.",
                suggested_action="Post a task to the swarm to activate the pipeline.",
            )
        )
        return results

    # Check event type distribution
    types = [e.event_type for e in spark_memory.get_events(limit=100)]
    type_counts = {}
    for t in types:
        type_counts[t] = type_counts.get(t, 0) + 1

    if "task_completed" not in type_counts and "task_failed" not in type_counts:
        if type_counts.get("task_posted", 0) > 3:
            results.append(
                Advisory(
                    category="system_health",
                    priority=0.6,
                    title="Tasks posted but none completing",
                    detail=f"{type_counts.get('task_posted', 0)} tasks posted "
                    f"but no completions or failures recorded.",
                    suggested_action="Check agent availability and auction configuration.",
                    evidence_count=type_counts.get("task_posted", 0),
                )
            )

    return results
