from __future__ import annotations

from typing import Any, Iterable

from .audit import AuditEvent, emit_event
from .config import get_config
from .context import RunContext
from .types import BoundaryMap, ToolRules


def _policy_names(values: Iterable[Any]) -> list[str]:
    names: list[str] = []
    for policy in values:
        name = getattr(policy, "__name__", None)
        names.append(name or repr(policy))
    return sorted(set(names))


def _rules_payload(rules: ToolRules) -> dict[str, Any]:
    return {
        "boundary": str(rules.boundary) if rules.boundary is not None else None,
        "policies": _policy_names([*rules.policies.require, *rules.policies.any_of]),
        "tag": {
            "activates": [getattr(tag, "value", str(tag)) for tag in rules.activates],
            "blocked_by": [getattr(tag, "value", str(tag)) for tag in rules.blocked_by],
        },
        "governed": rules.governed,
    }


def build_coverage_report(
    tool_rules: dict[str, ToolRules],
    mcp_summaries: dict[str, dict[str, Any]] | None = None,
) -> dict[str, Any]:
    config = get_config()
    return {
        "event": "coverage_report",
        "function_tools": {name: _rules_payload(rules) for name, rules in sorted(tool_rules.items())},
        "mcp_servers": mcp_summaries or {},
        "boundaries": _boundaries_payload(config.boundaries),
    }


def emit_coverage(
    ctx: RunContext | None,
    tool_rules: dict[str, ToolRules],
    mcp_summaries: dict[str, dict[str, Any]] | None = None,
) -> None:
    config = get_config()
    if not config.report_coverage:
        return
    report = build_coverage_report(tool_rules, mcp_summaries)
    emit_event(
        config.audit_sink,
        AuditEvent(
            event_type="coverage_report",
            tool_name=None,
            agent_id=ctx.agent_id if ctx else None,
            run_id=ctx.run_id if ctx else "coverage",
            active_tags=[],
            payload=report,
        ),
    )


def _boundaries_payload(boundaries: BoundaryMap) -> dict[str, Any]:
    payload: dict[str, Any] = {}
    for boundary, rule in boundaries.items():
        key = str(boundary)
        if rule is True:
            payload[key] = True
        else:
            if isinstance(rule, bool):
                payload[key] = rule
                continue
            payload[key] = [getattr(tag, "value", str(tag)) for tag in rule]
    return payload
