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

let
  cfg = config.services.certspotter;

  configDir = pkgs.linkFarm "certspotter-config" (
    lib.toList {
      name = "watchlist";
      path = pkgs.writeText "certspotter-watchlist" (builtins.concatStringsSep "\n" cfg.watchlist);
    }
    ++ lib.optional (cfg.emailRecipients != [ ]) {
      name = "email_recipients";
      path = pkgs.writeText "certspotter-email_recipients" (builtins.concatStringsSep "\n" cfg.emailRecipients);
    }
    # always generate hooks dir when no emails are provided to allow running cert spotter with no hooks/emails
    ++ lib.optional (cfg.emailRecipients == [ ] || cfg.hooks != [ ]) {
      name = "hooks.d";
      path = pkgs.linkFarm "certspotter-hooks" (lib.imap1 (i: path: {
        inherit path;
        name = "hook${toString i}";
      }) cfg.hooks);
    });
in
{
  options.services.certspotter = {
    enable = lib.mkEnableOption "Cert Spotter, a Certificate Transparency log monitor";

    package = lib.mkPackageOption pkgs "certspotter" { };

    startAtEnd = lib.mkOption {
      type = lib.types.bool;
      description = ''
        Whether to skip certificates issued before the first launch of Cert Spotter.
        Setting this to `false` will cause Cert Spotter to download tens of terabytes of data.
      '';
      default = true;
    };

    sendmailPath = lib.mkOption {
      type = with lib.types; nullOr path;
      description = ''
        Path to the `sendmail` binary. By default, the local sendmail wrapper is used
        (see {option}`services.mail.sendmailSetuidWrapper`}).
      '';
      example = lib.literalExpression ''"''${pkgs.system-sendmail}/bin/sendmail"'';
    };

    watchlist = lib.mkOption {
      type = with lib.types; listOf str;
      description = "Domain names to watch. To monitor a domain with all subdomains, prefix its name with `.` (e.g. `.example.org`).";
      default = [ ];
      example = [ ".example.org" "another.example.com" ];
    };

    emailRecipients = lib.mkOption {
      type = with lib.types; listOf str;
      description = "A list of email addresses to send certificate updates to.";
      default = [ ];
    };

    hooks = lib.mkOption {
      type = with lib.types; listOf path;
      description = ''
        Scripts to run upon the detection of a new certificate. See `man 8 certspotter-script` or
        [the GitHub page](https://github.com/SSLMate/certspotter/blob/${pkgs.certspotter.src.rev or "master"}/man/certspotter-script.md)
        for more info.
      '';
      default = [ ];
      example = lib.literalExpression ''
        [
          (pkgs.writeShellScript "certspotter-hook" '''
            echo "Event summary: $SUMMARY."
          ''')
        ]
      '';
    };

    extraFlags = lib.mkOption {
      type = with lib.types; listOf str;
      description = "Extra command-line arguments to pass to Cert Spotter";
      example = [ "-no_save" ];
      default = [ ];
    };
  };

  config = lib.mkIf cfg.enable {
    assertions = [
      {
        assertion = (cfg.emailRecipients != [ ]) -> (cfg.sendmailPath != null);
        message = ''
          You must configure the sendmail setuid wrapper (services.mail.sendmailSetuidWrapper)
          or services.certspotter.sendmailPath
        '';
      }
    ];

    services.certspotter.sendmailPath = let
      inherit (config.security) wrapperDir;
      inherit (config.services.mail) sendmailSetuidWrapper;
    in lib.mkMerge [
      (lib.mkIf (sendmailSetuidWrapper != null) (lib.mkOptionDefault "${wrapperDir}/${sendmailSetuidWrapper.program}"))
      (lib.mkIf (sendmailSetuidWrapper == null) (lib.mkOptionDefault null))
    ];

    users.users.certspotter = {
      description = "Cert Spotter user";
      group = "certspotter";
      home = "/var/lib/certspotter";
      isSystemUser = true;
    };
    users.groups.certspotter = { };

    systemd.services.certspotter = {
      description = "Cert Spotter - Certificate Transparency Monitor";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      environment.CERTSPOTTER_CONFIG_DIR = configDir;
      environment.SENDMAIL_PATH = if cfg.sendmailPath != null then cfg.sendmailPath else "/run/current-system/sw/bin/false";
      script = ''
        export CERTSPOTTER_STATE_DIR="$STATE_DIRECTORY"
        cd "$CERTSPOTTER_STATE_DIR"
        ${lib.optionalString cfg.startAtEnd ''
          if [[ ! -d logs ]]; then
            # Don't download certificates issued before the first launch
            exec ${cfg.package}/bin/certspotter -start_at_end ${lib.escapeShellArgs cfg.extraFlags}
          fi
        ''}
        exec ${cfg.package}/bin/certspotter ${lib.escapeShellArgs cfg.extraFlags}
      '';
      serviceConfig = {
        User = "certspotter";
        Group = "certspotter";
        StateDirectory = "certspotter";
      };
    };
  };

  meta.maintainers = with lib.maintainers; [ chayleaf ];
  meta.doc = ./certspotter.md;
}
