"""Rule registry for valn8n validation rules."""

from collections.abc import Callable
from pathlib import Path
from typing import Any

from valn8n_lib.types import Deprecation, Diagnostic, RuleCode, Severity, SuggestionLevel

type SourceMap = dict[str, Any] | None

type RuleFunc = Callable[[dict[str, Any], Path, SourceMap, SuggestionLevel], list[Diagnostic]]

RULE_REGISTRY: dict[str, tuple[RuleCode, RuleFunc]] = {}

RULE_GROUPS: dict[str, str] = {
    "ND01": "Filter operations",
    "ND02": "Node structure/keys",
    "ND03": "Parameter validation",
    "ND04": "Expression syntax",
    "ND05": "Credential validation",
    "ND06": "Connection validation",
    "ND07": "Error handling",
    "ND08": "Naming & documentation",
}

IO_RULE_REGISTRY: dict[str, RuleCode] = {}

IO_RULE_GROUPS: dict[str, str] = {"IO00": "File I/O validation"}


def register_io(code: str, severity: Severity, description: str) -> RuleCode:
    """Register an IO rule (metadata only, no check function)."""
    rule = RuleCode(code, severity, description=description)
    IO_RULE_REGISTRY[code] = rule
    return rule


def register(
    code: str,
    severity: Severity,
    deprecation: Deprecation = Deprecation.NO,
    description: str = "",
    strict: bool = True,
) -> Callable[[RuleFunc], RuleFunc]:
    """Register a rule function by its code."""

    def decorator(func: RuleFunc) -> RuleFunc:
        RULE_REGISTRY[code] = (
            RuleCode(code, severity, deprecation, description, strict=strict),
            func,
        )
        return func

    return decorator


def _matches(code: str, patterns: set[str]) -> bool:
    """Check if code matches any pattern (exact or prefix)."""
    return any(code == p or code.startswith(p) for p in patterns)


def get_rules(
    select: set[str] | None = None,
    ignore: set[str] | None = None,
    extend_select: set[str] | None = None,
    strict_only: bool = False,
) -> dict[str, tuple[RuleCode, RuleFunc]]:
    """Filter the rule registry by select/ignore/extend-select sets.

    Processing order: strict_only pre-filter, select, ignore, extend-select (re-include wins last).
    """
    rules = dict(RULE_REGISTRY)
    if strict_only:
        rules = {k: v for k, v in rules.items() if v[0].strict is True}
    if select:
        rules = {k: v for k, v in rules.items() if _matches(k, select)}
    if ignore:
        rules = {k: v for k, v in rules.items() if not _matches(k, ignore)}
    if extend_select:
        source = dict(RULE_REGISTRY)
        if strict_only:
            source = {k: v for k, v in source.items() if v[0].strict is True}
        if select:
            source = {k: v for k, v in source.items() if _matches(k, select)}
        for k, v in source.items():
            if _matches(k, extend_select):
                rules[k] = v
    return rules
