"""
Read pytest configuration (pythonpath, testpaths) from project config files.

Checks pytest.ini, pyproject.toml, and setup.cfg in that order.
"""

from __future__ import annotations

import configparser
import logging
import os
from dataclasses import dataclass, field

logger = logging.getLogger(__name__)


@dataclass
class PytestConfig:
    pythonpath: list[str] = field(default_factory=list)
    testpaths: list[str] = field(default_factory=list)


def _split_ini_list(value: str) -> list[str]:
    """Split a pytest ini-style list value (newline or space separated)."""
    items: list[str] = []
    for line in value.splitlines():
        line = line.strip()
        if line:
            items.extend(line.split())
    return items


def _read_pytest_ini(project_root: str) -> PytestConfig | None:
    path = os.path.join(project_root, "pytest.ini")
    if not os.path.isfile(path):
        return None
    try:
        parser = configparser.ConfigParser()
        parser.read(path, encoding="utf-8")
        if not parser.has_section("pytest"):
            return None
        config = PytestConfig()
        if parser.has_option("pytest", "pythonpath"):
            config.pythonpath = _split_ini_list(parser.get("pytest", "pythonpath"))
        if parser.has_option("pytest", "testpaths"):
            config.testpaths = _split_ini_list(parser.get("pytest", "testpaths"))
        return config
    except Exception:
        logger.debug("Failed to parse pytest.ini", exc_info=True)
        return None


def _read_pyproject_toml(project_root: str) -> PytestConfig | None:
    path = os.path.join(project_root, "pyproject.toml")
    if not os.path.isfile(path):
        return None
    try:
        # tomllib is available in Python 3.11+; tomli is the backport
        try:
            import tomllib
        except ModuleNotFoundError:
            import tomli as tomllib  # type: ignore[import-not-found,no-redef]

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

        ini_options = data.get("tool", {}).get("pytest", {}).get("ini_options", {})
        if not ini_options:
            return None
        config = PytestConfig()
        pp = ini_options.get("pythonpath")
        if isinstance(pp, list):
            config.pythonpath = [str(p) for p in pp]
        elif isinstance(pp, str):
            config.pythonpath = _split_ini_list(pp)
        tp = ini_options.get("testpaths")
        if isinstance(tp, list):
            config.testpaths = [str(p) for p in tp]
        elif isinstance(tp, str):
            config.testpaths = _split_ini_list(tp)
        return config
    except Exception:
        logger.debug("Failed to parse pyproject.toml", exc_info=True)
        return None


def _read_setup_cfg(project_root: str) -> PytestConfig | None:
    path = os.path.join(project_root, "setup.cfg")
    if not os.path.isfile(path):
        return None
    try:
        parser = configparser.ConfigParser()
        parser.read(path, encoding="utf-8")
        if not parser.has_section("tool:pytest"):
            return None
        config = PytestConfig()
        if parser.has_option("tool:pytest", "pythonpath"):
            config.pythonpath = _split_ini_list(parser.get("tool:pytest", "pythonpath"))
        if parser.has_option("tool:pytest", "testpaths"):
            config.testpaths = _split_ini_list(parser.get("tool:pytest", "testpaths"))
        return config
    except Exception:
        logger.debug("Failed to parse setup.cfg", exc_info=True)
        return None


def read_pytest_config(project_root: str) -> PytestConfig:
    """
    Read pytest pythonpath/testpaths config from the project.

    Checks (in order): pytest.ini → pyproject.toml → setup.cfg.
    Returns the first one found, or an empty PytestConfig.
    """
    for reader in (_read_pytest_ini, _read_pyproject_toml, _read_setup_cfg):
        result = reader(project_root)
        if result is not None:
            return result
    return PytestConfig()
