"""Renderable implementations for guardrail observability."""

from __future__ import annotations

import time
from typing import Any, Dict, List, Optional, Tuple

from briefcase.guardrails.framework import (
    AuditWrapper,
    Effect,
    EvalRequest,
    EvalResult,
    GuardrailEnv,
    Renderable,
)


class RenderMixin:
    """Mixin that adds Renderable support to any BaseGuardrailEnv.

    Tracks last evaluation and provides render() in all 3 modes.
    Mix in alongside BaseGuardrailEnv:

        class MyEnv(RenderMixin, BaseGuardrailEnv):
            ...

    After each evaluate(), call _record_for_render(result) to update
    the render state.
    """

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)

    _render_eval_count: int = 0
    _render_last_effect: Optional[Effect] = None
    _render_last_time_ms: float = 0.0

    def _record_for_render(self, result: EvalResult) -> None:
        """Call after evaluation to update render state."""
        self._render_eval_count += 1
        self._render_last_effect = result.effect
        self._render_last_time_ms = result.eval_time_ms

    def render(self, mode: str = "human") -> Dict[str, Any]:
        """Produce structured observation.

        Modes:
          "human" — readable strings
          "json"  — JSON-serializable values
          "otel"  — OTel attribute conventions
        """
        if mode == "otel":
            return {
                "guardrail.eval_count": self._render_eval_count,
                "guardrail.last_effect": (
                    self._render_last_effect.value if self._render_last_effect else None
                ),
                "guardrail.last_eval_time_ms": self._render_last_time_ms,
            }
        if mode == "json":
            return {
                "eval_count": self._render_eval_count,
                "last_effect": (
                    self._render_last_effect.value if self._render_last_effect else None
                ),
                "last_eval_time_ms": self._render_last_time_ms,
            }
        # human
        effect_str = self._render_last_effect.value if self._render_last_effect else "none"
        return {
            "summary": (
                f"Evaluations: {self._render_eval_count}, "
                f"Last: {effect_str}, "
                f"Time: {self._render_last_time_ms:.2f}ms"
            ),
        }


class RenderableAuditWrapper(AuditWrapper):
    """AuditWrapper with Renderable support.

    Extends AuditWrapper to provide render() with allow/deny counts,
    rates, and total evaluations. Natural fit since AuditWrapper
    already tracks every evaluation.
    """

    def render(self, mode: str = "human") -> Dict[str, Any]:
        """Render audit statistics."""
        log = self.audit_log
        total = len(log)
        allow_count = sum(1 for _, r in log if r.effect == Effect.ALLOW)
        deny_count = total - allow_count
        allow_rate = allow_count / max(total, 1)
        deny_rate = deny_count / max(total, 1)

        if mode == "otel":
            return {
                "guardrail.audit.total": total,
                "guardrail.audit.allow_count": allow_count,
                "guardrail.audit.deny_count": deny_count,
                "guardrail.audit.allow_rate": allow_rate,
                "guardrail.audit.deny_rate": deny_rate,
            }
        if mode == "json":
            return {
                "total": total,
                "allow_count": allow_count,
                "deny_count": deny_count,
                "allow_rate": allow_rate,
                "deny_rate": deny_rate,
            }
        # human
        return {
            "summary": (
                f"Audit: {total} evaluations, "
                f"{allow_count} allowed ({allow_rate:.0%}), "
                f"{deny_count} denied ({deny_rate:.0%})"
            ),
        }
