"""Source for pyproject.toml files or more generally toml files."""
from __future__ import annotations

import sys
from pathlib import Path

from pytoolconfig.sources.source import Source
from pytoolconfig.types import Key
from pytoolconfig.universal_config import UniversalConfig
from pytoolconfig.utils import (
    _dict_to_dataclass,
    find_config_file,
    max_py_version,
    min_py_version,
    parse_dependencies,
)

if sys.version_info < (3, 11, 0):
    import tomli as tomllib
else:
    import tomllib


class PyProject(Source):

    """Source for pyproject.toml and pytool.toml files.

    Can be extended to other toml files.
    """

    tool: str
    toml_dict: dict | None = None
    name: str = "pyproject.toml"
    description: str = """
    PEP 518 defines pyproject.toml as a configuration file to store build system
    requirements for Python projects. With the help of tools like Poetry or Flit
    it can fully replace the need for setup.py and setup.cfg files.
    """  # taken from black.
    file: Path | None

    def __init__(
        self,
        working_directory: Path,
        tool: str,
        bases: list[str] | None = None,
        recursive: bool = True,
    ) -> None:
        """Initialize the TOML configuration.

        :param working_directory: Working Directory
        :param tool: name of your tool. Will read configuration from [tool.yourtool]
        :param bases: Base files/folders to look for (besides pyproject.toml)
        :param recursive: search recursively up the directory tree for the file.
        """
        if recursive:
            self.file = find_config_file(working_directory, "pyproject.toml", bases)
        else:
            self.file = working_directory / "pyproject.toml"
        self.tool = tool

    def _read(self) -> bool:
        if not self.file or not self.file.exists():
            return False
        self.toml_dict = tomllib.loads(self.file.read_text())
        if self.toml_dict is None:
            return False
        if "tool" not in self.toml_dict:
            return False
        return self.tool in self.toml_dict["tool"]

    def parse(self) -> dict[str, Key] | None:
        """Parse the TOML file."""
        if not self._read():
            return None
        assert self.toml_dict
        return self.toml_dict["tool"][self.tool]

    def universalconfig(self) -> UniversalConfig:
        """Parse the file for the universal config object's fields.

        Only implement the relevant fields such as minimum python version.

        Pre: file was read but tool isn't necessarily in file.
        """
        if not self.toml_dict:
            return UniversalConfig()
        config: UniversalConfig
        config = (
            _dict_to_dataclass(UniversalConfig, self.toml_dict["tool"]["pytoolconfig"])
            if "pytoolconfig" in self.toml_dict.get("tool", {})
            else UniversalConfig()
        )
        if "project" in self.toml_dict:
            project = self.toml_dict["project"]
            if "requires-python" in project:
                raw_python_ver = project["requires-python"]
                config.min_py_version = min_py_version(raw_python_ver)
                config.max_py_version = max_py_version(raw_python_ver)
            if "dependencies" in project:
                dependencies = parse_dependencies(project["dependencies"])
                config.dependencies = list(dependencies)
            if "optional-dependencies" in project:
                optional_deps = {}
                for group, deps in project["optional-dependencies"].items():
                    optional_deps[group] = list(parse_dependencies(deps))
                config.optional_dependencies = optional_deps
            if "version" in project:
                config.version = project["version"]
        return config
