# based on https://gist.github.com/ajs124/ff04ab14435908d914cf5cedbc56a52e
{ config, lib, pkgs, ... }:

with lib;
let
  cfg = config.services.rt;

  configFile = pkgs.writeTextFile {
    name = "RT_SiteConfig.pm";
    text = ''
      use utf8;

      # System (Base configuration)
      Set($rtname, '${cfg.rtName}'); # Changing this will break responses to existing tickets
      Set($Organization, '${cfg.organization}'); # Changing this will break all existing tickets
      Set($CorrespondAddress, '${cfg.correspondAddress}');
      Set($CommentAddress, '${cfg.commentAddress}');
      Set($WebDomain, '${cfg.domain}');
      Set($Timezone, '${cfg.timeZone}');

      Set($DatabaseType, 'Pg');
      Set($DatabaseHost, 'localhost');
      Set($DatabaseUser, 'rt_user');
      Set($DatabaseName, 'rt5');
      # Read database password from file
      open my $fh, '<', '${cfg.dbPasswordFile}' or die 'Can\'t open file $!';
      my $dbpw = do { local $/; <$fh> };
      $dbpw =~ s/^\s+|\s+$//g;
      Set($DatabasePassword, $dbpw);

      # System (Logging)
      Set($LogToSTDERR, undef); # Don't log twice

      # System (Incoming mail gateway)
      Set($OwnerEmail, '${cfg.ownerEmail}');
      Set($MaxAttachmentSize, 15360000);
      Set($CheckMoreMSMailHeaders, 1);
      Set($RTAddressRegexp, '^(helpdesk|sales)\@(m-labs.hk)$');
      Set($LoopsToRTOwner, 0);

      # System (Outgoing mail)
      Set($SetOutgoingMailFrom, 'helpdesk@m-labs.hk');

      # System (Sendmail configuration)
      Set($SendmailPath, '${cfg.sendmailPath}');
      Set($SendmailArguments, '${concatStringsSep " " cfg.sendmailArguments}');

      # System (Application logic)
      Set($ParseNewMessageForTicketCcs, 1);

      # System (Extra Security)
      Set($RestrictLoginReferrer, 1);

      # System (Date and time handling)
      Set($DefaultTimeUnitsToHours, 1);
      Set($TimeInICal, 1);
      Set($DateTimeFormat, 'RFC2822');

      # System (Authorization and user configuration)
      Set($AutoLogoff, 262800); # 6 months
      Set($WebSecureCookies, 1);

      # Web Interface (Base configuration)
      Set($CanonicalizeRedirectURLs, 1);
      Set($CanonicalizeURLsInFeeds, 1);
      Set($WebBaseURL, '${cfg.baseUrl}');
      Set($LogoLinkURL, '${cfg.baseUrl}');

      # Web Interface (Home page)
      Set($DefaultSummaryRows, 50);

      # Web Interface (Ticket search)
      Set($DefaultSearchResultOrder, 'DESC'); # Display newer tickets first
      Set($SearchResultsAutoRedirect, 1); # Don't show result list when there is only one match
      Set(%FullTextSearch,
          Enable => 1,
          Indexed => 1,
          Column => 'ContentIndex',
          Table => 'AttachmentsIndex',
      );

      # Web Interface (Ticket options)
      Set($ShowMoreAboutPrivilegedUsers, 1);
      Set($MoreAboutRequestorGroupsLimit, undef);
      Set($HideUnsetFieldsOnDisplay, 1);

      # Web Interface (Articles)
      Set($ArticleOnTicketCreate, 0);

      # Web Interface (Message box properties)
      Set($MessageBoxRichText, 0);
      Set($MessageBoxIncludeSignatureOnComment, 0);

      # Web Interface (Transaction display)
      Set($MaxInlineBody, 0);
      Set($SuppressInlineTextFiles, 1);

      # Web Interface (Administrative interface)
      Set($ShowRTPortal, 0);
      Set($ShowEditSsytemConfig, 0);

      # Features (External storage)
      Set(%ExternalStorage,
          Type => 'Disk',
          Path => '/var/lib/rt/attachments',
      );
      Set($ExternalStorageCutoffSize, 0);

      # Features (Cryptography)
      Set(%Crypt, RejectOnMissingPrivateKey => 0, RejectOnBadData => 0, AllowEncryptDataInDB => 0);
      Set(%SMIME, Enable => 1, Keyring => '${pkgs.cacert}/etc/ssl/certs/');
      Set(%GnuPG, Enable => 1);
      Set(%GnuPGOptions,
        'keyserver' => 'hkp://keys.openpgp.org',
        'always-trust' => undef,
        'auto-key-locate' => 'keyserver',
        'keyserver-options' => 'auto-key-retrieve'
      );

      ${cfg.extraConfig}

      1;
    '';
    checkPhase = ''
      ${pkgs.perl}/bin/perl -c $out
    '';
  };
