#!/usr/bin/env python3
"""
Filter noisy [DEBUG ATC] / [DEBUG LEDGER] / [DEBUG REWINGER] console lines.

Precedence:
  1. Environment variable ABFLUG_TERMINAL_DEBUG
  2. CLI (--terminal-debug / --quiet-terminal on abflug_client_web via apply_cli_terminal_debug)
  3. butter.json or abflug.json key \"terminal_debug\"
  4. Default: all channels on (backward compatible)

terminal_debug examples (JSON or ABFLUG_TERMINAL_DEBUG):
  \"ledger\"                 — only Aviator Ledger CSV messages
  \"atc\", \"ledger\"        — comma-separated list
  \"none\"                   — mute routine/debug tags (critical errors still use always=True)
  \"client\"                 — alias for \"atc\"
"""

from __future__ import annotations

import os
from typing import Any, FrozenSet, Optional, Set

CHANNEL_ATC = "atc"
CHANNEL_LEDGER = "ledger"
CHANNEL_REWINGER = "rewinger"
_VALID: FrozenSet[str] = frozenset({CHANNEL_ATC, CHANNEL_LEDGER, CHANNEL_REWINGER})

_UNREAD = object()
_CLI_SPEC: Optional[str] = None
_CONFIG_TERMINAL_SPEC: Any = _UNREAD

_PARSE_RAW: Optional[str] = None
_PARSE_ALLOWED: Optional[Optional[FrozenSet[str]]] = None


def _scan_config_terminal_debug_spec() -> Optional[str]:
    from config_utils import _load_config_file

    for fn in ("butter.json", "abflug.json"):
        cfg = _load_config_file(fn)
        if not cfg:
            continue
        v = cfg.get("terminal_debug")
        if v is None:
            continue
        if isinstance(v, list):
            s = ",".join(str(x).strip() for x in v if str(x).strip())
        else:
            s = str(v).strip()
        return s or None
    return None


def _get_config_terminal_debug_spec() -> Optional[str]:
    global _CONFIG_TERMINAL_SPEC
    if _CONFIG_TERMINAL_SPEC is _UNREAD:
        _CONFIG_TERMINAL_SPEC = _scan_config_terminal_debug_spec()
    return _CONFIG_TERMINAL_SPEC


def load_config_terminal_debug_spec() -> None:
    """Eager-load terminal_debug from JSON (GUI / unified app startup)."""
    global _CONFIG_TERMINAL_SPEC
    _CONFIG_TERMINAL_SPEC = _scan_config_terminal_debug_spec()
    invalidate_terminal_debug_cache()


def apply_cli_terminal_debug(
    *,
    terminal_debug: Optional[str] = None,
    quiet_terminal: bool = False,
) -> None:
    """Apply argparse results for standalone abflug_client_web."""
    global _CLI_SPEC
    if quiet_terminal:
        _CLI_SPEC = "none"
    elif terminal_debug is not None and str(terminal_debug).strip():
        _CLI_SPEC = str(terminal_debug).strip()
    else:
        _CLI_SPEC = None
    invalidate_terminal_debug_cache()


def invalidate_terminal_debug_cache() -> None:
    global _PARSE_RAW, _PARSE_ALLOWED
    _PARSE_RAW = None
    _PARSE_ALLOWED = None


def _raw_spec() -> str:
    # CLI (abflug_client_web) wins over env so one-off runs are predictable
    if _CLI_SPEC is not None:
        return _CLI_SPEC.strip() or "all"
    env = os.environ.get("ABFLUG_TERMINAL_DEBUG", "").strip()
    if env:
        return env
    cfg_spec = _get_config_terminal_debug_spec()
    return cfg_spec.strip() if cfg_spec else "all"


def _parse_spec(spec: str) -> Optional[FrozenSet[str]]:
    """
    None  = all channels visible
    set() = none (unless always=True on a print)
    else  = only listed channels
    """
    s = spec.strip().lower()
    if not s or s in ("all", "*", "full", "true", "1", "yes", "on"):
        return None
    if s in ("none", "off", "false", "0", "no", "silent"):
        return frozenset()
    parts: Set[str] = set()
    for chunk in s.replace(";", ",").split(","):
        p = chunk.strip()
        if p == "client":
            p = CHANNEL_ATC
        if p in _VALID:
            parts.add(p)
    if not parts:
        return None
    return frozenset(parts)


def _allowed_channels() -> Optional[FrozenSet[str]]:
    global _PARSE_RAW, _PARSE_ALLOWED
    raw = _raw_spec()
    if _PARSE_RAW == raw and _PARSE_ALLOWED is not None:
        return _PARSE_ALLOWED
    allowed = _parse_spec(raw)
    _PARSE_RAW = raw
    _PARSE_ALLOWED = allowed
    return allowed


def terminal_debug_print(channel: str, msg: str, *, always: bool = False) -> None:
    """Print tagged debug lines for channel if enabled. always=True bypasses filters (critical errors)."""
    ch = channel.strip().lower()
    if not always:
        allowed = _allowed_channels()
        if allowed is not None and ch not in allowed:
            return
    print(msg, flush=True)
