from __future__ import annotations

from dataclasses import dataclass, field, replace
import contextvars
from typing import Any

from .audit import AuditLogger, AuditSink
from .types import BoundaryMap


@dataclass(frozen=True)
class RegulatorConfig:
    boundaries: BoundaryMap = field(default_factory=dict)
    mode: str = "enforce"
    require_annotations: bool = False
    audit_sink: AuditSink | None = None
    report_coverage: bool = True

    def with_updates(self, **kwargs: Any) -> "RegulatorConfig":
        return replace(self, **kwargs)


_DEFAULT_CONFIG = RegulatorConfig(
    boundaries={},
    mode="enforce",
    require_annotations=False,
    audit_sink=AuditLogger(),
    report_coverage=True,
)

_UNSET = object()

_GLOBAL_CONFIG: RegulatorConfig = _DEFAULT_CONFIG
_CONFIG_OVERRIDE: contextvars.ContextVar[RegulatorConfig | None] = contextvars.ContextVar(
    "rgltr_config_override",
    default=None,
)


def configure(
    *,
    boundaries: BoundaryMap | None = None,
    mode: str | None = None,
    require_annotations: bool | None = None,
    audit_sink: AuditSink | None | object = _UNSET,
    report_coverage: bool | None = None,
) -> None:
    global _GLOBAL_CONFIG
    updates: dict[str, Any] = {}
    if boundaries is not None:
        updates["boundaries"] = boundaries
    if mode is not None:
        updates["mode"] = mode
    if require_annotations is not None:
        updates["require_annotations"] = require_annotations
    if audit_sink is not _UNSET:
        updates["audit_sink"] = audit_sink
    if report_coverage is not None:
        updates["report_coverage"] = report_coverage
    _GLOBAL_CONFIG = _GLOBAL_CONFIG.with_updates(**updates)


def reset_config() -> None:
    global _GLOBAL_CONFIG
    _GLOBAL_CONFIG = _DEFAULT_CONFIG


def get_config() -> RegulatorConfig:
    override = _CONFIG_OVERRIDE.get()
    return override or _GLOBAL_CONFIG


class config_scope:
    def __init__(self, config: RegulatorConfig | None) -> None:
        self._config = config
        self._token: contextvars.Token[RegulatorConfig | None] | None = None

    def __enter__(self) -> None:
        if self._config is not None:
            self._token = _CONFIG_OVERRIDE.set(self._config)

    def __exit__(self, *_exc: object) -> None:
        if self._token is not None:
            _CONFIG_OVERRIDE.reset(self._token)

    async def __aenter__(self) -> None:
        self.__enter__()

    async def __aexit__(self, *_exc: object) -> None:
        self.__exit__()