in {
  options.services.rt = with types; {

    enable = mkEnableOption "rt system";

    package = mkOption {
      description = "Package to use";
      default = pkgs.rt;
      defaultText = "pkgs.rt";
      type = package;
    };

    baseUrl = mkOption {
      description = "Base URL for web interface";
      default = "https://${cfg.domain}";
      defaultText = "https://\${cfg.domain}";
      type = str;
    };

    commentAddress = mkOption {
      description = "Default address from/to which comments are sent";
      type = str;
    };

    correspondAddress = mkOption {
      description = "Default address from/to which correspondences are sent";
      type = str;
    };

    dbPasswordFile = mkOption {
      description = "File containing the database password";
      type = str;
      default = "/etc/nixos/secret/rtpasswd";
      internal = true;
    };

    domain = mkOption {
      description = "Which domain RT is running on";
      type = str;
    };

    ownerEmail = mkOption {
      description = "Address of a human who manages RT. RT will send errors generated by the mail gateway to this address; it will also be displayed as the contact person on the RT's login page.";
      type = str;
    };

    port = mkOption {
      description = "Which port rt-server should listen on";
      type = port;
      default = 4201;
    };

    sendmailPath = mkOption {
      description = "Sendmail binary used to send... mail";
      default = "${pkgs.msmtp}/bin/sendmail";
      defaultText = "\${pkgs.msmtp}/bin/sendmail";
      type = str;
    };

    sendmailArguments = mkOption {
      description = "Arguments to call sendmailPath with";
      default = [ ];
      type = listOf (oneOf [ str path ]);
    };

    timeZone = mkOption {
      description = "Used to convert times entered by users into GMT, as they are stored in the database, and back again; users can override this";
      type = str;
      default = config.time.timeZone;
      defaultText = "[time.timeZone]";
    };

    rtName = mkOption {
      description = "Name of this RT instance";
      type = str;
    };

    organization = mkOption {
      description = "Name of the organization of this instance";
      type = str;
    };

    extraConfig = mkOption {
      description = "Verbatim config to append to generated on";
      type = lines;
      default = "";
    };
  };

  config = let
    components = [
      "rt-clean-sessions"
      "rt-email-dashboards"
      "rt-email-digest-daily"
      "rt-email-digest-weekly"
      "rt-externalize-attachments"
      "rt-fulltext-indexer"
      "rt-validator"
    ];

    mkTimer = name: {
      "${name}" = {
        wantedBy = [ "timers.target" ];
        timerConfig.Unit = [ "${name}.service" ];
      };
    };

    mkService = name: extraArgs: {
      "${name}" = {
        stopIfChanged = false;

        serviceConfig = {
          ExecStart = if extraArgs == ""
            then "${cfg.package}/bin/${name}"
            else mkForce "${cfg.package}/bin/${name} ${extraArgs}";
          User = "rt";
          Group = "rt";

          PrivateNetwork = false;
          MemoryDenyWriteExecute = false;

          ReadOnlyPaths = [ cfg.dbPasswordFile ];
        };

        environment = {
          RT_SITE_CONFIG = configFile;
        };

        path = with pkgs; [
          w3m
        ];
      };
    };
  in (mkIf cfg.enable {
    systemd.services = mkMerge ((map (c: mkService c "") components) ++ [
      (mkService "rt-server" "--port ${toString cfg.port} --server Starman")
      (mkService "rt-clean-sessions" "--skip-user")
      (mkService "rt-fulltext-indexer" "--limit 500000")
      (mkService "rt-validator" "--check")
      {
        rt-server = {
          serviceConfig = {
            StateDirectory = [ "rt/" "rt/attachments/" "rt/shredder/" "rt/smime/" ];
            RuntimeDirectory = [ "rt/" "rt/mason_data/" ];
            LogsDirectory = "rt/";
          };

          after = [ "postgresql.service" ];
          wantedBy = [ "multi-user.target" ];
        };
      }
      {
        rt-externalize-attachments = {
          serviceConfig.StateDirectory = "rt/attachments/";
        };
      }

      { rt-email-digest-daily.serviceConfig.ExecStart = mkForce "${cfg.package}/bin/rt-email-digest -m daily"; }
      { rt-email-digest-weekly.serviceConfig.ExecStart = mkForce "${cfg.package}/bin/rt-email-digest -m weekly"; }
    ]);

    systemd.timers = mkMerge ((map mkTimer components) ++ [
      {
        rt-clean-sessions.timerConfig.OnCalendar = "daily";
        rt-email-dashboards.timerConfig.OnCalendar = "hourly";
        rt-email-digest-daily.timerConfig.OnCalendar = "daily";
        rt-email-digest-weekly.timerConfig.OnCalendar = "weekly";
        rt-externalize-attachments.timerConfig.OnCalendar = "01:00";
        rt-fulltext-indexer.timerConfig.OnCalendar = "02:00";
        rt-validator.timerConfig.OnCalendar = "*-*-01 03:00:00";
      }
    ]);

    users.users.rt = {
      isSystemUser = true;
      group = "rt";
    };
    users.groups.rt = {};

    systemd.tmpfiles.rules = [
      "d /var/lib/secrets/rt 0500 rt rt -"
      "d /var/lib/rt/gpg 0700 rt rt -"
    ];
  });
}