"""Checker for multiple classes in one file."""

import re
from pathlib import Path
from typing import List

from stablestack.checkers.base import BaseChecker
from stablestack.models import Finding, Severity, Tier


class MultipleClassesChecker(BaseChecker):
    """Detects multiple class definitions in a single file."""

    rule_id = "STRUCT001"
    rule_name = "multiple-classes-per-file"
    description = "Multiple classes defined in a single file."
    why = (
        "Having multiple classes in one file makes code harder to find and maintain. "
        "Each class should live in its own file, named after the class. This makes "
        "imports clearer and helps AI assistants understand your codebase structure."
    )
    severity = Severity.INFO
    tier = Tier.PREFERENCE  # Opinionated style choice, not everyone agrees
    file_extensions = {".py"}

    # Pattern to match class definitions
    _CLASS_PATTERN = re.compile(r"^class\s+(\w+)\s*[:\(]", re.MULTILINE)

    # Classes that are typically OK to have alongside main class
    _ALLOWED_COMPANION_PATTERNS = [
        r".*Exception$",  # Custom exceptions
        r".*Error$",  # Custom errors
        r".*Mixin$",  # Mixins
        r".*Base$",  # Base classes
        r".*Meta$",  # Metaclasses
        r".*Config$",  # Config classes (e.g., Pydantic)
        r".*Schema$",  # Schema classes
    ]

    def check_file(self, path: Path, content: str) -> List[Finding]:
        findings = []

        # Find all class definitions
        matches = list(self._CLASS_PATTERN.finditer(content))

        if len(matches) <= 1:
            return findings

        # Filter out allowed companion classes
        main_classes = []
        for match in matches:
            class_name = match.group(1)
            is_companion = any(
                re.match(pattern, class_name)
                for pattern in self._ALLOWED_COMPANION_PATTERNS
            )
            if not is_companion:
                main_classes.append((class_name, match))

        if len(main_classes) > 1:
            class_names = [name for name, _ in main_classes]
            line_no = content[: main_classes[1][1].start()].count("\n") + 1

            findings.append(
                Finding(
                    file=str(path),
                    line=line_no,
                    column=1,
                    code=f"class {main_classes[1][0]}",
                    rule_id=self.rule_id,
                    rule_name=self.rule_name,
                    message=f"Multiple classes in one file: {', '.join(class_names)}. Consider splitting into separate files.",
                    severity=self.severity,
                    fix_hint=f"Move each class to its own file (e.g., {class_names[0].lower()}.py, {class_names[1].lower()}.py)",
                    why=self.why,
                )
            )

        return findings
