#!/usr/bin/env python

# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
# ruff: noqa: E402

from __future__ import annotations

from copy import deepcopy

DOCUMENTATION = """
---
module: compose
version_added: 1.0.0
short_description: Simplify docker-compose deployments
seealso:
    - name: Compose file reference
      description: Complete reference of the compose file spec.
      link: https://docs.docker.com/reference/compose-file/
description:
    - Easily create docker-compose files using a single module
author:
    - "Luca Bilke (@ssnailed)"
attributes:
    check_mode:
        support: full
    diff_mode:
        support: full
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
    settings:
        description:
            - Settings/Defaults for the module.
        type: dict
        suboptions:
            default_definition:
                description:
                    - Default definition for all containers.
                    - Overwritten by per-service defaults.
                type: dict
            service_default_definitions:
                description:
                    - Default definitions for each service
                type: dict
                suboptions:
                    custom:
                        description:
                            - Default definitions for custom services.
                        type: dict
                    docker_in_docker:
                        description:
                            - Default definitions for docker in docker services.
                        type: dict
                    docker_socket_proxy:
                        description:
                            - Default definitions for docker socket proxy services.
                        type: dict
                    docker_volume_backupper:
                        description:
                            - Default definitions for docker volume backupper services.
                        type: dict
                    mariadb:
                        description:
                            - Default definitions for MariaDB services.
                        type: dict
                    postgres:
                        description:
                            - Default definitions for PostgreSQL services.
                        type: dict
                    redis:
                        description:
                            - Default definitions for Redis services.
                        type: dict
            service_default_args:
                description:
                    - Default arguments for each service helper
                type: dict
                suboptions:
                    custom:
                        description:
                            - Default args for custom services.
                        type: dict
                    docker_in_docker:
                        description:
                            - Default args for docker in docker services.
                        type: dict
                    docker_socket_proxy:
                        description:
                            - Default args for docker socket proxy services.
                        type: dict
                    docker_volume_backupper:
                        description:
                            - Default args for docker volume backupper services.
                        type: dict
                    mariadb:
                        description:
                            - Default args for MariaDB services.
                        type: dict
                    postgres:
                        description:
                            - Default args for PostgreSQL services.
                        type: dict
                    redis:
                        description:
                            - Default args for Redis services.
                        type: dict
    services:
        description:
            - Services to create in the project.
        type: dict
        suboptions:
            custom:
                description:
                    - Custom service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
                    internal_network:
                        description:
                            - If true, add internal network to service.
                        type: bool
                        default: false
                    label_helpers:
                        description:
                            - Label helper configurations.
                        type: dict
                        suboptions:
                            docker_volume_backupper:
                                description:
                                    - Docker Volume Backupper label helper configuration.
                                type: dict
                                suboptions:
                                    stop:
                                        description:
                                            - If true, stop the container when backing up.
                                        type: bool
                                        default: true
                            traefik_middleware:
                                description:
                                    - Traefik Middleware label helper configuration.
                                type: dict
                                suboptions:
                                    proxy_type:
                                        description:
                                            - Traefik proxy type.
                                        type: str
                                        choices: [http, tcp, udp]
                                        default: http
                                    name:
                                        description:
                                            - Name of the middleware.
                                        type: string
                                    middleware:
                                        description:
                                            - The traefik middleware to use.
                                        type: str
                                        required: true
                                    settings:
                                        description:
                                            - Middleware options.
                                        type: dict
                                        required: true
                            traefik_router:
                                description:
                                    - Traefik Router label helper configuration.
                                type: dict
                                suboptions:
                                    proxy_type:
                                        description:
                                            - Traefik proxy type.
                                        type: str
                                        choices: [http, tcp, udp]
                                        default: http
                                    name:
                                        description:
                                            - Name of the middleware.
                                        type: string
                                    rule:
                                        description:
                                            - Routing rule to match.
                                        type: str
                                        required: true
                                    service:
                                        description:
                                            - Traefik service to point at.
                                        type: str
                                    certresolver:
                                        description:
                                            - Certresolver to use.
                                        type: str
                                    entrypoints:
                                        description:
                                            - Entrypoints to listen on.
                                        type: list
                                        elements: str
                                    middlewares:
                                        description:
                                            - Middlewares to use.
                                        type: list
                                        elements: str
                            traefik_service:
                                description:
                                    - Traefik Service label helper configuration.
                                type: dict
                                suboptions:
                                    proxy_type:
                                        description:
                                            - Traefik proxy type.
                                        type: str
                                        choices: [http, tcp, udp]
                                        default: http
                                    name:
                                        description:
                                            - Name of the middleware.
                                        type: string
                                    port:
                                        description:
                                            - Port to forward to.
                                        type: int
            docker_in_docker:
                description:
                    - Docker-in-Docker service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
            docker_socket_proxy:
                description:
                    - Docker Socket Proxy service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
                    read_only:
                        description:
                            - If true, only allow read access to the docker socket.
                        type: bool
                        default: true
            docker_volume_backupper:
                description:
                    - Docker Socket Proxy service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
                    archive:
                        description:
                            - Directory to store backups in.
                        type: path
                    backup_volumes:
                        description:
                            - List of volume names of volumes to backup.
                        type: list
                        elements: str
            mariadb:
                description:
                    - MariaDB service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
                    backup:
                        description:
                            - If true, add labels for the docker volume backupper.
                        type: bool
                    database:
                        description:
                            - Name of database.
                        type: str
                        required: true
                    username:
                        description:
                            - Username for database.
                        type: str
                        required: true
                    password:
                        description:
                            - Password for database.
                        type: str
                        required: true
                    root_password:
                        description:
                            - Root password for database.
                        type: str
            postgres:
                description:
                    - PostgreSQL service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
                    backup:
                        description:
                            - If true, add labels for the docker volume backupper.
                        type: bool
                    database:
                        description:
                            - Name of database.
                        type: str
                        required: true
                    username:
                        description:
                            - Username for database.
                        type: str
                        required: true
                    password:
                        description:
                            - Password for database.
                        type: str
                        required: true
            redis:
                description:
                    - Redis service definition.
                type: dict
                suboptions:
                    name:
                        description:
                            - Name of the service.
                        type: str
                    image:
                        description:
                            - Image to use for service.
                        type: str
                    defaults:
                        description:
                            - Service definition to be overwritten.
                        type: dict
                    overwrite:
                        description:
                            - Service definition to overwrite with.
                        type: dict
"""

