"""Parse pyproject.toml files in directory, supports multiple projects."""

from __future__ import annotations

import argparse
import json
import logging
import sys
import time
from pathlib import Path

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

__all__ = ["parse_project_data"]


logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
cwd = Path.cwd()


def parse_project_data(directory: Path, recursive: bool = False) -> dict:
    """Parse pyproject.toml file in directory and return project data.

    Returns:
        dict: Project data.
    """
    data = _parse_pyproject(directory, recursive=recursive)
    if not data:
        return {}
    return _extract_project_info(data)


def _parse_pyproject(directory: Path, recursive: bool = False) -> dict[str, dict]:
    """Parse pyproject.toml file in directory and return raw data."""
    data = {}
    if recursive:
        for pyproject_path in directory.rglob("pyproject.toml"):
            with pyproject_path.open("rb") as f:
                data[pyproject_path.parent.stem] = tomllib.load(f)
    else:
        pyproject_path = directory / "pyproject.toml"
        if not pyproject_path.is_file():
            logger.error(f"No pyproject.toml found in {directory}")
            return {}

        with pyproject_path.open("rb") as f:
            logger.debug(f"Parsing {pyproject_path}")
            data[pyproject_path.parent.stem] = tomllib.load(f)

    logger.debug(f"Parsed {len(data)} pyproject.toml files, data: {data}")
    return data


def _extract_project_info(data: dict) -> dict:
    """Extract commonly used project information from parsed data."""
    if not data:
        logger.error("No data to extract")
        return {}

    project_info = {}
    for key, value in data.items():
        if "project" in value:
            project = value.get("project", {})
            build_system = value.get("build-system", {})
            project_info.setdefault(
                key,
                {
                    "name": project.get("name"),
                    "version": project.get("version"),
                    "description": project.get("description"),
                    "readme": project.get("readme"),
                    "requires_python": project.get("requires-python"),
                    "dependencies": project.get("dependencies", []),
                    "optional_dependencies": project.get("optional-dependencies", {}),
                    "scripts": project.get("scripts", {}),
                    "entry_points": project.get("entry-points", {}),
                    "authors": project.get("authors", []),
                    "license": project.get("license"),
                    "keywords": project.get("keywords", []),
                    "classifiers": project.get("classifiers", []),
                    "urls": project.get("urls", {}),
                    "build_backend": build_system.get("build-backend"),
                    "requires": build_system.get("requires", []),
                },
            )
        else:
            logger.warning(f"No project information found in {key}")
            project_info.setdefault(key, {})

    logger.debug(f"Extracted {len(project_info)} projects, info: {project_info}")
    return project_info


def _check_directory(directory: str) -> bool:
    """Check if directory is valid."""
    if not directory:
        logger.error("Error: No directory specified")
        return False

    dir_path = Path(directory)
    if not dir_path.is_dir():
        logger.error(f"Error: {dir_path} is not a directory")
        return False

    return True


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--directory", "-D", type=str, default=str(cwd), help="Directory to parse")
    parser.add_argument("--debug", "-d", action="store_true", help="Debug mode")
    parser.add_argument("--recursive", "-r", action="store_true", help="Recursively parse subdirectories")
    parser.add_argument("--show", "-s", action="store_true", help="Show parsed data")
    parser.add_argument("--output", "-o", type=str, default="projects.json", help="Output file path")

    args = parser.parse_args()
    if args.debug:
        logger.setLevel(logging.DEBUG)

    if not _check_directory(args.directory):
        return

    output_path = (cwd / args.output).with_suffix(".json")
    if args.show:
        if output_path.is_file():
            logger.info(f"Loading output from `{output_path}`:")
            with output_path.open("r", encoding="utf-8") as f:
                output_data = json.load(f)
            logger.info(json.dumps(output_data, indent=2, ensure_ascii=False, sort_keys=True))
            return
        else:
            logger.debug(f"No json file found at {output_path}, continue parsing...")

    t0 = time.perf_counter()
    logger.info(f"Parsing pyproject.toml in {args.directory}")
    output_data = parse_project_data(Path(args.directory), recursive=args.recursive)
    if args.show:
        logger.info(json.dumps(output_data, indent=2, ensure_ascii=False, sort_keys=True))
        return

    try:
        with output_path.open("w", encoding="utf-8") as f:
            json.dump(output_data, f, indent=2, ensure_ascii=False)
    except Exception as e:
        logger.error(f"Error writing output to {output_path}: {e}")
        return
    else:
        logger.info(f"Output written to {output_path}, took {time.perf_counter() - t0:.4f}s")
