"""Shared pattern detection utilities."""

from __future__ import annotations

import re

from collections import defaultdict
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from tree_sitter import Node

from vibelexity.metrics.indirection.python import (
    check_factory_functions,
    check_high_indirection_depth,
)
from vibelexity.patterns.loops import check_repeated_calls
from vibelexity.patterns.python import (
    check_module_level_execution,
    check_nested_imports,
    check_wildcard_imports,
)


_IGNORE_COMMENT_RE = re.compile(r"#\s*vibelexity-ignore\s*:(.+)")

_IGNORE_PATTERN_MAP = {
    "nested-import": "nested_import",
    "nested_import": "nested_import",
    "wildcard-import": "wildcard_import",
    "wildcard_import": "wildcard_import",
    "module-level-call": "module_level_call",
    "module_level_call": "module_level_call",
    "factory": "factory_function",
    "factory_function": "factory_function",
    "indirection": "high_indirection_depth",
    "high-indirection": "high_indirection_depth",
    "high_indirection_depth": "high_indirection_depth",
    "repeated-calls": "repeated_calls",
    "repeated_calls": "repeated_calls",
}


def _parse_ignore_comments(source: str) -> set[str]:
    """Parse vibelexity-ignore comments from source code.

    Args:
        source: Source code string

    Returns:
        Set of pattern types to ignore (normalized to violation types)
    """
    ignored = set()
    for line in source.splitlines():
        line = line.strip()
        match = _IGNORE_COMMENT_RE.match(line)
        if match:
            patterns_str = match.group(1).strip()
            patterns = [p.strip() for p in patterns_str.split(",")]
            for pattern in patterns:
                if pattern in _IGNORE_PATTERN_MAP:
                    ignored.add(_IGNORE_PATTERN_MAP[pattern])
    return ignored


def _filter_violations(violations: list, ignored_patterns: set[str]) -> list:
    """Filter violations based on ignored pattern types.

    Args:
        violations: List of PatternViolation objects
        ignored_patterns: Set of pattern types to ignore

    Returns:
        Filtered list of PatternViolation objects
    """
    if not ignored_patterns:
        return violations
    return [v for v in violations if v.type not in ignored_patterns]


def check_patterns(
    language: str,
    root: Node,
    patterns: list[str] | None = None,
    source: str | None = None,
) -> list:
    """Run pattern checks for a language.

    Args:
        language: Language name ("python", "typescript", "go")
        root: AST root node
        patterns: List of specific pattern functions to run, or None for all
        source: Optional source code string for parsing ignore comments

    Returns:
        List of PatternViolation objects
    """
    if language == "python":
        pattern_fns = {
            "check_module_level_execution": check_module_level_execution,
            "check_nested_imports": check_nested_imports,
            "check_wildcard_imports": check_wildcard_imports,
            "check_repeated_calls": check_repeated_calls,
            "check_factory_functions": check_factory_functions,
            "check_high_indirection_depth": check_high_indirection_depth,
        }
    elif language == "typescript":
        pattern_fns = {}
    elif language == "go":
        pattern_fns = {}
    else:
        return []

    if patterns:
        pattern_fns = {k: v for k, v in pattern_fns.items() if k in patterns}

    violations = []
    for name, fn in pattern_fns.items():
        violations.extend(fn(root))

    if source:
        ignored_patterns = _parse_ignore_comments(source)
        violations = _filter_violations(violations, ignored_patterns)

    return violations