from typing import Any

from ansible.module_utils.basic import AnsibleModule  # type: ignore[reportMissingStubFile]
from ansible_collections.snailed.ez_compose.plugins.module_utils import (
    common,
    label,
    service,
)


def label_argument_spec() -> dict[str, Any]:
    label_args: dict[str, Any] = {
        "type": "dict",
        "options": {},
    }

    for module in label.__all__:
        if module == "common":
            continue

        options = {
            **label.common.BASE_ARGS,
            **getattr(label, module).EXTRA_ARGS,
        }

        label_args["options"][module] = {
            "type": "dict",
            "options": options,
        }

    return label_args


def service_argument_spec() -> dict[str, Any]:
    service_args: dict[str, Any] = {
        "type": "dict",
        "suboptions": {},
        "required": True,
    }

    for module in service.__all__:
        if module == "common":
            continue

        options = {
            **service.common.BASE_ARGS,
            **getattr(service, module).EXTRA_ARGS,
        }

        # TODO: move to service.common
        if module == "custom":
            options["label_helpers"] = label_argument_spec()

        service_args["suboptions"][module] = {
            "type": "dict",
            "suboptions": options,
        }

    return service_args


def settings_spec() -> dict[str, Any]:
    settings: dict[str, Any] = {
        "type": "dict",
        "suboptions": {
            "default_definition": {"type": "dict"},
            "service_default_args": {"type": "dict", "suboptions": {}},
            "service_default_definitions": {"type": "dict", "suboptions": {}},
        },
    }

    for module in service.__all__:
        if module == "common":
            continue

        settings["suboptions"]["service_default_definitions"]["suboptions"][module] = {
            "type": "dict",
        }

        args = deepcopy(getattr(service, module).EXTRA_ARGS)
        for arg in args.values():
            arg.pop("required", None)

        settings["suboptions"]["service_default_args"]["suboptions"][module] = {
            "type": "dict",
            "suboptions": args,
        }

    return settings


def main() -> None:
    module = AnsibleModule(
        argument_spec={
            "name": {
                "aliases": ["project"],
                "type": "str",
                "required": True,
            },
            "project_dir": {
                "type": "path",
                "default": "/var/lib/ez_compose",
            },
            "settings": settings_spec(),
            "services": service_argument_spec(),
        },
    )

    state = common.get_state(module)

    for name, args in module.params["services"].items():
        state = getattr(service, name).helper(state, args)


if __name__ == "__main__":
    main()