{
  config,
  lib,
  pkgs,
  ...
}:
let
  inherit (lib)
    mkIf
    mkOption
    mkMerge
    types
    ;
  assertions = import ../assertions.nix { inherit lib; };

  cfg = config.services.podman;

  podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };

  createQuadletSource =
    name: containerDef:
    let
      extractQuadletReference =
        type: value:
        let
          regex = "([a-zA-Z0-9_-]+\\." + type + ").*";
          parts = builtins.match regex value;
        in
        if parts == null then value else builtins.elemAt parts 0;

      dependencyBySuffix =
        type: value:
        if (lib.hasInfix ".${type}" value) then
          let
            name = extractQuadletReference type value;
          in
          if (lib.hasAttr name cfg.internal.builtQuadlets) then
            [ (cfg.internal.builtQuadlets.${name}) ]
          else
            [ ]
        else
          [ ];

      withResolverFor =
        type: value:
        let
          resolve = v: dependencyBySuffix type v;
        in
        if builtins.isList value then
          builtins.concatLists (map resolve value) # Flatten list of lists
        else
          resolve value;

      dependencyServices =
        (withResolverFor "image" containerDef.image)
        ++ (withResolverFor "build" containerDef.image)
        ++ (withResolverFor "network" containerDef.network)
        ++ (withResolverFor "volume" containerDef.volumes);

      checkQuadletReference =
        types: value:
        if builtins.isList value then
          builtins.concatLists (map (checkQuadletReference types) value)
        else
          let
            type = lib.findFirst (t: lib.hasInfix ".${t}" value) null types;
          in
          if (type != null) then
            let
              quadletName = extractQuadletReference type value;
              quadletsOfType = lib.filterAttrs (
                _n: v: v.quadletData.resourceType == type
              ) cfg.internal.builtQuadlets;
            in
            if (lib.hasAttr quadletName quadletsOfType) then
              [
                (lib.replaceStrings [ quadletName ] [ "podman-${quadletName}" ] value)
              ]
            else
              [ value ]
          else if
            ((lib.hasInfix "/nix/store" value) == false && lib.hasAttr value cfg.internal.builtQuadlets)
          then
            lib.warn ''
              A value for Podman container '${name}' might use a reference to another quadlet: ${value}.
              Append the type '.${
                cfg.internal.builtQuadlets.${value}.quadletData.resourceType
              }' to '${lib.baseName value}' if this is intended.
            '' [ value ]
          else
            [ value ];

      quadlet = (
        podman-lib.deepMerge {
          Container = {
            AddCapability = containerDef.addCapabilities;
            AddDevice = containerDef.devices;
            AutoUpdate = containerDef.autoUpdate;
            ContainerName = name;
            DropCapability = containerDef.dropCapabilities;
            Entrypoint = containerDef.entrypoint;
            Environment = containerDef.environment;
            EnvironmentFile = containerDef.environmentFile;
            Exec = containerDef.exec;
            Group = containerDef.group;
            Image = checkQuadletReference [ "build" "image" ] containerDef.image;
            IP = containerDef.ip4;
            IP6 = containerDef.ip6;
            Label = (containerDef.labels // { "nix.home-manager.managed" = true; });
            Network = checkQuadletReference [ "network" ] containerDef.network;
            NetworkAlias = containerDef.networkAlias;
            PodmanArgs = containerDef.extraPodmanArgs;
            PublishPort = containerDef.ports;
            UserNS = containerDef.userNS;
            User = containerDef.user;
            Volume = checkQuadletReference [ "volume" ] containerDef.volumes;
          };
          Install = {
            WantedBy = lib.optionals containerDef.autoStart [
              "default.target"
              "multi-user.target"
            ];
          };
          Service = {
            Environment = {
              PATH = (
                builtins.concatStringsSep ":" [
                  "/run/wrappers/bin"
                  "/run/current-system/sw/bin"
                  "${pkgs.nftables}/bin"
                  "${config.home.homeDirectory}/.nix-profile/bin"
                  "${pkgs.systemd}/bin"
                ]
              );
            };
            Restart = "always";
            TimeoutStopSec = 30;
          };
          Unit = {
            Description = (
              if (builtins.isString containerDef.description) then
                containerDef.description
              else
                "Service for container ${name}"
            );
          };
        } containerDef.extraConfig
      );
    in
    {
      dependencies = dependencyServices;
      attrs = quadlet;
      text = ''
        # Automatically generated by home-manager podman container configuration
        # DO NOT EDIT THIS FILE DIRECTLY
        #
        # ${name}.container
        ${podman-lib.toQuadletIni quadlet}
      '';
    };

  toQuadletInternal =
    name: containerDef:
    let
      src = createQuadletSource name containerDef;
    in
    {
      assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
      inherit (src) dependencies;
      resourceType = "container";
      serviceName = "podman-${src.attrs.Container.ContainerName}"; # generated service name: 'podman-<name>.service'
      source = podman-lib.removeBlankLines src.text;
    };

  # Define the container user type as the user interface
  containerDefinitionType = types.submodule {
    options = {

      addCapabilities = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "CAP_DAC_OVERRIDE"
          "CAP_IPC_OWNER"
        ];
        description = "The capabilities to add to the container.";
      };

      autoStart = mkOption {
        type = types.bool;
        default = true;
        description = ''
          Whether to start the container on boot (requires user lingering).
        '';
      };

      autoUpdate = mkOption {
        type = types.enum [
          null
          "registry"
          "local"
        ];
        default = null;
        example = "registry";
        description = "The autoupdate policy for the container.";
      };

      description = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "My Container";
        description = "The description of the container.";
      };

      devices = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [ "/dev/<host>:/dev/<container>" ];
        description = "The devices to mount into the container";
      };

      dropCapabilities = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "CAP_DAC_OVERRIDE"
          "CAP_IPC_OWNER"
        ];
        description = "The capabilities to drop from the container.";
      };

      entrypoint = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "/foo.sh";
        description = "The container entrypoint.";
      };

      environment = mkOption {
        type = podman-lib.primitiveAttrs;
        default = { };
        example = {
          VAR1 = "0:100";
          VAR2 = true;
          VAR3 = 5;
        };
        description = "Environment variables to set in the container.";
      };

      environmentFile = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "/etc/environment"
          "/etc/other-env"
        ];
        description = ''
          Paths to files containing container environment variables.
        '';
      };

      exec = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "sleep inf";
        description = "The command to run after the container start.";
      };

      extraPodmanArgs = mkOption {
        type = types.listOf types.str;
        default = [ ];
        example = [
          "--security-opt=no-new-privileges"
          "--security-opt=seccomp=unconfined"
        ];
        description = "Extra arguments to pass to the podman run command.";
      };

      extraConfig = mkOption {
        type = podman-lib.extraConfigType;
        default = { };
        example = {
          Container = {
            User = 1000;
          };
          Service = {
            TimeoutStartSec = 15;
          };
        };
        description = ''
          INI sections and values to populate the Container Quadlet.
        '';
      };

      group = mkOption {
        type = with types; nullOr (either int str);
        default = null;
        description = "The group ID inside the container.";
      };

      image = mkOption {
        type = types.str;
        example = "registry.access.redhat.com/ubi9-minimal:latest";
        description = "The container image.";
      };

      ip4 = mkOption {
        type = with types; nullOr str;
        default = null;
        description = "Set an IPv4 address for the container.";
      };

      ip6 = mkOption {
        type = with types; nullOr str;
        default = null;
        description = "Set an IPv6 address for the container.";
      };

      labels = mkOption {
        type = with types; attrsOf str;
        default = { };
        example = {
          app = "myapp";
          some-label = "somelabel";
        };
        description = "The labels to apply to the container.";
      };

      network = mkOption {
        type = with types; either str (listOf str);
        default = [ ];
        apply = value: if lib.isString value then [ value ] else value;
        example = lib.literalMD ''
          `"host"`
          or
          `"bridge_network_1"`
          or
          `[ "bridge_network_1" "bridge_network_2" ]`
        '';
        description = ''
          The network mode or network/s to connect the container to. Equivalent
          to `podman run --network=<option>`.
        '';
      };

      networkAlias = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "mycontainer"
          "web"
        ];
        description = "Network aliases for the container.";
      };

      ports = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "8080:80"
          "8443:443"
        ];
        description = "A mapping of ports between host and container";
      };

      userNS = mkOption {
        type = with types; nullOr str;
        default = null;
        description = "Use a user namespace for the container.";
      };

      user = mkOption {
        type = with types; nullOr (either int str);
        default = null;
        description = "The user ID inside the container.";
      };

      volumes = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [
          "/tmp:/tmp"
          "/var/run/test.secret:/etc/secret:ro"
        ];
        description = "The volumes to mount into the container.";
      };

    };
  };

in
{

  imports = [ ./options.nix ];

  options.services.podman.containers = mkOption {
    type = types.attrsOf containerDefinitionType;
    default = { };
    description = "Defines Podman container quadlet configurations.";
  };

  config =
    let
      containerQuadlets = lib.mapAttrsToList toQuadletInternal cfg.containers;
    in
    mkIf cfg.enable (mkMerge [
      {
        assertions = [
          (assertions.assertPlatform "services.podman.containers" config pkgs lib.platforms.linux)
        ];
      }
      (mkIf pkgs.stdenv.hostPlatform.isLinux {
        services.podman.internal.quadletDefinitions = containerQuadlets;
        assertions = lib.flatten (map (container: container.assertions) containerQuadlets);

        # manifest file
        xdg.configFile."podman/containers.manifest".text =
          podman-lib.generateManifestText containerQuadlets;
      })
    ]);
}
