"""
Analysis helpers.

Pure functions that sort, filter, and summarise raw market dicts
returned by the Polymarket API.
"""

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


def safe_float(value: Any, default: float = 0.0) -> float:
    """Convert a value to float, returning *default* on failure."""
    try:
        return float(value)
    except (TypeError, ValueError):
        return default


def safe_int(value: Any, default: int = 0) -> int:
    """Convert a value to int, returning *default* on failure."""
    try:
        return int(float(value))
    except (TypeError, ValueError):
        return default


def parse_iso(date_str: str | None) -> datetime | None:
    """Parse an ISO-8601 date string into a timezone-aware datetime."""
    if not date_str:
        return None
    try:
        dt = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return dt
    except (ValueError, AttributeError):
        return None


# ── Ranking helpers ──────────────────────────────────────────────

def rank_by_volume(markets: list[dict], limit: int = 10) -> list[dict]:
    """Return the top *limit* markets ordered by 24-hour volume."""
    return sorted(
        markets,
        key=lambda m: safe_float(m.get("volume24hr")),
        reverse=True,
    )[:limit]


def rank_by_newest(markets: list[dict], limit: int = 10) -> list[dict]:
    """Return the most recently created markets."""
    def _created(m: dict) -> str:
        return m.get("createdAt") or m.get("created_at") or ""
    return sorted(markets, key=_created, reverse=True)[:limit]


def filter_closing_soon(markets: list[dict], hours: int = 24) -> list[dict]:
    """Return markets whose end date is within *hours* from now."""
    now = datetime.now(timezone.utc)
    result: list[dict] = []
    for m in markets:
        end = parse_iso(m.get("endDate") or m.get("end_date_iso") or m.get("endDateIso"))
        if end and 0 < (end - now).total_seconds() <= hours * 3600:
            result.append(m)
    return sorted(
        result,
        key=lambda m: parse_iso(
            m.get("endDate") or m.get("end_date_iso") or m.get("endDateIso")
        ) or now,
    )


# ── Formatting helpers ──────────────────────────────────────────

def _parse_probability(raw: Any) -> float:
    """Extract the 'Yes' probability from the API's outcomePrices field.

    The Gamma API returns outcomePrices as a JSON-encoded string
    like '["0.62", "0.38"]' where index 0 is the Yes price.
    """
    if isinstance(raw, str):
        try:
            parsed = json.loads(raw)
            return safe_float(parsed[0])
        except (json.JSONDecodeError, IndexError):
            return safe_float(raw)
    if isinstance(raw, list) and raw:
        return safe_float(raw[0])
    return 0.5


def format_market(m: dict) -> dict:
    """Normalise a raw market dict into a clean, agent-friendly shape."""
    clob_ids = m.get("clobTokenIds")
    if isinstance(clob_ids, str):
        try:
            clob_ids = json.loads(clob_ids)
        except json.JSONDecodeError:
            clob_ids = []

    return {
        "market_id": m.get("id") or m.get("conditionId") or "",
        "question": m.get("question") or "",
        "probability": _parse_probability(m.get("outcomePrices")),
        "volume_24h": safe_float(m.get("volume24hr")),
        "volume_total": safe_float(m.get("volume")),
        "liquidity": safe_float(m.get("liquidity")),
        "category": m.get("category") or m.get("groupSlug") or "unknown",
        "end_date": m.get("endDate") or m.get("endDateIso") or m.get("end_date_iso") or "",
        "active": m.get("active", True),
        "closed": m.get("closed", False),
        "slug": m.get("slug") or "",
        "token_ids": clob_ids or [],
    }


def format_market_summary(m: dict) -> dict:
    """Build a detailed summary dict for a single market."""
    base = format_market(m)

    outcomes_raw = m.get("outcomes") or "[]"
    if isinstance(outcomes_raw, str):
        try:
            outcomes_raw = json.loads(outcomes_raw)
        except json.JSONDecodeError:
            pass

    base.update({
        "description": (m.get("description") or "")[:500],
        "outcomes": outcomes_raw,
        "best_bid": safe_float(m.get("bestBid")),
        "best_ask": safe_float(m.get("bestAsk")),
        "spread": safe_float(m.get("spread")),
        "last_trade_price": safe_float(m.get("lastTradePrice")),
        "created_at": m.get("createdAt") or m.get("created_at") or "",
        "resolution_source": m.get("resolutionSource") or "",
        "condition_id": m.get("conditionId") or "",
    })
    return base


def format_orderbook(book: dict) -> dict:
    """Summarise an orderbook response."""
    bids = book.get("bids") or []
    asks = book.get("asks") or []
    return {
        "market": book.get("market") or "",
        "asset_id": book.get("asset_id") or "",
        "last_trade_price": safe_float(book.get("last_trade_price")),
        "bids_count": len(bids),
        "asks_count": len(asks),
        "best_bid": safe_float(bids[0].get("price")) if bids else None,
        "best_ask": safe_float(asks[0].get("price")) if asks else None,
        "spread": round(
            safe_float(asks[0].get("price")) - safe_float(bids[0].get("price")), 4
        ) if bids and asks else None,
        "bids": [{"price": safe_float(b.get("price")), "size": safe_float(b.get("size"))} for b in bids[:10]],
        "asks": [{"price": safe_float(a.get("price")), "size": safe_float(a.get("size"))} for a in asks[:10]],
    }


def format_price_history(data: dict) -> dict:
    """Format a price history response into a clean summary."""
    history = data.get("history") or []
    if not history:
        return {"points": 0, "history": []}

    prices = [safe_float(p.get("p")) for p in history]
    return {
        "points": len(history),
        "first_price": prices[0] if prices else None,
        "last_price": prices[-1] if prices else None,
        "min_price": min(prices) if prices else None,
        "max_price": max(prices) if prices else None,
        "price_change": round(prices[-1] - prices[0], 4) if len(prices) >= 2 else None,
        "price_change_pct": round((prices[-1] - prices[0]) / prices[0] * 100, 2) if len(prices) >= 2 and prices[0] > 0 else None,
        "history": [{"t": p.get("t"), "p": safe_float(p.get("p"))} for p in history],
    }
