"""MyBurn Trust — payload integrity checks, hash chains, and anomaly detection."""
from __future__ import annotations

import hashlib
import json
import math
from dataclasses import dataclass, asdict
from typing import Optional

from myburn.server import PRICING

# ---------------------------------------------------------------------------
# TrustFlag — individual check result
# ---------------------------------------------------------------------------
@dataclass
class TrustFlag:
    code: str
    severity: str  # "hard" or "soft"
    message: str


# ---------------------------------------------------------------------------
# Valid model prefixes
# ---------------------------------------------------------------------------
_VALID_PREFIXES = ("claude-opus-", "claude-sonnet-", "claude-haiku-")


# ---------------------------------------------------------------------------
# Benford's Law expected distribution (digits 1-9)
# ---------------------------------------------------------------------------
_BENFORD = {d: math.log10(1 + 1 / d) for d in range(1, 10)}


# ---------------------------------------------------------------------------
# Payload hashing
# ---------------------------------------------------------------------------
def compute_payload_hash(payload: dict) -> str:
    """Compute deterministic SHA-256 hash of a payload, excluding meta-fields (keys starting with '_')."""
    filtered = {k: v for k, v in payload.items() if not k.startswith("_")}
    canonical = json.dumps(filtered, sort_keys=True, separators=(",", ":"))
    digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
    return f"sha256:{digest}"


# ---------------------------------------------------------------------------
# Individual checks
# ---------------------------------------------------------------------------
def check_model_names(payload: dict) -> list[TrustFlag]:
    """Validate that all model names start with known Claude prefixes. HARD reject on unknown."""
    flags = []
    for model in payload.get("models", []):
        name = model.get("name", "")
        if not any(name.startswith(p) for p in _VALID_PREFIXES):
            flags.append(TrustFlag(
                code="UNKNOWN_MODEL",
                severity="hard",
                message=f"Unknown model: {name}",
            ))
    return flags


def check_price_consistency(payload: dict) -> list[TrustFlag]:
    """Check per-model cost/tokens ratio against known pricing. SOFT flag."""
    flags = []
    models = payload.get("models", [])
    total_cost = payload.get("cost", 0)

    for model in models:
        name = model.get("name", "")
        tokens = model.get("tokens", 0)
        cost = model.get("cost", 0)

        if tokens <= 0 or cost <= 0:
            continue

        pricing = PRICING.get(name)
        if not pricing:
            continue

        # Effective rate: cost per million tokens
        effective_rate = cost / tokens * 1_000_000

        # Min rate = cache_read (cheapest), max rate = output (most expensive)
        min_rate = pricing["cache_read"]
        max_rate = pricing["output"]

        # ±20% tolerance
        if effective_rate < min_rate * 0.8:
            flags.append(TrustFlag(
                code="PRICE_TOO_LOW",
                severity="soft",
                message=f"{name}: effective rate ${effective_rate:.2f}/MTok below min ${min_rate:.2f}/MTok",
            ))
        elif effective_rate > max_rate * 1.2:
            flags.append(TrustFlag(
                code="PRICE_TOO_HIGH",
                severity="soft",
                message=f"{name}: effective rate ${effective_rate:.2f}/MTok above max ${max_rate:.2f}/MTok",
            ))

    # Cross-check: sum of model costs ≈ total cost
    if models and total_cost > 0:
        model_cost_sum = sum(m.get("cost", 0) for m in models)
        if model_cost_sum > 0:
            diff_pct = abs(model_cost_sum - total_cost) / total_cost
            if diff_pct > 0.20:
                flags.append(TrustFlag(
                    code="COST_SUM_MISMATCH",
                    severity="soft",
                    message=f"Model cost sum ${model_cost_sum:.2f} vs total ${total_cost:.2f} ({diff_pct:.0%} off)",
                ))

    return flags


def check_daily_sum(payload: dict) -> list[TrustFlag]:
    """Check that sum of daily costs ≈ total cost (±20%). HARD reject."""
    flags = []
    days = payload.get("days", [])
    total_cost = payload.get("cost", 0)

    if not days or total_cost <= 0:
        return flags

    day_sum = sum(d.get("cost", 0) for d in days)
    if total_cost > 0:
        diff_pct = abs(day_sum - total_cost) / total_cost
        if diff_pct > 0.20:
            flags.append(TrustFlag(
                code="DAILY_SUM_MISMATCH",
                severity="hard",
                message=f"Daily cost sum ${day_sum:.2f} vs total ${total_cost:.2f} ({diff_pct:.0%} off)",
            ))

    return flags


