"""
Phase 97: Platform Operational Cost Tracking & Optimization.

Records, queries, budgets, forecasts, and optimizes every cost incurred by
the platform. Categories cover compute, storage, api_calls, bandwidth, and
marketplace_fee. Supports per-agent cost attribution.

Storage:
  state/broker/costs/
    ledger.jsonl       — append-only cost records
    budgets.json       — spending limits per category
"""

import json
import time
import uuid
from collections import defaultdict
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

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

COSTS_DIR = BROKER_DIR / "costs"
LEDGER_FILE = COSTS_DIR / "ledger.jsonl"
BUDGETS_FILE = COSTS_DIR / "budgets.json"

COSTS_DIR.mkdir(parents=True, exist_ok=True)

VALID_CATEGORIES = ("compute", "storage", "api_calls", "bandwidth", "marketplace_fee")


# ── Helpers ────────────────────────────────────────────────────────────────

def _load_budgets() -> dict:
    if not BUDGETS_FILE.exists():
        return {}
    try:
        return json.loads(BUDGETS_FILE.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        return {}


def _save_budgets(budgets: dict) -> None:
    _write_json_sync(BUDGETS_FILE, budgets)


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

def record_cost(category: str, amount: float, currency: str = "credits",
                description: str = "", agent_id: Optional[str] = None) -> dict:
    """Record a cost entry. Categories: compute, storage, api_calls, bandwidth, marketplace_fee."""
    if category not in VALID_CATEGORIES:
        raise ValueError(f"Invalid category '{category}'. Must be one of {VALID_CATEGORIES}")
    if amount < 0:
        raise ValueError("Amount must be non-negative")
    entry = {
        "cost_id": uuid.uuid4().hex[:12],
        "category": category,
        "amount": amount,
        "currency": currency,
        "description": description,
        "agent_id": agent_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "epoch": time.time(),
    }
    _append_jsonl_sync(LEDGER_FILE, entry)
    return entry


def get_costs(category: Optional[str] = None, agent_id: Optional[str] = None,
              since: Optional[str] = None, until: Optional[str] = None) -> List[dict]:
    """Query costs with optional filters."""
    entries = _read_jsonl_sync(LEDGER_FILE)
    results = []
    for e in entries:
        if category and e.get("category") != category:
            continue
        if agent_id and e.get("agent_id") != agent_id:
            continue
        ts = e.get("timestamp", "")
        if since and ts < since:
            continue
        if until and ts > until:
            continue
        results.append(e)
    return results


def get_cost_summary(period_hours: int = 24) -> dict:
    """Total by category, top spenders, and trend for the given period."""
    cutoff = (datetime.now(timezone.utc) - timedelta(hours=period_hours)).isoformat()
    entries = _read_jsonl_sync(LEDGER_FILE)
    recent = [e for e in entries if e.get("timestamp", "") >= cutoff]
    by_category: Dict[str, float] = defaultdict(float)
    by_agent: Dict[str, float] = defaultdict(float)
    total = 0.0
    for e in recent:
        amt = e.get("amount", 0)
        by_category[e.get("category", "unknown")] += amt
        aid = e.get("agent_id")
        if aid:
            by_agent[aid] += amt
        total += amt
    top_spenders = sorted(by_agent.items(), key=lambda x: x[1], reverse=True)[:5]
    # Trend: split period into two halves
    mid = (datetime.now(timezone.utc) - timedelta(hours=period_hours / 2)).isoformat()
    first_half = sum(e.get("amount", 0) for e in recent if e.get("timestamp", "") < mid)
    second_half = sum(e.get("amount", 0) for e in recent if e.get("timestamp", "") >= mid)
    if first_half > 0:
        trend_pct = round(((second_half - first_half) / first_half) * 100, 2)
    else:
        trend_pct = 0 if second_half == 0 else 100.0
    return {
        "period_hours": period_hours,
        "total": round(total, 4),
        "by_category": dict(by_category),
        "top_spenders": [{"agent_id": a, "total": round(t, 4)} for a, t in top_spenders],
        "trend_pct": trend_pct,
        "entry_count": len(recent),
    }


def set_budget(category: str, limit: float, period: str = "daily") -> dict:
    """Set a spending limit for a category."""
    if category not in VALID_CATEGORIES:
        raise ValueError(f"Invalid category '{category}'. Must be one of {VALID_CATEGORIES}")
    if period not in ("daily", "weekly", "monthly"):
        raise ValueError(f"Invalid period '{period}'. Must be daily, weekly, or monthly")
    budgets = _load_budgets()
    budgets[category] = {"limit": limit, "period": period}
    _save_budgets(budgets)
    return budgets[category]


def check_budget(category: str) -> tuple:
    """Returns (within_budget, remaining, usage_pct)."""
    budgets = _load_budgets()
    if category not in budgets:
        return (True, float("inf"), 0.0)
    budget = budgets[category]
    limit = budget["limit"]
    period = budget["period"]
    hours = {"daily": 24, "weekly": 168, "monthly": 720}[period]
    cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
    entries = _read_jsonl_sync(LEDGER_FILE)
    spent = sum(
        e.get("amount", 0)
        for e in entries
        if e.get("category") == category and e.get("timestamp", "") >= cutoff
    )
    remaining = max(limit - spent, 0)
    usage_pct = round((spent / limit) * 100, 2) if limit > 0 else 0.0
    return (spent <= limit, round(remaining, 4), usage_pct)


def get_cost_forecast(days: int = 7) -> dict:
    """Project future costs based on recent daily average."""
    entries = _read_jsonl_sync(LEDGER_FILE)
    if not entries:
        return {"forecast_days": days, "projected_total": 0, "daily_average": 0,
                "by_category": {}}
    # Calculate daily averages from last 7 days
    week_cutoff = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
    recent = [e for e in entries if e.get("timestamp", "") >= week_cutoff]
    if not recent:
        recent = entries  # use all data if less than 7 days
    total_recent = sum(e.get("amount", 0) for e in recent)
    # Determine time span
    epochs = [e.get("epoch", 0) for e in recent if e.get("epoch")]
    if len(epochs) >= 2:
        span_days = max((max(epochs) - min(epochs)) / 86400, 1)
    else:
        span_days = 1
    daily_avg = total_recent / span_days
    by_cat: Dict[str, float] = defaultdict(float)
    for e in recent:
        by_cat[e.get("category", "unknown")] += e.get("amount", 0)
    cat_daily = {c: round(v / span_days * days, 4) for c, v in by_cat.items()}
    return {
        "forecast_days": days,
        "projected_total": round(daily_avg * days, 4),
        "daily_average": round(daily_avg, 4),
        "by_category": cat_daily,
    }


def optimize_suggestions() -> List[dict]:
    """Suggest cost reductions based on usage patterns."""
    entries = _read_jsonl_sync(LEDGER_FILE)
    if not entries:
        return [{"suggestion": "No cost data yet — start tracking to get optimization tips."}]
    by_cat: Dict[str, float] = defaultdict(float)
    for e in entries:
        by_cat[e.get("category", "unknown")] += e.get("amount", 0)
    total = sum(by_cat.values())
    suggestions: List[dict] = []
    # High-cost categories
    for cat, amount in sorted(by_cat.items(), key=lambda x: x[1], reverse=True):
        pct = (amount / total * 100) if total else 0
        if pct > 40:
            suggestions.append({
                "category": cat,
                "pct_of_total": round(pct, 1),
                "suggestion": f"{cat} accounts for {round(pct,1)}% of spending. "
                              f"Consider caching, batching, or tiered usage.",
            })
    # Budget overruns
    budgets = _load_budgets()
    for cat, budget in budgets.items():
        within, remaining, usage_pct = check_budget(cat)
        if not within:
            suggestions.append({
                "category": cat,
                "suggestion": f"{cat} is over budget ({usage_pct}% used). "
                              f"Review recent entries and reduce usage.",
            })
        elif usage_pct > 80:
            suggestions.append({
                "category": cat,
                "suggestion": f"{cat} at {usage_pct}% of budget — approaching limit.",
            })
    if not suggestions:
        suggestions.append({"suggestion": "Costs look healthy. No optimizations needed."})
    return suggestions
