#!/bin/env python3
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
# ruff: noqa: T201

from __future__ import annotations

import sys
import textwrap
from pathlib import Path
from typing import TYPE_CHECKING, cast

import yaml
from ansible_collections.snailed.ez_docker.plugins.module_utils import (
    label,
    service,
)

if TYPE_CHECKING:
    from types import ModuleType

GALAXY_METADATA_PATH = Path("../galaxy.yml")


def get_modules(parent_module: ModuleType) -> list[tuple[str, ModuleType]]:
    return [
        (name, getattr(parent_module, name)) for name in parent_module.__all__ if name != "common"
    ]


def settings_format(text: str) -> str:
    return "\n".join(
        line for line in text.splitlines() if not line.strip().startswith(("default:", "required:"))
    )


def get_module_docs(
    module: ModuleType,
) -> str:
    ret: str = ""
    if docs := getattr(module, "DOCUMENTATION", None):
        ret = docs

    if ret:
        return ret

    msg = f"Module {module.__name__} has no documentation"
    raise ValueError(msg)


def get_metadata() -> str:
    with GALAXY_METADATA_PATH.open("r") as f:
        metadata = yaml.safe_load(f)

    docs = """
module: compose
version_added: 1.0.0
short_description: Simplify docker-compose deployments.
description: Easily create docker-compose files using a single module
seealso:
    - name: Compose file reference
      description: Complete reference of the compose file spec.
      link: https://docs.docker.com/reference/compose-file/
attributes:
    check_mode:
        support: full
    diff_mode:
        support: full
"""

    if author := metadata.get("author"):
        if isinstance(author, str):
            docs += f"author: {author}\n"

        elif isinstance(author, list):
            docs += "author:\n"
            for a in cast(list[str], author):
                docs += f"    - {a}\n"

    return docs


def get_settings_docs() -> str:
    service_default_definitions = """
service_default_definitions:
    description:
        - Default definitions for each service
    type: dict
    suboptions:
"""

    service_default_args = """
service_default_args:
    description:
        - Default arguments for each service helper.
    type: dict
    suboptions:
"""

    label_default_args = """
label_default_args:
    description:
        - Default arguments for each label helper.
    type: dict
    suboptions:
"""

    for name, module in get_modules(service):
        if module.__name__.endswith("common"):
            continue

        service_default_definitions += f"""
        {name}:
            description:
                - Default definitions for {name} services.
            type: dict
"""

        service_default_args += textwrap.indent(settings_format(get_module_docs(module)), " " * 8)

        if module.__name__.endswith("custom"):
            for _, label_module in get_modules(label):
                if label_module.__name__.endswith("common"):
                    continue

                service_default_args += textwrap.indent(
                    settings_format(get_module_docs(label_module)), " " * 24
                )
        else:
            service_default_args += textwrap.indent(
                settings_format(service.common.BASE_DOCUMENTATION), " " * 16
            )

    for _, module in get_modules(label):
        if module.__name__.endswith("common"):
            continue

        label_default_args += textwrap.indent(settings_format(get_module_docs(module)), " " * 8)

    return f"""
settings:
    description:
        - Settings/Defaults for the module.
    type: dict
    suboptions:
        external_networks:
            description:
                - Networks to mark as external.
            type: list
            elements: str
        external_volumes:
            description:
                - Volumes to mark as external.
            type: list
            elements: str
        default_definition:
            description:
                - Default definition for all containers.
                - Overwritten by per-service defaults.
            type: dict
{textwrap.indent(service_default_definitions, " " * 8)}
{textwrap.indent(service_default_args, " " * 8)}
{textwrap.indent(label_default_args, " " * 8)}
"""


def get_service_docs() -> str:
    service_docs = """
services:
    description:
        - Services to create in the project.
    type: dict
    suboptions:
"""
    for _, module in get_modules(service):
        service_docs += f"""
{textwrap.indent(get_module_docs(module), " " * 8)}
"""

        if module.__name__.endswith("custom"):
            for _, label_module in get_modules(label):
                if label_module.__name__ == "common":
                    continue

                service_docs += textwrap.indent(
                    settings_format(get_module_docs(label_module)), " " * 24
                )
        else:
            service_docs += textwrap.indent(service.common.BASE_DOCUMENTATION, " " * 16)

    return service_docs


ret = f"""
---
{get_metadata()}
options:
    name:
        description:
            - Name of the compose project to create or modify.
        aliases: [project]
        type: str
    project_dir:
        description:
            - Path to store project directory under.
        type: path
{textwrap.indent(get_settings_docs(), " " * 4)}
{textwrap.indent(get_service_docs(), " " * 4)}
"""

ret = "\n".join(line for line in ret.splitlines() if line.strip())

print(ret)
try:
    yaml.safe_load(ret)
except yaml.YAMLError as e:
    print("YAML is invalid!\n", file=sys.stderr)
    print(e, file=sys.stderr)