"""Configuration loading from pyproject.toml."""

from __future__ import annotations

import sys
from dataclasses import dataclass, field
from fnmatch import fnmatch
from pathlib import Path

if sys.version_info >= (3, 11):
    import tomllib
else:
    import tomli as tomllib


@dataclass
class PriorityRule:
    """A rule for assigning priority to modules based on path patterns."""

    pattern: str
    level: str = "medium"
    reviewers_required: int = 1

    def matches(self, path: str) -> bool:
        """Check if this rule matches the given path."""
        return fnmatch(path, self.pattern)


@dataclass
class Config:
    """Configuration for glreview."""

    # Source patterns to discover modules
    sources: list[str] = field(default_factory=lambda: ["src/**/*.py"])

    # Patterns to exclude
    exclude: list[str] = field(default_factory=lambda: ["**/_version.py"])

    # Path to the registry file
    registry: str = "review_registry.json"

    # GitLab issue labels
    issue_labels: list[str] = field(default_factory=lambda: ["review"])

    # Path to custom issue template (optional)
    issue_template: str | None = None

    # Priority rules (in order of precedence)
    priority_rules: list[PriorityRule] = field(default_factory=list)

    # Named reviewer teams
    teams: dict[str, list[str]] = field(default_factory=dict)

    # Project root directory
    root: Path = field(default_factory=Path.cwd)

    def get_priority(self, path: str) -> tuple[str, int]:
        """Get the priority level and required reviewers for a path.

        Returns:
            Tuple of (level, reviewers_required)
        """
        for rule in self.priority_rules:
            if rule.matches(path):
                return rule.level, rule.reviewers_required

        # Default
        return "medium", 1

    @property
    def registry_path(self) -> Path:
        """Get the absolute path to the registry file."""
        return self.root / self.registry


def load_config(project_root: Path | None = None) -> Config:
    """Load configuration from pyproject.toml.

    Args:
        project_root: Path to the project root. If None, searches upward
            from cwd for pyproject.toml.

    Returns:
        Config object with loaded settings.
    """
    if project_root is None:
        project_root = find_project_root()

    pyproject_path = project_root / "pyproject.toml"

    if not pyproject_path.exists():
        # Return defaults
        return Config(root=project_root)

    with open(pyproject_path, "rb") as f:
        data = tomllib.load(f)

    tool_config = data.get("tool", {}).get("glreview", {})

    # Parse priority rules
    priority_rules = []
    for rule_data in tool_config.get("priority", []):
        priority_rules.append(
            PriorityRule(
                pattern=rule_data.get("pattern", "**/*.py"),
                level=rule_data.get("level", "medium"),
                reviewers_required=rule_data.get("reviewers_required", 1),
            )
        )

    return Config(
        sources=tool_config.get("sources", ["src/**/*.py"]),
        exclude=tool_config.get("exclude", ["**/_version.py"]),
        registry=tool_config.get("registry", "review_registry.json"),
        issue_labels=tool_config.get("issue_labels", ["review"]),
        issue_template=tool_config.get("issue_template"),
        priority_rules=priority_rules,
        teams=tool_config.get("teams", {}),
        root=project_root,
    )


def find_project_root(start: Path | None = None) -> Path:
    """Find the project root by searching upward for pyproject.toml or .git.

    Args:
        start: Starting directory. Defaults to cwd.

    Returns:
        Path to the project root.

    Raises:
        FileNotFoundError: If no project root can be found.
    """
    if start is None:
        start = Path.cwd()

    current = start.resolve()

    while current != current.parent:
        if (current / "pyproject.toml").exists():
            return current
        if (current / ".git").exists():
            return current
        current = current.parent

    # Fall back to start directory
    return start.resolve()
