from __future__ import annotations

import math
from datetime import datetime, timezone
from typing import Optional

from receipts.models import ForecastScoreReceipt


def _clamp_probability(p: float) -> float:
    return min(1.0 - 1e-6, max(1e-6, p))


def brier_score(p_success: float, outcome: bool) -> float:
    """Compute Brier score for a binary outcome."""
    p = max(0.0, min(1.0, p_success))
    o = 1.0 if outcome else 0.0
    return (p - o) ** 2


def log_loss(p_success: float, outcome: bool) -> float:
    """Compute log loss for a binary outcome (clamped for stability)."""
    p = _clamp_probability(p_success)
    o = 1.0 if outcome else 0.0
    return -(o * math.log(p) + (1.0 - o) * math.log(1.0 - p))


def score_forecast(
    *,
    forecast_receipt_id: str,
    forecaster_id: Optional[str],
    target_type: Optional[str],
    p_success: Optional[float],
    horizon_s: Optional[int],
    outcome: Optional[bool],
    occurred_at: Optional[datetime] = None,
    evaluated_at: Optional[datetime] = None,
) -> ForecastScoreReceipt:
    """
    Build a ForecastScoreReceipt from a forecast and observed outcome.
    """
    if evaluated_at is None:
        evaluated_at = datetime.now(timezone.utc)

    outcome_label = "unknown"
    if outcome is True:
        outcome_label = "success"
    elif outcome is False:
        outcome_label = "failure"

    score_brier = None
    score_log = None
    if p_success is not None and outcome is not None:
        score_brier = brier_score(p_success, outcome)
        score_log = log_loss(p_success, outcome)

    time_to_regret_ms = None
    if occurred_at:
        delta = evaluated_at - occurred_at
        time_to_regret_ms = int(delta.total_seconds() * 1000)

    return ForecastScoreReceipt(
        forecast_receipt_id=forecast_receipt_id,
        forecaster_id=forecaster_id,
        target_type=target_type,
        horizon_s=horizon_s,
        p_success=p_success,
        outcome=outcome_label,
        score_brier=score_brier,
        score_log=score_log,
        time_to_regret_ms=time_to_regret_ms,
        evaluated_at=evaluated_at,
    )
