# Taken from https://git.tempest.dev/ashe/nixos-wrapper-pounce/src/branch/main/flake.nix
# I have removed a lot of the things I didn't need, and modified a few things to fit 
# my preferences (specifically, simplified the cert situation)
{ config, lib, pkgs, ... }:
with lib;
let
  cfg = config.services.pounce;
  pkg = pkgs.pounce;
  defaultUser = "pounce";

  # {{{ systemd hardening flags
  hardeningFlags = {
    CapabilityBoundingSet = [ "" ];
    NoNewPrivileges = true;
    PrivateDevices = true;
    PrivateMounts = true;
    PrivateTmp = true;
    PrivateUsers = true;
    ProtectClock = true;
    ProtectControlGroups = true;
    ProtectKernelLogs = true;
    ProtectKernelModules = true;
    ProtectKernelTunables = true;
    ProtectProc = "invisible";
    RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
    RestrictNamespaces = true;
    RestrictSUIDSGID = true;
    SystemCallArchitectures = "native";
    SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
  };
  # }}}
in
{
  # {{{ Options
  options.services.pounce = {
    # {{{ general options
    enable = mkEnableOption "the Pounce IRC bouncer and Calico dispatcher";

    user = mkOption {
      type = types.str;
      default = defaultUser;
      description = lib.mdDoc ''
        User account under which Pounce runs. If not specified, a default user
        will be created.
      '';
    };

    dataDir = mkOption {
      type = types.str;
      default = "/run/pounce";
      description = lib.mdDoc ''
        Directory where each Pounce instance's UNIX-domain socket is stored for
        Calico to route to.
      '';
    };

    externalHost = mkOption {
      type = types.str;
      example = "example.org";
      description = lib.mdDoc ''
        Base domain name Calico will be accessible at. Each instance
        will be at a subdomain of this.
      '';
    };

    bindHost = mkOption {
      type = types.str;
      default = "localhost";
      description = lib.mdDoc ''
        The IP or host for Calico to bind to.
      '';
    };

    port = mkOption {
      type = types.port;
      default = 6697;
      description = lib.mdDoc "Port for Calico to listen on.";
    };

    openFirewall = mkOption {
      type = types.bool;
      default = false;
      description = lib.mdDoc "Open port in the firewall for Calico.";
    };

    timeout = mkOption {
      type = types.ints.positive;
      default = 1000;
      description = lib.mdDoc ''
        Timeout parameter (in milliseconds) for Calico to close a connection
        if no `ClientHello` message is sent.
      '';
    };

    certDir = mkOption {
      type = types.str;
      example = "/var/lib/acme/wildcard-irc.exmaple.com";
      description = lib.mdDoc ''
        Directory where each Pounce instance's TLS certificates and private
        keys are stored. The folder must contain wildcard certs as generated by acme.
      '';
    };
    # }}} 
    # {{{ networks 
    networks = mkOption {
      type = types.attrsOf (types.submodule {
        options = {
          config = mkOption {
            type = types.path;
            description = lib.mdDoc ''
              Location to load pounce configuration from
            '';
          };
        };
      });
      default = { };
      description = lib.mdDoc "Attribute set of IRC networks to connect to.";
    };
  };
  # }}}
  # }}}
  # {{{ config 
  config = mkIf cfg.enable {
    systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0700 ${cfg.user} ${cfg.user} -" ];
    systemd.services = mkMerge (
      # {{{ calico service 
      [
        {
          calico = {
            wantedBy = [ "multi-user.target" ];
            after = [ "network.target" ];

            description = "Calico dispatcher for Pounce IRC bouncer.";

            serviceConfig = {
              User = cfg.user;
              Group = cfg.user;
              ExecStart = ''
                ${pkg}/bin/calico \
                  -H ${cfg.bindHost} -P ${toString cfg.port} \
                  -t ${toString cfg.timeout} ${cfg.dataDir}
              '';
              Restart = "on-failure";
            } // hardeningFlags;
          };
        }
      ] ++
      # }}}
      # {{{ pounce service
      (mapAttrsToList
        (name: value: mkMerge [
          {
            "pounce-${name}" = {
              wantedBy = [ "calico.service" ];
              after = [ "network.target" ];
              before = [ "calico.service" ];

              description = "Pounce IRC bouncer for the ${name} network.";

              serviceConfig = {
                User = cfg.user;
                Group = cfg.user;
                ExecStart = ''
                  ${pkg}/bin/pounce \
                    -C ${cfg.certDir}/fullchain.pem \
                    -K ${cfg.certDir}/key.pem \
                    -U ${cfg.dataDir} -H ${name}.${cfg.externalHost} \
                    ${value.config}
                '';
                Restart = "on-failure";
              } // hardeningFlags;
            };
          }
        ])
        cfg.networks)
      # }}}
    );

    users = optionalAttrs (cfg.user == defaultUser) {
      users.${defaultUser} = {
        group = defaultUser;
        isSystemUser = true;
      };

      groups.${defaultUser} = { };
    };

    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
  };
  # }}}
}