{ config, lib, ... }:

let
  nncpCfgFile = "/run/nncp.hjson";
  programCfg = lib.optionalAttrs (config.programs ? nncp) config.programs.nncp;
  callerCfg = config.services.nncp.caller;
  daemonCfg = config.services.nncp.daemon;
  pkg = programCfg.package;
in
{
  options = {

    services.nncp = {
      caller = {
        enable = lib.mkEnableOption ''
          croned NNCP TCP daemon caller.
          The daemon will take configuration from
          <xref linkend="opt-programs.nncp.settings"/>
        '';
        extraArgs = lib.mkOption {
          type = with lib.types; listOf str;
          description = "Extra command-line arguments to pass to caller.";
          default = [ ];
          example = [ "-autotoss" ];
        };
      };

      daemon = {
        enable = lib.mkEnableOption ''
          NNCP TCP synronization daemon.
          The daemon will take configuration from
          <xref linkend="opt-programs.nncp.settings"/>
        '';

        socketActivation = {
          enable = lib.mkEnableOption ''
            Whether to run nncp-daemon persistently or socket-activated.
          '';
          listenStreams = lib.mkOption {
            type = with lib.types; listOf str;
            description = ''
              TCP sockets to bind to.
              See <xref linkend="opt-systemd.sockets._name_.listenStreams"/>.
            '';
            default = [ "5400" ];
          };
        };

        extraArgs = lib.mkOption {
          type = with lib.types; listOf str;
          description = "Extra command-line arguments to pass to daemon.";
          default = [ ];
          example = [ "-autotoss" ];
        };
      };

    };
  };

  config = lib.mkIf (programCfg.enable or callerCfg.enable or daemonCfg.enable) {
    assertions = [{
      assertion =
        let
          callerCongfigured =
            let neigh = config.programs.nncp.settings.neigh or { };
            in lib.lists.any (x: lib.hasAttr "calls" x && x.calls != [ ])
            (lib.attrValues neigh);
        in !callerCfg.enable || callerCongfigured;
      message = "NNCP caller enabled but call configuration is missing";
    }];

    systemd.services."nncp-caller" = {
      inherit (callerCfg) enable;
      description = "Croned NNCP TCP daemon caller.";
      documentation = [ "http://www.nncpgo.org/nncp_002dcaller.html" ];
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = ''
          ${pkg}/bin/nncp-caller -noprogress -cfg "${nncpCfgFile}" ${
            lib.strings.escapeShellArgs callerCfg.extraArgs
          }'';
        Group = "uucp";
        UMask = "0002";
      };
    };

    systemd.services."nncp-daemon" = lib.mkIf daemonCfg.enable {
      enable = !daemonCfg.socketActivation.enable;
      description = "NNCP TCP syncronization daemon.";
      documentation = [ "http://www.nncpgo.org/nncp_002ddaemon.html" ];
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = ''
          ${pkg}/bin/nncp-daemon -noprogress -cfg "${nncpCfgFile}" ${
            lib.strings.escapeShellArgs daemonCfg.extraArgs
          }'';
        Restart = "on-failure";
        Group = "uucp";
        UMask = "0002";
      };
    };

    systemd.services."nncp-daemon@" = lib.mkIf daemonCfg.socketActivation.enable {
      description = "NNCP TCP syncronization daemon.";
      documentation = [ "http://www.nncpgo.org/nncp_002ddaemon.html" ];
      after = [ "network.target" ];
      serviceConfig = {
        ExecStart = ''
          ${pkg}/bin/nncp-daemon -noprogress -ucspi -cfg "${nncpCfgFile}" ${
            lib.strings.escapeShellArgs daemonCfg.extraArgs
          }'';
        Group = "uucp";
        UMask = "0002";
        StandardInput = "socket";
        StandardOutput = "inherit";
        StandardError = "journal";
      };
    };

    systemd.sockets.nncp-daemon = lib.mkIf daemonCfg.socketActivation.enable {
      inherit (daemonCfg.socketActivation) listenStreams;
      description = "socket for NNCP TCP syncronization.";
      conflicts = [ "nncp-daemon.service" ];
      wantedBy = [ "sockets.target" ];
      socketConfig.Accept = true;
    };
  };
}