def check_token_consistency(payload: dict) -> list[TrustFlag]:
    """Check token sub-fields add up to total. HARD reject on mismatch."""
    flags = []
    tokens = payload.get("tokens", {})
    if not isinstance(tokens, dict):
        return flags

    total = tokens.get("total", 0)
    if total <= 0:
        return flags

    # Sub-field sum: input + output + cacheRead + cacheCreation == total
    sub_sum = (
        tokens.get("input", 0)
        + tokens.get("output", 0)
        + tokens.get("cacheRead", 0)
        + tokens.get("cacheCreation", 0)
    )

    if sub_sum != total:
        flags.append(TrustFlag(
            code="TOKEN_SUM_MISMATCH",
            severity="hard",
            message=f"Token sub-fields sum to {sub_sum} but total is {total}",
        ))

    # Cross-check: sum of model tokens ≈ total tokens (soft)
    models = payload.get("models", [])
    if models and total > 0:
        model_token_sum = sum(m.get("tokens", 0) for m in models)
        if model_token_sum > 0:
            diff_pct = abs(model_token_sum - total) / total
            if diff_pct > 0.20:
                flags.append(TrustFlag(
                    code="MODEL_TOKEN_MISMATCH",
                    severity="soft",
                    message=f"Model token sum {model_token_sum} vs total {total} ({diff_pct:.0%} off)",
                ))

    return flags


def check_active_days(payload: dict) -> list[TrustFlag]:
    """Check activeDays consistency. SOFT for mismatch with days[], HARD if streak > activeDays."""
    flags = []
    active_days = payload.get("activeDays", 0)
    streak = payload.get("streak", 0)
    days = payload.get("days", [])

    # activeDays should match len(days)
    if days and active_days != len(days):
        flags.append(TrustFlag(
            code="ACTIVE_DAYS_MISMATCH",
            severity="soft",
            message=f"activeDays={active_days} but days[] has {len(days)} entries",
        ))

    # streak cannot exceed activeDays (logically impossible)
    if streak > active_days and active_days > 0:
        flags.append(TrustFlag(
            code="STREAK_EXCEEDS_ACTIVE",
            severity="hard",
            message=f"streak={streak} exceeds activeDays={active_days}",
        ))

    return flags


def check_benford(payload: dict) -> list[TrustFlag]:
    """Apply Benford's Law to leading digits of daily costs. SOFT flag."""
    flags = []
    days = payload.get("days", [])

    # Extract leading digits from daily costs > 0
    leading_digits: list[int] = []
    for day in days:
        cost = day.get("cost", 0)
        if cost > 0:
            # Get leading digit
            s = f"{cost:.10f}".lstrip("0").lstrip(".")
            if s:
                digit = int(s[0])
                if 1 <= digit <= 9:
                    leading_digits.append(digit)

    n = len(leading_digits)
    if n < 10:
        return flags  # Too few values, skip

    # Count observed frequencies
    observed = {d: 0 for d in range(1, 10)}
    for d in leading_digits:
        observed[d] += 1

    # Chi-squared test
    chi_sq = 0.0
    for d in range(1, 10):
        expected = n * _BENFORD[d]
        chi_sq += (observed[d] - expected) ** 2 / expected

    # Threshold: relaxed for < 30 values, stricter for >= 30
    # Chi-squared critical value for 8 df at p=0.05 is 15.51, at p=0.01 is 20.09
    threshold = 25.0 if n < 30 else 20.0

    if chi_sq > threshold:
        flags.append(TrustFlag(
            code="BENFORD_ANOMALY",
            severity="soft",
            message=f"Benford's Law anomaly: chi-squared={chi_sq:.1f} (threshold={threshold}, n={n})",
        ))

    return flags


def verify_hash_chain(
    declared_prev: Optional[str],
    actual_prev: Optional[str],
) -> list[TrustFlag]:
    """Verify the hash chain link. SOFT flag on mismatch (could be re-install)."""
    flags = []

    if declared_prev is None or declared_prev == "genesis":
        # First push or client doesn't support chain yet — OK
        return flags

    if actual_prev is None:
        # First push on server side — genesis is expected, anything else is OK too
        return flags

    if declared_prev != actual_prev:
        flags.append(TrustFlag(
            code="CHAIN_BREAK",
            severity="soft",
            message=f"Hash chain break: declared prev={declared_prev[:20]}... vs stored={actual_prev[:20]}...",
        ))

    return flags


# ---------------------------------------------------------------------------
# Scoring
# ---------------------------------------------------------------------------
def compute_trust_score(flags: list[TrustFlag]) -> float:
    """Compute trust score from flags. 1.0 = perfect, 0.0 = floor."""
    score = 1.0
    for f in flags:
        if f.severity == "hard":
            score -= 0.5
        elif f.severity == "soft":
            score -= 0.1
    return max(0.0, round(score, 2))


# ---------------------------------------------------------------------------
# Orchestrator
# ---------------------------------------------------------------------------
def validate_trust(
    payload: dict,
    prev_hash: Optional[str] = None,
) -> tuple[str, list[dict], float, list[TrustFlag]]:
    """Run all trust checks on a payload.

    Returns:
        (payload_hash, flag_dicts, trust_score, raw_flags)
    """
    payload_hash = compute_payload_hash(payload)

    flags: list[TrustFlag] = []
    flags.extend(check_model_names(payload))
    flags.extend(check_price_consistency(payload))
    flags.extend(check_daily_sum(payload))
    flags.extend(check_token_consistency(payload))
    flags.extend(check_active_days(payload))
    flags.extend(check_benford(payload))

    # Hash chain
    declared_prev = payload.get("_prevHash")
    flags.extend(verify_hash_chain(declared_prev, prev_hash))

    score = compute_trust_score(flags)
    flag_dicts = [asdict(f) for f in flags]

    return payload_hash, flag_dicts, score, flags
