"""Module for generating Odoo `__manifest__.py` files."""

import pprint
from contextlib import suppress
from copy import deepcopy
from pathlib import Path
from typing import Any

from packaging.requirements import Requirement

from .primitives import (
    build_full_version,
    deep_merge,
    matches_release_specifier,
    parse_release,
)

ADDON_PREFIX = "odoo-addon-"


def addon_name(pkg_name: str) -> str:
    """Get addon name from package name.

    Args:
        pkg_name: Package name (e.g., "odoo-addon-example-module")

    Returns:
        Addon name (e.g., "example_module")

    Raises:
        ValueError: If package name doesn't start with ADDON_PREFIX
    """
    if not pkg_name.startswith(ADDON_PREFIX):
        raise ValueError(
            f"Invalid package name: '{pkg_name}'. "
            "Package name must start with 'odoo-addon-' "
            "(e.g., 'odoo-addon-example-module')."
        )
    return pkg_name.removeprefix(ADDON_PREFIX).replace("-", "_")


def parse_project_dependencies(
    pyproject_data: dict[str, Any],
) -> tuple[list[str], list[str]]:
    """Parse project.dependencies to extract depends and external_dependencies.python.

    - odoo-addon-* packages become depends (with prefix stripped)
    - odoo>=X.Y becomes depends as just 'odoo'
    - Other packages become external_dependencies.python

    Args:
        pyproject_data: Parsed pyproject.toml data

    Returns:
        Tuple of (depends list, external_dependencies.python list)
    """
    project = pyproject_data.get("project", {})
    dependencies: list[str] = project.get("dependencies", [])

    depends: list[str] = []
    external_deps: list[str] = []

    for dep in dependencies:
        req = Requirement(dep)
        name = req.name

        if name.startswith(ADDON_PREFIX):
            depends.append(addon_name(name))
        elif name == "odoo":
            continue
        else:
            external_deps.append(dep)

    return depends, external_deps


def get_author_value(pyproject_data: dict[str, Any]) -> str | None:
    """Extract author value from project authors list.

    Args:
        pyproject_data: Parsed `pyproject.toml` data

    Returns:
        Comma-separated author names, or `None` if not found
    """
    try:
        authors = pyproject_data["project"]["authors"]
    except KeyError:
        return None
    if not authors:
        return None
    return ", ".join(author["name"] for author in authors if "name" in author)


def _standard_manifest(
    pyproject_data: dict[str, Any], odoo_release: str
) -> dict[str, Any]:
    """Convert standard `pyproject.toml` values to `__manifest__.py`.

    Args:
        pyproject_data: Parsed `pyproject.toml` data
        odoo_release: Odoo release (e.g., "19.0")

    Returns:
        Dictionary representing standard `__manifest__.py` content
    """
    name = addon_name(pyproject_data["project"]["name"])
    human_name = name.replace("_", " ").title()

    project = pyproject_data["project"]
    module_version = project["version"]
    full_version = build_full_version(module_version, odoo_release)

    manifest: dict[str, Any] = {
        "name": human_name,
        "version": full_version,
    }
    authors = get_author_value(pyproject_data)
    if authors:
        manifest["author"] = authors

    with suppress(KeyError):
        manifest["website"] = pyproject_data["project"]["urls"]["Homepage"]
    with suppress(KeyError):
        manifest["license"] = pyproject_data["project"]["license"]
    with suppress(KeyError):
        manifest["summary"] = pyproject_data["project"]["description"]

    addon_deps, py_deps = parse_project_dependencies(pyproject_data)
    if addon_deps:
        manifest["depends"] = addon_deps
    if py_deps:
        manifest["external_dependencies"] = {"python": py_deps}

    return manifest


def _manual_manifest(
    pyproject_data: dict[str, Any], odoo_release: str
) -> dict[str, Any]:
    """Get version-specific overrides from `pyproject.toml`, if any.

    Args:
        pyproject_data: Parsed `pyproject.toml` data
        odoo_release: Odoo release (e.g., "19.0") to match against

    Returns:
        Merged overrides dictionary for matching releases
    """
    manifest_config = (
        pyproject_data.get("tool", {}).get("polytropos", {}).get("manifest", [])
    )
    if not isinstance(manifest_config, list):
        raise ValueError(
            "[tool.polytropos.manifest] must be a list, if defined. "
            "Use [[tool.polytropos.manifest]] (double brackets) in pyproject.toml."
        )
    manifest_config = deepcopy(manifest_config)
    parsed_release = parse_release(odoo_release)

    result: dict[str, Any] = {}
    for item in manifest_config:
        releases_spec = item.pop("releases", None)
        if releases_spec and not matches_release_specifier(
            parsed_release, releases_spec
        ):
            continue
        result = deep_merge(result, item)

    return result


def generate_manifest(
    pyproject_data: dict[str, Any], odoo_release: str
) -> dict[str, Any]:
    """Convert standard `pyproject.toml` values to `__manifest__.py`.

    Args:
        pyproject_data: Parsed `pyproject.toml` data
        odoo_release: Odoo release (e.g., "19.0")

    Returns:
        Dictionary representing `__manifest__.py` content
    """
    standard = _standard_manifest(pyproject_data, odoo_release)
    overrides = _manual_manifest(pyproject_data, odoo_release)

    manifest = deep_merge(standard, overrides)
    return manifest


def write_manifest(manifest: dict[str, Any], output_path: Path) -> None:
    """Write manifest to __manifest__.py file.

    Args:
        manifest: Manifest dictionary
        output_path: Path where to write __manifest__.py
    """
    content = generate_manifest_content(manifest)
    output_path.write_text(content)


def generate_manifest_content(manifest: dict[str, Any]) -> str:
    """Generate __manifest__.py file content.

    Args:
        manifest: Manifest dictionary

    Returns:
        String content for __manifest__.py
    """
    return pprint.pformat(manifest)
