"""Taxonomy invent freeze policy — dependency-inverted gate for auto-create.

Default: frozen (MASTER_TAXONOMY_AUTO_INVENT=false). Intentional dashboard/API
callers pass allow_invent=True. Operator unlock (import UI / confirm payload)
sets a request-scoped override via taxonomy_auto_invent_enabled().

Remote category/collection provision (Magento create_category, Shopify collection
create) is gated by the same freeze so outbound channels cannot be polluted while
master taxonomy is locked. Pass allow_provision=True only when intentionally
pushing existing master nodes to remotes.
"""

from __future__ import annotations

from contextlib import contextmanager
from contextvars import ContextVar
from typing import Iterator, Optional, Protocol


_auto_invent_override: ContextVar[Optional[bool]] = ContextVar(
    "taxonomy_auto_invent_override",
    default=None,
)
_remote_provision_override: ContextVar[Optional[bool]] = ContextVar(
    "taxonomy_remote_provision_override",
    default=None,
)


class TaxonomyInventPolicy(Protocol):
    def can_auto_invent(self) -> bool: ...

    def can_provision_remotes(self) -> bool: ...


class EnvTaxonomyInventPolicy:
    """ENV-backed policy with optional request-scoped override."""

    def can_auto_invent(self) -> bool:
        override = _auto_invent_override.get()
        if override is not None:
            return bool(override)
        from settings import load_master_taxonomy_freeze_config

        return bool(load_master_taxonomy_freeze_config().auto_invent)

    def can_provision_remotes(self) -> bool:
        """Remote create follows invent freeze unless explicitly overridden."""
        override = _remote_provision_override.get()
        if override is not None:
            return bool(override)
        # Same ENV as invent: freeze local + remote create together.
        return self.can_auto_invent()


_default_policy: TaxonomyInventPolicy = EnvTaxonomyInventPolicy()


def get_taxonomy_invent_policy() -> TaxonomyInventPolicy:
    return _default_policy


def can_auto_invent(*, allow_invent: Optional[bool] = None) -> bool:
    """Return whether auto-invent of collections/nodes is allowed.

    Explicit allow_invent wins (intentional API / unlock). Otherwise consult policy.
    """
    if allow_invent is not None:
        return bool(allow_invent)
    return get_taxonomy_invent_policy().can_auto_invent()


def can_provision_remotes(*, allow_provision: Optional[bool] = None) -> bool:
    """Return whether creating missing Magento/Shopify taxonomy remotes is allowed."""
    if allow_provision is not None:
        return bool(allow_provision)
    return get_taxonomy_invent_policy().can_provision_remotes()


@contextmanager
def taxonomy_auto_invent_enabled(enabled: bool) -> Iterator[None]:
    """Temporarily allow or deny auto-invent for the current request/task."""
    token = _auto_invent_override.set(bool(enabled))
    try:
        yield
    finally:
        _auto_invent_override.reset(token)


@contextmanager
def taxonomy_remote_provision_enabled(enabled: bool) -> Iterator[None]:
    """Temporarily allow or deny remote category/collection create."""
    token = _remote_provision_override.set(bool(enabled))
    try:
        yield
    finally:
        _remote_provision_override.reset(token)


def coerce_taxonomy_mode_when_frozen(taxonomy_mode: str) -> tuple[str, str, bool]:
    """Return (effective_mode, requested_mode, was_coerced).

    When invent is frozen, full → link_only so pipelines cannot rebuild hierarchy.
    """
    requested = str(taxonomy_mode or "link_only").strip().lower().replace("-", "_")
    if requested not in {"full", "link_only", "skip"}:
        requested = "link_only"
    if requested == "full" and not can_auto_invent():
        return "link_only", requested, True
    return requested, requested, False


def coerce_remote_provision_when_frozen(
    create_missing_remotes: bool,
    *,
    allow_provision: Optional[bool] = None,
) -> tuple[bool, bool]:
    """Return (effective_create_missing, was_coerced).

    When remote provision is frozen, force create_missing_remotes off.
    """
    if not create_missing_remotes:
        return False, False
    if can_provision_remotes(allow_provision=allow_provision):
        return True, False
    return False, True
