# Copyright (c) 2024 Sagi Shnaidman (@sshnaidm)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

import os
import shlex

from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
    compare_systemd_file_content,
)

QUADLET_ROOT_PATH = "/etc/containers/systemd/"
QUADLET_NON_ROOT_PATH = "~/.config/containers/systemd/"

# https://github.com/containers/podman/blob/main/pkg/systemd/quadlet/quadlet_common.go
QUADLET_SUFFIXES = [
    ".artifact",
    ".container",
    ".volume",
    ".kube",
    ".network",
    ".image",
    ".build",
    ".pod",
    ".quadlets",
]


def resolve_quadlet_dir(module):
    quadlet_dir = module.params.get("quadlet_dir")
    if not quadlet_dir:
        user_is_root = os.geteuid() == 0
        if user_is_root:
            quadlet_dir = QUADLET_ROOT_PATH
        else:
            quadlet_dir = os.path.expanduser(QUADLET_NON_ROOT_PATH)
    return quadlet_dir


class Quadlet:
    param_map = {}

    def __init__(self, section: str, params: dict):
        self.section = section
        self.service_section = {}
        self.custom_params = self.custom_prepare_params(params)
        self.dict_params = self.prepare_params()

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for specific Quadlet types.
        """
        # This should be implemented in child classes if needed.
        return params

    def _map_restart_policy(self, policy: str) -> str:
        """
        Map Podman restart policies to systemd Restart values.
        """
        # Podman restart policies: no, on-failure, always, unless-stopped
        # systemd Restart values: no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, always
        policy_map = {
            "no": "no",
            "on-failure": "on-failure",
            "always": "always",
            "unless-stopped": "always",  # systemd doesn't have unless-stopped, use always
        }
        return policy_map.get(policy, policy)

    def prepare_params(self) -> dict:
        """
        Convert parameter values as per param_map.
        """
        processed_params = []
        seen_quadlet_keys = set()
        for param_key, quadlet_key in self.param_map.items():
            if quadlet_key in seen_quadlet_keys:
                continue
            value = self.custom_params.get(param_key)
            if value is not None:
                seen_quadlet_keys.add(quadlet_key)
                if isinstance(value, list):
                    for item in value:
                        processed_params.append([quadlet_key, item])
                else:
                    if isinstance(value, bool):
                        value = str(value).lower()
                    processed_params.append([quadlet_key, value])
        return processed_params

    def create_quadlet_content(self) -> str:
        """
        Construct the quadlet content as a string.
        """
        custom_user_options = self.custom_params.get("quadlet_options")

        # Separate quadlet_options into main-section options (plain key=value)
        # and extra sections (containing section headers like [Install])
        main_section_options = []
        extra_section_options = []
        if custom_user_options:
            for opt in custom_user_options:
                if opt.lstrip().startswith("["):
                    extra_section_options.append(opt)
                else:
                    main_section_options.append(opt)

        # Build main section
        content = f"[{self.section}]\n" + "\n".join(f"{key}={value}" for key, value in self.dict_params)

        # Append plain key=value quadlet_options to the main section
        if main_section_options:
            content += "\n" + "\n".join(main_section_options)

        # Add Service section if there are service parameters
        # Check if user hasn't already added a [Service] section in quadlet_options
        has_service_in_custom = False
        if custom_user_options:
            has_service_in_custom = any("[Service]" in opt for opt in custom_user_options)

        if self.service_section and not has_service_in_custom:
            service_text = "\n\n[Service]\n" + "\n".join(
                f"{key}={value}" for key, value in self.service_section.items()
            )
            content += service_text

        # Append extra section options (e.g. [Install] blocks) after the Service section
        if extra_section_options:
            content += "\n\n" + "\n".join(extra_section_options)

        return content + "\n"

    def write_to_file(self, path: str):
        """
        Write the quadlet content to a file at the specified path.
        """
        content = self.create_quadlet_content()
        with open(path, "w") as file:
            file.write(content)


class ContainerQuadlet(Quadlet):
    param_map = {
        "cap_add": "AddCapability",
        "device": "AddDevice",
        "annotation": "Annotation",
        "name": "ContainerName",
        # the following are not implemented yet in Podman module
        "AutoUpdate": "AutoUpdate",
        "ContainersConfModule": "ContainersConfModule",
        # end of not implemented yet
        "dns": "DNS",
        "dns_option": "DNSOption",
        "dns_search": "DNSSearch",
        "cap_drop": "DropCapability",
        "cgroups": "CgroupsMode",
        "entrypoint": "Entrypoint",
        "env": "Environment",
        "env_file": "EnvironmentFile",
        "env_host": "EnvironmentHost",
        "etc_hosts": "AddHost",
        "command": "Exec",
        "expose": "ExposeHostPort",
        "gidmap": "GIDMap",
        "global_args": "GlobalArgs",
        "group": "Group",  # Does not exist in module parameters
        "group_add": "GroupAdd",
        "healthcheck": "HealthCmd",
        "healthcheck_interval": "HealthInterval",
        "healthcheck_failure_action": "HealthOnFailure",
        "healthcheck_retries": "HealthRetries",
        "healthcheck_start_period": "HealthStartPeriod",
        "healthcheck_timeout": "HealthTimeout",
        "health_startup_cmd": "HealthStartupCmd",
        "health_startup_interval": "HealthStartupInterval",
        "health_startup_retries": "HealthStartupRetries",
        "health_startup_success": "HealthStartupSuccess",
        "health_startup_timeout": "HealthStartupTimeout",
        "hostname": "HostName",
        "image": "Image",
        "ip": "IP",
        "ip6": "IP6",
        "label": "Label",
        "log_driver": "LogDriver",
        "log_opt": "LogOpt",
        "Mask": "Mask",  # add it in security_opt
        "mount": "Mount",
        "network": "Network",
        "network_aliases": "NetworkAlias",
        "no_new_privileges": "NoNewPrivileges",
        "sdnotify": "Notify",
        "pids_limit": "PidsLimit",
        "pod": "Pod",
        "publish": "PublishPort",
        "pull": "Pull",
        "read_only": "ReadOnly",
        "read_only_tmpfs": "ReadOnlyTmpfs",
        "rootfs": "Rootfs",
        "init": "RunInit",
        "SeccompProfile": "SeccompProfile",
        "secrets": "Secret",
        # All these are in security_opt
        "SecurityLabelDisable": "SecurityLabelDisable",
        "SecurityLabelFileType": "SecurityLabelFileType",
        "SecurityLabelLevel": "SecurityLabelLevel",
        "SecurityLabelNested": "SecurityLabelNested",
        "SecurityLabelType": "SecurityLabelType",
        "shm_size": "ShmSize",
        "stop_signal": "StopSignal",
        "stop_timeout": "StopTimeout",
        "subgidname": "SubGIDMap",
        "subuidname": "SubUIDMap",
        "sysctl": "Sysctl",
        "timezone": "Timezone",
        "tmpfs": "Tmpfs",
        "uidmap": "UIDMap",
        "ulimit": "Ulimit",
        "Unmask": "Unmask",  # --security-opt unmask=ALL
        "user": "User",
        "userns": "UserNS",
        "volume": "Volume",
        "workdir": "WorkingDir",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Container", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for container-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        if params["annotation"]:
            params["annotation"] = ["%s=%s" % (k, v) for k, v in params["annotation"].items()]
        if params["cap_add"]:
            params["cap_add"] = " ".join(params["cap_add"])
        if params["cap_drop"]:
            params["cap_drop"] = " ".join(params["cap_drop"])
        if params["command"]:
            params["command"] = (
                " ".join([str(j) for j in params["command"]])
                if isinstance(params["command"], list)
                else params["command"]
            )
        if params["label"]:
            params["label"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["label"].items()]
        if params["env"]:
            params["env"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["env"].items()]
        if params["rootfs"]:
            params["rootfs"] = params["image"]
            params["image"] = None
        if params["sysctl"]:
            params["sysctl"] = ["%s=%s" % (k, v) for k, v in params["sysctl"].items()]
        if params["tmpfs"]:
            params["tmpfs"] = ["%s:%s" % (k, v) if v else k for k, v in params["tmpfs"].items()]

        # Work on params which are not in the param_map but can be calculated
        params["global_args"] = []
        if params["user"] and len(str(params["user"]).split(":")) > 1:
            user, group = params["user"].split(":")
            params["user"] = user
            params["group"] = group
        if params["security_opt"]:
            if "no-new-privileges" in params["security_opt"]:
                params["no_new_privileges"] = True
                params["security_opt"].remove("no-new-privileges")
        if params["log_level"]:
            params["global_args"].append(f"--log-level {params['log_level']}")
        if params["debug"]:
            params["global_args"].append("--log-level debug")

        # Work on params which are not in the param_map and add them to PodmanArgs
        params["podman_args"] = []
        if params["arch"]:
            params["podman_args"].append(f"--arch {params['arch']}")
        if params["authfile"]:
            params["podman_args"].append(f"--authfile {params['authfile']}")
        if params["attach"]:
            for attach in params["attach"]:
                params["podman_args"].append(f"--attach {attach}")
        if params["blkio_weight"]:
            params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}")
        if params["blkio_weight_device"]:
            params["podman_args"].append(
                " ".join(
                    [f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()]
                )
            )
        if params["cgroupns"]:
            params["podman_args"].append(f"--cgroupns {params['cgroupns']}")
        if params["cgroup_conf"]:
            for k, v in params["cgroup_conf"].items():
                params["podman_args"].append(f"--cgroup-conf {k}={v}")
        if params["cgroup_parent"]:
            params["podman_args"].append(f"--cgroup-parent {params['cgroup_parent']}")
        if params["chrootdirs"]:
            params["podman_args"].append(f"--chrootdirs {params['chrootdirs']}")
        if params["cidfile"]:
            params["podman_args"].append(f"--cidfile {params['cidfile']}")
        if params["conmon_pidfile"]:
            params["podman_args"].append(f"--conmon-pidfile {params['conmon_pidfile']}")
        if params["cpus"]:
            params["podman_args"].append(f"--cpus {params['cpus']}")
        if params["cpuset_cpus"]:
            params["podman_args"].append(f"--cpuset-cpus {params['cpuset_cpus']}")
        if params["cpuset_mems"]:
            params["podman_args"].append(f"--cpuset-mems {params['cpuset_mems']}")
        if params["cpu_period"]:
            params["podman_args"].append(f"--cpu-period {params['cpu_period']}")
        if params["cpu_quota"]:
            params["podman_args"].append(f"--cpu-quota {params['cpu_quota']}")
        if params["cpu_rt_period"]:
            params["podman_args"].append(f"--cpu-rt-period {params['cpu_rt_period']}")
        if params["cpu_rt_runtime"]:
            params["podman_args"].append(f"--cpu-rt-runtime {params['cpu_rt_runtime']}")
        if params["cpu_shares"]:
            params["podman_args"].append(f"--cpu-shares {params['cpu_shares']}")
        if params["decryption_key"]:
            params["podman_args"].append(f"--decryption-key {params['decryption_key']}")
        if params["device_cgroup_rule"]:
            params["podman_args"].append(f"--device-cgroup-rule {params['device_cgroup_rule']}")
        if params["device_read_bps"]:
            for i in params["device_read_bps"]:
                params["podman_args"].append(f"--device-read-bps {i}")
        if params["device_read_iops"]:
            for i in params["device_read_iops"]:
                params["podman_args"].append(f"--device-read-iops {i}")
        if params["device_write_bps"]:
            for i in params["device_write_bps"]:
                params["podman_args"].append(f"--device-write-bps {i}")
        if params["device_write_iops"]:
            for i in params["device_write_iops"]:
                params["podman_args"].append(f"--device-write-iops {i}")
        if params["etc_hosts"]:
            params["etc_hosts"] = ["%s:%s" % (k, v) for k, v in params["etc_hosts"].items()]
        if params["env_merge"]:
            for k, v in params["env_merge"].items():
                params["podman_args"].append(f"--env {k}={v}")
        if params["gpus"]:
            params["podman_args"].append(f"--gpus {params['gpus']}")
        if params["group_entry"]:
            params["podman_args"].append(f"--group-entry {params['group_entry']}")
        if params["hostuser"]:
            params["podman_args"].append(f"--hostuser {params['hostuser']}")
        if params["hooks_dir"]:
            for hook in params["hooks_dir"]:
                params["podman_args"].append(f"--hooks-dir {hook}")
        if params["http_proxy"]:
            params["podman_args"].append(f"--http-proxy {params['http_proxy']}")
        if params["image_volume"]:
            params["podman_args"].append(f"--image-volume {params['image_volume']}")
        if params["init_ctr"]:
            params["podman_args"].append(f"--init-ctr {params['init_ctr']}")
        if params["init_path"]:
            params["podman_args"].append(f"--init-path {params['init_path']}")
        if params["interactive"]:
            params["podman_args"].append("--interactive")
        if params["ipc"]:
            params["podman_args"].append(f"--ipc {params['ipc']}")
        if params["kernel_memory"]:
            params["podman_args"].append(f"--kernel-memory {params['kernel_memory']}")
        if params["label_file"]:
            params["podman_args"].append(f"--label-file {params['label_file']}")
        if params["log_opt"]:
            params["log_opt"] = [
                "%s=%s" % (k.replace("max_size", "max-size"), v) for k, v in params["log_opt"].items() if v is not None
            ]
        if params["mac_address"]:
            params["podman_args"].append(f"--mac-address {params['mac_address']}")
        if params["memory"]:
            params["podman_args"].append(f"--memory {params['memory']}")
        if params["memory_reservation"]:
            params["podman_args"].append(f"--memory-reservation {params['memory_reservation']}")
        if params["memory_swap"]:
            params["podman_args"].append(f"--memory-swap {params['memory_swap']}")
        if params["memory_swappiness"]:
            params["podman_args"].append(f"--memory-swappiness {params['memory_swappiness']}")
        if params["no_healthcheck"]:
            params["podman_args"].append("--no-healthcheck")
        if params["no_hosts"] is not None:
            params["podman_args"].append(f"--no-hosts={params['no_hosts']}")
        if params["oom_kill_disable"]:
            params["podman_args"].append(f"--oom-kill-disable={params['oom_kill_disable']}")
        if params["oom_score_adj"]:
            params["podman_args"].append(f"--oom-score-adj {params['oom_score_adj']}")
        if params["os"]:
            params["podman_args"].append(f"--os {params['os']}")
        if params["passwd"]:
            params["podman_args"].append("--passwd")
        if params["passwd_entry"]:
            params["podman_args"].append(f"--passwd-entry {params['passwd_entry']}")
        if params["personality"]:
            params["podman_args"].append(f"--personality {params['personality']}")
        if params["pid"]:
            params["podman_args"].append(f"--pid {params['pid']}")
        if params["pid_file"]:
            params["podman_args"].append(f"--pid-file {params['pid_file']}")
        if params["platform"]:
            params["podman_args"].append(f"--platform {params['platform']}")
        if params["preserve_fd"]:
            for pres in params["preserve_fd"]:
                params["podman_args"].append(f"--preserve-fd {pres}")
        if params["preserve_fds"]:
            params["podman_args"].append(f"--preserve-fds {params['preserve_fds']}")
        if params["privileged"]:
            params["podman_args"].append("--privileged")
        if params["publish_all"]:
            params["podman_args"].append("--publish-all")
        if params["rdt_class"]:
            params["podman_args"].append(f"--rdt-class {params['rdt_class']}")
        if params["requires"]:
            params["podman_args"].append(f"--requires {','.join(params['requires'])}")
        # restart_policy is handled in the [Service] section, not as a podman argument
        # to avoid conflicts with --rm option in Quadlet files
        if params["restart_policy"]:
            self.service_section["Restart"] = self._map_restart_policy(params["restart_policy"])
        if params["retry"]:
            params["podman_args"].append(f"--retry {params['retry']}")
        if params["retry_delay"]:
            params["podman_args"].append(f"--retry-delay {params['retry_delay']}")
        if params["rm"]:
            params["podman_args"].append("--rm")
        if params["rmi"]:
            params["podman_args"].append("--rmi")
        if params["seccomp_policy"]:
            params["podman_args"].append(f"--seccomp-policy {params['seccomp_policy']}")
        if params["security_opt"]:
            for security_opt in params["security_opt"]:
                params["podman_args"].append(f"--security-opt {security_opt}")
        if params["shm_size_systemd"]:
            params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
        if params["sig_proxy"]:
            params["podman_args"].append(f"--sig-proxy {params['sig_proxy']}")
        if params["systemd"]:
            params["podman_args"].append(f"--systemd={str(params['systemd']).lower()}")
        if params["timeout"]:
            params["podman_args"].append(f"--timeout {params['timeout']}")
        if params["tls_verify"]:
            params["podman_args"].append(f"--tls-verify={str(params['tls_verify']).lower()}")
        if params["tty"]:
            params["podman_args"].append("--tty")
        if params["umask"]:
            params["podman_args"].append(f"--umask {params['umask']}")
        if params["unsetenv"]:
            for unset in params["unsetenv"]:
                params["podman_args"].append(f"--unsetenv {unset}")
        if params["unsetenv_all"]:
            params["podman_args"].append("--unsetenv-all")
        if params["uts"]:
            params["podman_args"].append(f"--uts {params['uts']}")
        if params["variant"]:
            params["podman_args"].append(f"--variant {params['variant']}")
        if params["volumes_from"]:
            for volume in params["volumes_from"]:
                params["podman_args"].append(f"--volumes-from {volume}")
        if params["cmd_args"]:
            params["podman_args"] += params["cmd_args"]

        # Return params with custom processing applied
        return params


class NetworkQuadlet(Quadlet):
    param_map = {
        "name": "NetworkName",
        "internal": "Internal",
        "driver": "Driver",
        "gateway": "Gateway",
        "disable_dns": "DisableDNS",
        "subnet": "Subnet",
        "ip_range": "IPRange",
        "ipv6": "IPv6",
        "opt": "Options",
        # Add more parameter mappings specific to networks
        "ContainersConfModule": "ContainersConfModule",
        "dns": "DNS",
        "ipam_driver": "IPAMDriver",
        "Label": "Label",
        "global_args": "GlobalArgs",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Network", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for network-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        if params["debug"]:
            params["global_args"].append("--log-level debug")
        if params["opt"]:
            new_opt = []
            for k, v in params["opt"].items():
                if v is not None:
                    new_opt.append(f"{k}={v}")
            params["opt"] = new_opt
        return params


# This is a inherited class that represents a Quadlet file for the Podman pod
class PodQuadlet(Quadlet):
    param_map = {
        "name": "PodName",
        "network": "Network",
        "publish": "PublishPort",
        "volume": "Volume",
        "ContainersConfModule": "ContainersConfModule",
        "global_args": "GlobalArgs",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Pod", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for pod-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        params["global_args"] = []
        params["podman_args"] = []

        if params["add_host"]:
            for host in params["add_host"]:
                params["podman_args"].append(f"--add-host {host}")
        if params["cgroup_parent"]:
            params["podman_args"].append(f"--cgroup-parent {params['cgroup_parent']}")
        if params["blkio_weight"]:
            params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}")
        if params["blkio_weight_device"]:
            params["podman_args"].append(
                " ".join(
                    [f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()]
                )
            )
        if params["cpuset_cpus"]:
            params["podman_args"].append(f"--cpuset-cpus {params['cpuset_cpus']}")
        if params["cpuset_mems"]:
            params["podman_args"].append(f"--cpuset-mems {params['cpuset_mems']}")
        if params["cpu_shares"]:
            params["podman_args"].append(f"--cpu-shares {params['cpu_shares']}")
        if params["cpus"]:
            params["podman_args"].append(f"--cpus {params['cpus']}")
        if params["device"]:
            for device in params["device"]:
                params["podman_args"].append(f"--device {device}")
        if params["device_read_bps"]:
            for i in params["device_read_bps"]:
                params["podman_args"].append(f"--device-read-bps {i}")
        if params["device_write_bps"]:
            for i in params["device_write_bps"]:
                params["podman_args"].append(f"--device-write-bps {i}")
        if params["dns"]:
            for dns in params["dns"]:
                params["podman_args"].append(f"--dns {dns}")
        if params["dns_opt"]:
            for dns_option in params["dns_opt"]:
                params["podman_args"].append(f"--dns-option {dns_option}")
        if params["dns_search"]:
            for dns_search in params["dns_search"]:
                params["podman_args"].append(f"--dns-search {dns_search}")
        if params["gidmap"]:
            for gidmap in params["gidmap"]:
                params["podman_args"].append(f"--gidmap {gidmap}")
        if params["exit_policy"]:
            params["podman_args"].append(f"--exit-policy={params['exit_policy']}")
        if params["gpus"]:
            params["podman_args"].append(f"--gpus {params['gpus']}")
        if params["hostname"]:
            params["podman_args"].append(f"--hostname {params['hostname']}")
        if params["infra"]:
            params["podman_args"].append(f"--infra {params['infra']}")
        if params["infra_command"]:
            params["podman_args"].append(f"--infra-command {params['infra_command']}")
        if params["infra_conmon_pidfile"]:
            params["podman_args"].append(f"--infra-conmon-pidfile {params['infra_conmon_pidfile']}")
        if params["infra_image"]:
            params["podman_args"].append(f"--infra-image {params['infra_image']}")
        if params["infra_name"]:
            params["podman_args"].append(f"--infra-name {params['infra_name']}")
        if params["ip"]:
            params["podman_args"].append(f"--ip {params['ip']}")
        if params["ip6"]:
            params["podman_args"].append(f"--ip6 {params['ip6']}")
        if params["label"]:
            for label, label_v in params["label"].items():
                params["podman_args"].append(f"--label {label}={label_v}")
        if params["label_file"]:
            params["podman_args"].append(f"--label-file {params['label_file']}")
        if params["mac_address"]:
            params["podman_args"].append(f"--mac-address {params['mac_address']}")
        if params["memory"]:
            params["podman_args"].append(f"--memory {params['memory']}")
        if params["memory_swap"]:
            params["podman_args"].append(f"--memory-swap {params['memory_swap']}")
        if params["no_hosts"]:
            params["podman_args"].append(f"--no-hosts {params['no_hosts']}")
        if params["pid"]:
            params["podman_args"].append(f"--pid {params['pid']}")
        if params["pod_id_file"]:
            params["podman_args"].append(f"--pod-id-file {params['pod_id_file']}")
        # restart_policy is handled in the [Service] section, not as a podman argument
        # to avoid conflicts with --rm option in Quadlet files
        if params["restart_policy"]:
            self.service_section["Restart"] = self._map_restart_policy(params["restart_policy"])
        if params["security_opt"]:
            for security_opt in params["security_opt"]:
                params["podman_args"].append(f"--security-opt {security_opt}")
        if params["share"]:
            params["podman_args"].append(f"--share {params['share']}")
        if params["share_parent"] is not None:
            params["podman_args"].append(f"--share-parent={str(params['share_parent']).lower()}")
        if params["shm_size"]:
            params["podman_args"].append(f"--shm-size {params['shm_size']}")
        if params["shm_size_systemd"]:
            params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
        if params["subgidname"]:
            params["podman_args"].append(f"--subgidname {params['subgidname']}")
        if params["subuidname"]:
            params["podman_args"].append(f"--subuidname {params['subuidname']}")
        if params["sysctl"]:
            for k, v in params["sysctl"].items():
                params["podman_args"].append(f"--sysctl {k}={v}")
        if params["uidmap"]:
            for uidmap in params["uidmap"]:
                params["podman_args"].append(f"--uidmap {uidmap}")
        if params["userns"]:
            params["podman_args"].append(f"--userns {params['userns']}")
        if params["uts"]:
            params["podman_args"].append(f"--uts {params['uts']}")
        if params["volumes_from"]:
            for volume in params["volumes_from"]:
                params["podman_args"].append(f"--volumes-from {volume}")
        if params["debug"]:
            params["global_args"].append("--log-level debug")

        return params


# This is a inherited class that represents a Quadlet file for the Podman volume
class VolumeQuadlet(Quadlet):
    param_map = {
        "name": "VolumeName",
        "driver": "Driver",
        "label": "Label",
        # 'opt': 'Options',
        "ContainersConfModule": "ContainersConfModule",
        "global_args": "GlobalArgs",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Volume", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for volume-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        params["global_args"] = []
        params["podman_args"] = []

        if params["debug"]:
            params["global_args"].append("--log-level debug")
        if params["label"]:
            params["label"] = ["%s=%s" % (k, v) for k, v in params["label"].items()]
        if params["options"]:
            for opt in params["options"]:
                params["podman_args"].append(f"--opt {opt}")

        return params


# This is a inherited class that represents a Quadlet file for the Podman kube
class KubeQuadlet(Quadlet):
    param_map = {
        "configmap": "ConfigMap",
        "log_driver": "LogDriver",
        "network": "Network",
        "kube_file": "Yaml",
        "userns": "UserNS",
        "AutoUpdate": "AutoUpdate",
        "ExitCodePropagation": "ExitCodePropagation",
        "KubeDownForce": "KubeDownForce",
        "PublishPort": "PublishPort",
        "SetWorkingDirectory": "SetWorkingDirectory",
        "ContainersConfModule": "ContainersConfModule",
        "global_args": "GlobalArgs",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Kube", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for kube-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        params["global_args"] = []
        params["podman_args"] = []

        if params["debug"]:
            params["global_args"].append("--log-level debug")

        return params


# This is a inherited class that represents a Quadlet file for the Podman image
class ImageQuadlet(Quadlet):
    param_map = {
        "AllTags": "AllTags",
        "arch": "Arch",
        "authfile": "AuthFile",
        "auth_file": "AuthFile",
        "ca_cert_dir": "CertDir",
        "creds": "Creds",
        "DecryptionKey": "DecryptionKey",
        "name": "Image",
        "ImageTag": "ImageTag",
        "OS": "OS",
        "validate_certs": "TLSVerify",
        "Variant": "Variant",
        "ContainersConfModule": "ContainersConfModule",
        "global_args": "GlobalArgs",
        "podman_args": "PodmanArgs",
    }

    def __init__(self, params: dict):
        super().__init__("Image", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for image-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        params["global_args"] = []
        params["podman_args"] = []

        if params["username"] and params["password"]:
            params["creds"] = f"{params['username']}:{params['password']}"
        # if params['validate_certs'] is not None:
        #     params['validate_certs'] = str(params['validate_certs']).lower()

        return params


# This is a inherited class that represents a Quadlet file for the Podman build
class BuildQuadlet(Quadlet):
    param_map = {
        "annotation": "Annotation",
        "arch": "Arch",
        "authfile": "AuthFile",
        "build_args": "BuildArg",
        "ContainersConfModule": "ContainersConfModule",
        "dns": "DNS",
        "dns_opt": "DNSOption",
        "dns_search": "DNSSearch",
        "env": "Environment",
        "file": "File",
        "force_rm": "ForceRM",
        "global_args": "GlobalArgs",
        "group_add": "GroupAdd",
        "ignore_file": "IgnoreFile",
        "name": "ImageTag",
        "labels": "Label",
        "network": "Network",
        "podman_args": "PodmanArgs",
        "pull": "Pull",
        "retry": "Retry",
        "retry_delay": "RetryDelay",
        "secret": "Secret",
        "set_working_directory": "SetWorkingDirectory",
        "target": "Target",
        "validate_certs": "TLSVerify",
        "variant": "Variant",
        "volume": "Volume",
    }

    def __init__(self, params: dict):
        super().__init__("Build", params)

    def custom_prepare_params(self, params: dict) -> dict:
        """
        Custom parameter processing for build-specific parameters.
        """
        # Work on params in params_map and convert them to a right form
        params["global_args"] = []
        params["podman_args"] = []

        if params["annotation"]:
            params["annotation"] = ["%s=%s" % (k, v) for k, v in params["annotation"].items()]
        if params["username"] and params["password"]:
            params["podman_args"].append(f"--creds {shlex.quote(params['username'] + ':' + params['password'])}")
        if params["build_args"]:
            params["build_args"] = ["%s=%s" % (k, v) for k, v in params["build_args"].items()]
        if params["env"]:
            params["env"] = ["%s=%s" % (k, v) for k, v in params["env"].items()]
        if params["labels"]:
            params["labels"] = ["%s=%s" % (k, v) for k, v in params["labels"].items()]
        if params["cmd_args"]:
            params["podman_args"] += params["cmd_args"]
        if params["ca_cert_dir"]:
            params["podman_args"].append(f"--cert-dir {params['ca_cert_dir']}")
        if params["cache"] is False:
            params["podman_args"].append("--no-cache")

        return params


def check_quadlet_directory(module, quadlet_dir):
    """Check if the directory exists and is writable. If not, fail the module."""
    if not os.path.exists(quadlet_dir):
        try:
            os.makedirs(quadlet_dir)
        except Exception as e:
            module.fail_json(msg="Directory for quadlet_file can't be created: %s" % e)
    if not os.access(quadlet_dir, os.W_OK):
        module.fail_json(msg="Directory for quadlet_file is not writable: %s" % quadlet_dir)


def create_quadlet_state(module, issuer):
    """Create a quadlet file for the specified issuer."""
    class_map = {
        "build": BuildQuadlet,
        "container": ContainerQuadlet,
        "network": NetworkQuadlet,
        "pod": PodQuadlet,
        "volume": VolumeQuadlet,
        "kube": KubeQuadlet,
        "image": ImageQuadlet,
    }
    # Let's detect which user is running
    user = "root" if os.geteuid() == 0 else "user"
    quadlet_dir = module.params.get("quadlet_dir")
    if not quadlet_dir:
        if user == "root":
            quadlet_dir = QUADLET_ROOT_PATH
        else:
            quadlet_dir = os.path.expanduser(QUADLET_NON_ROOT_PATH)
    # Create a filename based on the issuer
    if not module.params.get("name") and not module.params.get("quadlet_filename"):
        module.fail_json(msg=f"Filename for {issuer} is required for creating a quadlet file.")
    if issuer == "image" or issuer == "build":
        name = module.params["name"].split("/")[-1].split(":")[0]
    else:
        name = module.params.get("name")
    quad_file_name = module.params["quadlet_filename"]
    if quad_file_name and not quad_file_name.endswith(f".{issuer}"):
        quad_file_name = f"{quad_file_name}.{issuer}"
    filename = quad_file_name or f"{name}.{issuer}"
    quadlet_file_path = os.path.join(quadlet_dir, filename)
    # Check if the directory exists and is writable
    if not module.check_mode:
        check_quadlet_directory(module, quadlet_dir)
    # Specify file permissions
    mode = module.params.get("quadlet_file_mode", None)
    if mode is None and not os.path.exists(quadlet_file_path):
        # default mode for new quadlet file only
        mode = "0640"
    # Check if file already exists and if it's different
    quadlet = class_map[issuer](module.params)
    quadlet_content = quadlet.create_quadlet_content()
    file_diff = compare_systemd_file_content(quadlet_file_path, quadlet_content)
    if bool(file_diff):
        if not module.check_mode:
            quadlet.write_to_file(quadlet_file_path)
            if mode is not None:
                module.set_mode_if_different(quadlet_file_path, mode, False)
        results_update = {
            "changed": True,
            "diff": {
                "before": ("\n".join(file_diff[0]) if isinstance(file_diff[0], list) else file_diff[0] + "\n"),
                "after": ("\n".join(file_diff[1]) if isinstance(file_diff[1], list) else file_diff[1] + "\n"),
            },
        }
    else:
        # adjust file permissions
        diff = {}
        if mode is not None and module.set_mode_if_different(quadlet_file_path, mode, False, diff):
            results_update = {"changed": True, "diff": diff}
        else:
            results_update = {}
    return results_update


# Check with following command:
# QUADLET_UNIT_DIRS=<Directory> /usr/lib/systemd/system-generators/podman-system-generator {--user} --dryrun
