{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.services.podman;

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

  awaitPodmanUnshare = pkgs.writeShellScript "await-podman-unshare" ''
    until ${cfg.package}/bin/podman unshare ${pkgs.coreutils}/bin/true; do
      sleep 1;
    done
  '';

  createQuadletSource = name: networkDef:
    let
      cfg = (podman-lib.deepMerge {
        Install = {
          WantedBy = (if networkDef.autoStart then [
            "default.target"
            "multi-user.target"
          ] else
            [ ]);
        };
        Network = {
          Driver = networkDef.driver;
          Gateway = networkDef.gateway;
          Internal = networkDef.internal;
          NetworkName = name;
          Label = networkDef.labels // { "nix.home-manager.managed" = true; };
          PodmanArgs = networkDef.extraPodmanArgs;
          Subnet = networkDef.subnet;
        };
        Service = {
          Environment = {
            PATH = (builtins.concatStringsSep ":" [
              "${podman-lib.newuidmapPaths}"
              "${makeBinPath [ pkgs.su pkgs.coreutils ]}"
            ]);
          };
          ExecStartPre = [ "${awaitPodmanUnshare}" ];
          TimeoutStartSec = 15;
          RemainAfterExit = "yes";
        };
        Unit = {
          After = [ "network.target" ];
          Description = (if (builtins.isString networkDef.description) then
            networkDef.description
          else
            "Service for network ${name}");
        };
      } networkDef.extraConfig);
    in ''
      # Automatically generated by home-manager for podman network configuration
      # DO NOT EDIT THIS FILE DIRECTLY
      #
      # ${name}.network
      ${podman-lib.toQuadletIni cfg}
    '';

  toQuadletInternal = name: networkDef: {
    assertions = podman-lib.buildConfigAsserts name networkDef.extraConfig;
    serviceName =
      "podman-${name}"; # quadlet service name: 'podman-<name>-network.service'
    source = podman-lib.removeBlankLines (createQuadletSource name networkDef);
    resourceType = "network";
  };

in let
  networkDefinitionType = types.submodule {
    options = {

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

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

      driver = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "bridge";
        description = "The network driver to use.";
      };

      extraConfig = mkOption {
        type = podman-lib.extraConfigType;
        default = { };
        example = literalExpression ''
          {
            Network = {
              ContainerConfModule = "/etc/nvd.conf";
            };
            Service = {
              TimeoutStartSec = 30;
            };
          }
        '';
        description = "INI sections and values to populate the Network Quadlet";
      };

      extraPodmanArgs = mkOption {
        type = with types; listOf str;
        default = [ ];
        example = [ "--dns=192.168.55.1" "--ipam-driver" ];
        description = ''
          Extra arguments to pass to the podman network create command.
        '';
      };

      gateway = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "192.168.20.1";
        description = "The gateway IP to use for the network.";
      };

      internal = mkOption {
        type = with types; nullOr bool;
        default = null;
        description = "Whether the network should be internal";
      };

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

      subnet = mkOption {
        type = with types; nullOr str;
        default = null;
        example = "192.168.20.0/24";
        description = "The subnet to use for the network.";
      };

    };
  };
in {
  options.services.podman.networks = mkOption {
    type = types.attrsOf networkDefinitionType;
    default = { };
    description = "Defines Podman network quadlet configurations.";
  };

  config = let networkQuadlets = mapAttrsToList toQuadletInternal cfg.networks;
  in mkIf cfg.enable {
    services.podman.internal.quadletDefinitions = networkQuadlets;
    assertions = flatten (map (network: network.assertions) networkQuadlets);

    home.file."${config.xdg.configHome}/podman/networks.manifest".text =
      podman-lib.generateManifestText networkQuadlets;
  };
}