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

with lib;
let cfg = config.fudo.chat;

in {
  options.fudo.chat = {
    enable = mkEnableOption "Enable chat server";

    hostname = mkOption {
      type = types.str;
      description = "Hostname at which this chat server is accessible.";
      example = "chat.mydomain.com";
    };

    site-name = mkOption {
      type = types.str;
      description = "The name of this chat server.";
      example = "My Fancy Chat Site";
    };

    smtp-server = mkOption {
      type = types.str;
      description = "SMTP server to use for sending notification emails.";
      example = "mail.my-site.com";
    };

    smtp-user = mkOption {
      type = types.str;
      description = "Username with which to connect to the SMTP server.";
    };

    smtp-password-file = mkOption {
      type = types.str;
      description =
        "Path to a file containing the password to use while connecting to the SMTP server.";
    };

    state-directory = mkOption {
      type = types.str;
      description = "Path at which to store server state data.";
      default = "/var/lib/mattermost";
    };

    database = mkOption {
      type = (types.submodule {
        options = {
          name = mkOption {
            type = types.str;
            description = "Database name.";
          };

          hostname = mkOption {
            type = types.str;
            description = "Database host.";
          };

          user = mkOption {
            type = types.str;
            description = "Database user.";
          };

          password-file = mkOption {
            type = types.str;
            description = "Path to file containing database password.";
          };
        };
      });
      description = "Database configuration.";
      example = {
        name = "my_database";
        hostname = "my.database.com";
        user = "db_user";
        password-file = /path/to/some/file.pw;
      };
    };
  };

  config = mkIf cfg.enable (let
    pkg = pkgs.mattermost;
    default-config = builtins.fromJSON (readFile "${pkg}/config/config.json");
    modified-config = recursiveUpdate default-config {
      ServiceSettings.SiteURL = "https://${cfg.hostname}";
      ServiceSettings.ListenAddress = "127.0.0.1:8065";
      TeamSettings.SiteName = cfg.site-name;
      EmailSettings = {
        RequireEmailVerification = true;
        SMTPServer = cfg.smtp-server;
        SMTPPort = 587;
        EnableSMTPAuth = true;
        SMTPUsername = cfg.smtp-user;
        SMTPPassword = (fileContents cfg.smtp-password-file);
        SendEmailNotifications = true;
        ConnectionSecurity = "STARTTLS";
        FeedbackEmail = "chat@fudo.org";
        FeedbackName = "Admin";
      };
      EnableEmailInvitations = true;
      SqlSettings.DriverName = "postgres";
      SqlSettings.DataSource = "postgres://${cfg.database.user}:${
          fileContents cfg.database.password-file
        }@${cfg.database.hostname}:5432/${cfg.database.name}";
    };
    mattermost-config-file =
      pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config);
    mattermost-user = "mattermost";
    mattermost-group = "mattermost";

  in {
    users = {
      users = {
        ${mattermost-user} = {
          isSystemUser = true;
          group = mattermost-group;
        };
      };

      groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; };
    };

    system.activationScripts.mattermost = ''
      mkdir -p ${cfg.state-directory}
    '';

    systemd.services.mattermost = {
      description = "Mattermost Chat Server";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];

      preStart = ''
        mkdir -p ${cfg.state-directory}/config
        cp ${mattermost-config-file} ${cfg.state-directory}/config/config.json
        ln -sf ${pkg}/bin ${cfg.state-directory}
        ln -sf ${pkg}/fonts ${cfg.state-directory}
        ln -sf ${pkg}/i18n ${cfg.state-directory}
        ln -sf ${pkg}/templates ${cfg.state-directory}
        cp -uRL ${pkg}/client ${cfg.state-directory}
        chown -R ${mattermost-user}:${mattermost-group} ${cfg.state-directory}
        chmod u+w -R ${cfg.state-directory}/client
        chmod o-rwx -R ${cfg.state-directory}
      '';

      serviceConfig = {
        PermissionsStartOnly = true;
        ExecStart = "${pkg}/bin/mattermost";
        WorkingDirectory = cfg.state-directory;
        Restart = "always";
        RestartSec = "10";
        LimitNOFILE = "49152";
        User = mattermost-user;
        Group = mattermost-group;
      };
    };

    security.acme.certs.${cfg.hostname}.email = config.fudo.common.admin-email;

    services.nginx = {
      enable = true;

      appendHttpConfig = ''
        proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off;
      '';

      virtualHosts = {
        "${cfg.hostname}" = {
          enableACME = true;
          forceSSL = true;

          locations."/" = {
            proxyPass = "http://127.0.0.1:8065";

            extraConfig = ''
              client_max_body_size 50M;
              proxy_set_header Connection "";
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-By $server_addr:$server_port;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
              proxy_set_header X-Frame-Options SAMEORIGIN;
              proxy_buffers 256 16k;
              proxy_buffer_size 16k;
              proxy_read_timeout 600s;
              proxy_cache mattermost_cache;
              proxy_cache_revalidate on;
              proxy_cache_min_uses 2;
              proxy_cache_use_stale timeout;
              proxy_cache_lock on;
              proxy_http_version 1.1;
            '';
          };

          locations."~ /api/v[0-9]+/(users/)?websocket$" = {
            proxyPass = "http://127.0.0.1:8065";

            extraConfig = ''
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection "upgrade";
              client_max_body_size 50M;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-By $server_addr:$server_port;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
              proxy_set_header X-Frame-Options SAMEORIGIN;
              proxy_buffers 256 16k;
              proxy_buffer_size 16k;
              client_body_timeout 60;
              send_timeout 300;
              lingering_timeout 5;
              proxy_connect_timeout 90;
              proxy_send_timeout 300;
              proxy_read_timeout 90s;
            '';
          };
        };
      };
    };
  });
}