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

from __future__ import annotations

import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Set

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

from stablestack.models import Tier


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

    # Which rules to enable/disable
    enable: Set[str] = field(default_factory=set)
    disable: Set[str] = field(default_factory=set)

    # Tier filters
    max_tier: Optional[Tier] = None  # Run checkers at or below this tier (--up-to)
    exact_tier: Optional[Tier] = None  # Run ONLY checkers at this tier (--tier)

    # Path patterns
    include: List[str] = field(default_factory=list)
    exclude: List[str] = field(default_factory=lambda: [
        "**/node_modules/**",
        "**/__pycache__/**",
        "**/.git/**",
        "**/venv/**",
        "**/.venv/**",
        "**/dist/**",
        "**/build/**",
        "**/.next/**",
        "**/.nuxt/**",
    ])

    # Per-rule configuration
    rules: Dict[str, Dict[str, Any]] = field(default_factory=dict)

    @classmethod
    def load(cls, config_path: Path | None = None, search_from: Path | None = None) -> "Config":
        """
        Load configuration from pyproject.toml.

        Args:
            config_path: Path to pyproject.toml, or None to search automatically
            search_from: Directory to start searching from (default: cwd).
                        When running against a target project, pass the target
                        path so the config is loaded from the right project.

        Returns:
            Config instance with loaded settings
        """
        if config_path is None:
            config_path = cls._find_config(search_from)

        if config_path is None or not config_path.exists():
            return cls()

        try:
            with open(config_path, "rb") as f:
                data = tomllib.load(f)
        except (OSError, tomllib.TOMLDecodeError):
            return cls()

        tool_config = data.get("tool", {}).get("stablestack", {})
        return cls._from_dict(tool_config)

    @classmethod
    def _find_config(cls, search_from: Path | None = None) -> Path | None:
        """Find pyproject.toml starting from search_from directory, or cwd if not given.

        When search_from is provided (i.e. the user passed an explicit target path),
        we ONLY search from that directory upward.  We do NOT fall back to cwd,
        because cwd may be a completely different project (e.g. the stablestack repo
        itself) whose config would incorrectly filter the target's files.
        """
        if search_from is not None:
            resolved = search_from.resolve()
            start = resolved if resolved.is_dir() else resolved.parent
        else:
            start = Path.cwd()

        for parent in [start] + list(start.parents):
            config_path = parent / "pyproject.toml"
            if config_path.exists():
                # Only use it if it has a [tool.stablestack] section
                try:
                    with open(config_path, "rb") as f:
                        data = tomllib.load(f)
                    tool = data.get("tool", {})
                    if "stablestack" in tool:
                        return config_path
                except (OSError, tomllib.TOMLDecodeError):
                    pass

        return None

    @classmethod
    def _from_dict(cls, data: Dict[str, Any]) -> "Config":
        """Create Config from dictionary."""
        config = cls()

        # Parse enable/disable
        if "enable" in data:
            config.enable = set(data["enable"])
        if "disable" in data:
            config.disable = set(data["disable"])

        # Parse path patterns
        if "include" in data:
            config.include = list(data["include"])
        if "exclude" in data:
            config.exclude = list(data["exclude"])

        # Parse per-rule config
        if "rules" in data:
            config.rules = dict(data["rules"])

        return config
