"""
Phase 54: Platform Metrics Collection and Time-Series Storage.

Records timestamped metric data points and provides aggregation, querying,
and dashboard summaries. Each metric is stored in its own JSONL file for
efficient append-only writes.

Storage:
  state/broker/metrics/
    {metric_name}.jsonl  — time-series data for each metric
"""

import json
import logging
import time
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional

from brynq.server import BROKER_DIR, _write_json_sync, _read_jsonl_sync, _append_jsonl_sync

logger = logging.getLogger(__name__)

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

METRICS_DIR = BROKER_DIR / "metrics"
METRICS_DIR.mkdir(parents=True, exist_ok=True)


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

def _metric_file(name: str) -> Path:
    safe = name.replace("/", "_").replace("\\", "_").replace(" ", "_")
    return METRICS_DIR / f"{safe}.jsonl"


def _parse_interval(interval: str) -> int:
    """Parse interval string like '1h', '30m', '1d' into seconds."""
    value = int(interval[:-1])
    unit = interval[-1].lower()
    if unit == "s":
        return value
    elif unit == "m":
        return value * 60
    elif unit == "h":
        return value * 3600
    elif unit == "d":
        return value * 86400
    else:
        raise ValueError(f"Unknown interval unit '{unit}'. Use s/m/h/d.")


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

def record_metric(
    name: str,
    value: float,
    tags: Optional[Dict[str, str]] = None,
) -> dict:
    """Record a metric data point with a timestamp.

    Args:
        name: Metric name (e.g. 'tasks.completed', 'response_time_ms').
        value: Numeric value.
        tags: Optional key-value tags for filtering.

    Returns:
        The recorded data point.
    """
    now = datetime.now(timezone.utc).isoformat()
    point = {
        "name": name,
        "value": value,
        "timestamp": now,
        "tags": tags or {},
    }

    _append_jsonl_sync(_metric_file(name), point)
    logger.debug("Recorded metric %s = %s", name, value)
    return point


def get_metric(
    name: str,
    since: Optional[str] = None,
    until: Optional[str] = None,
    interval: Optional[str] = None,
) -> List[dict]:
    """Retrieve time-series data for a metric.

    Args:
        name: Metric name.
        since: ISO-8601 start time (inclusive).
        until: ISO-8601 end time (inclusive).
        interval: Aggregation interval (e.g. '1h', '30m'). If None, raw points.

    Returns:
        List of data points or aggregated buckets.
    """
    points = _read_jsonl_sync(_metric_file(name))
    if not points:
        return []

    # Filter by time range
    filtered = []
    for p in points:
        ts = p.get("timestamp", "")
        if since and ts < since:
            continue
        if until and ts > until:
            continue
        filtered.append(p)

    if not interval:
        return filtered

    # Aggregate by interval
    if not filtered:
        return []

    interval_secs = _parse_interval(interval)
    buckets: Dict[str, List[float]] = {}

    for p in filtered:
        ts = p.get("timestamp", "")
        try:
            dt = datetime.fromisoformat(ts)
            epoch = dt.timestamp()
            bucket_epoch = int(epoch // interval_secs) * interval_secs
            bucket_key = datetime.fromtimestamp(bucket_epoch, tz=timezone.utc).isoformat()
        except (ValueError, TypeError):
            continue

        if bucket_key not in buckets:
            buckets[bucket_key] = []
        buckets[bucket_key].append(p["value"])

    result = []
    for bucket_ts in sorted(buckets.keys()):
        values = buckets[bucket_ts]
        result.append({
            "timestamp": bucket_ts,
            "avg": round(sum(values) / len(values), 4),
            "min": min(values),
            "max": max(values),
            "sum": round(sum(values), 4),
            "count": len(values),
        })

    return result


def get_latest(name: str) -> Optional[dict]:
    """Get the most recent data point for a metric."""
    points = _read_jsonl_sync(_metric_file(name))
    if not points:
        return None
    return points[-1]


def list_metrics() -> List[str]:
    """Return all known metric names."""
    names = []
    for path in METRICS_DIR.glob("*.jsonl"):
        name = path.stem.replace("_", ".")
        # Read first entry to get the actual name
        entries = _read_jsonl_sync(path)
        if entries:
            name = entries[0].get("name", name)
        names.append(name)
    names.sort()
    return names


def aggregate(
    name: str,
    period_hours: int = 24,
    fn: str = "avg",
) -> Optional[float]:
    """Compute an aggregate over a time period.

    Args:
        name: Metric name.
        period_hours: Look-back period in hours.
        fn: Aggregation function: avg, min, max, sum, count.

    Returns:
        Computed value or None if no data.
    """
    valid_fns = {"avg", "min", "max", "sum", "count"}
    if fn not in valid_fns:
        raise ValueError(f"Invalid function '{fn}'. Must be one of {valid_fns}")

    since = (datetime.now(timezone.utc) - timedelta(hours=period_hours)).isoformat()
    points = _read_jsonl_sync(_metric_file(name))
    values = [p["value"] for p in points if p.get("timestamp", "") >= since]

    if not values:
        return None

    if fn == "avg":
        return round(sum(values) / len(values), 4)
    elif fn == "min":
        return min(values)
    elif fn == "max":
        return max(values)
    elif fn == "sum":
        return round(sum(values), 4)
    elif fn == "count":
        return len(values)
    return None


def get_dashboard_data() -> dict:
    """Return pre-computed metrics for the main dashboard.

    Returns:
        Dict with active_agents, tasks_per_hour, success_rate, avg_response_time.
    """
    now = datetime.now(timezone.utc)
    one_hour_ago = (now - timedelta(hours=1)).isoformat()
    twenty_four_hours = (now - timedelta(hours=24)).isoformat()

    # Active agents (from agent_heartbeat metric)
    agent_points = _read_jsonl_sync(_metric_file("active_agents"))
    active_agents = 0
    if agent_points:
        active_agents = int(agent_points[-1].get("value", 0))

    # Tasks per hour
    task_points = _read_jsonl_sync(_metric_file("tasks_completed"))
    tasks_last_hour = sum(
        1 for p in task_points if p.get("timestamp", "") >= one_hour_ago
    )

    # Success rate (last 24h)
    success_points = _read_jsonl_sync(_metric_file("task_success"))
    recent_success = [p for p in success_points if p.get("timestamp", "") >= twenty_four_hours]
    success_rate = 0.0
    if recent_success:
        success_rate = round(
            sum(p["value"] for p in recent_success) / len(recent_success) * 100, 2
        )

    # Avg response time
    rt_points = _read_jsonl_sync(_metric_file("response_time_ms"))
    recent_rt = [p["value"] for p in rt_points if p.get("timestamp", "") >= twenty_four_hours]
    avg_rt = round(sum(recent_rt) / len(recent_rt), 2) if recent_rt else 0.0

    return {
        "active_agents": active_agents,
        "tasks_per_hour": tasks_last_hour,
        "success_rate": success_rate,
        "avg_response_time_ms": avg_rt,
    }
