"""
Redaction rules loader.

Mirrors the Preprocessor behavior:
- Fetch classifiers from the redaction API (POST empty body)
- Compile into `RedactionRule`s
- Cache in-memory with TTL
"""

from __future__ import annotations

import json
import re
import time
from typing import List, Optional

import requests

from chatsee.redaction.models import RedactionRule
from chatsee.redaction.validators import VALIDATORS


_CACHED_RULES: Optional[List[RedactionRule]] = None
_CACHED_AT_EPOCH_S: Optional[float] = None


def _compile_rules(docs: list) -> List[RedactionRule]:
    rules: List[RedactionRule] = []

    for doc in docs:
        if not isinstance(doc, dict):
            continue

        if doc.get("enabled") is False:
            continue

        validator_name = doc.get("validator")  # None or string
        validator = VALIDATORS.get(validator_name) if validator_name else None

        try:
            rules.append(
                RedactionRule(
                    type=doc["type"],
                    regex=re.compile(doc["regex"], re.IGNORECASE | re.MULTILINE),
                    priority=int(doc["priority"]),
                    mask_char=doc.get("mask_char", "X"),
                    mask_strategy=doc.get("mask_strategy", "FULL"),
                    validator=validator,
                )
            )
        except Exception:
            # Fail open: skip invalid classifier doc.
            continue

    # lower priority number = higher precedence
    rules.sort(key=lambda r: r.priority)
    return rules


def _fetch_classifiers_from_api(
    classifiers_url: str, timeout_seconds: float = 5.0, verify_ssl: bool = True
) -> list:
    resp = requests.post(
        classifiers_url,
        json={},
        timeout=timeout_seconds,
        verify=verify_ssl,
        headers={
            "accept": "application/json",
            "content-type": "application/json",
        },
    )
    resp.raise_for_status()

    payload = resp.json()
    api_status = payload.get("status")
    if api_status is not None and int(api_status) != 200:
        raise ValueError(
            f"Classifier API returned status={api_status}: {payload.get('message')}"
        )

    data = payload.get("data", [])
    if not isinstance(data, list):
        raise ValueError(f"Unexpected classifiers payload shape: data={type(data)}")

    return data


def load_redaction_rules_sync(
    *,
    classifiers_url: str,
    force_refresh: bool = False,
    cache_ttl_seconds: int = 300,
    timeout_seconds: float = 5.0,
    verify_ssl: bool = True,
) -> List[RedactionRule]:
    global _CACHED_RULES, _CACHED_AT_EPOCH_S

    now = time.time()
    if (
        not force_refresh
        and _CACHED_RULES is not None
        and _CACHED_AT_EPOCH_S is not None
        and cache_ttl_seconds > 0
        and (now - _CACHED_AT_EPOCH_S) < cache_ttl_seconds
    ):
        return _CACHED_RULES

    try:
        docs = _fetch_classifiers_from_api(
            classifiers_url, timeout_seconds=timeout_seconds, verify_ssl=verify_ssl
        )
        rules = _compile_rules(docs)
        _CACHED_RULES = rules
        _CACHED_AT_EPOCH_S = now
        return rules
    except (requests.RequestException, json.JSONDecodeError, ValueError):
        # Fail open: proceed without redaction rules
        return []

