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

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

  inherit (lib.strings) concatStringsSep;

  webmail-user = "webmail-php";
  webmail-group = "webmail-php";

  base-data-path = "/var/rainloop";

  fastcgi-conf = builtins.toFile "fastcgi.conf" ''
    fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;

    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    fastcgi_param  REQUEST_SCHEME     $scheme;
    fastcgi_param  HTTPS              $https if_not_empty;

    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;

    # PHP only, required if PHP was built with --enable-force-cgi-redirect
    fastcgi_param  REDIRECT_STATUS    200;
  '';

  site-packages = mapAttrs (site: site-cfg:
    pkgs.rainloop-community.overrideAttrs (oldAttrs: {
      # Not sure how to correctly specify this arg...
      #dataPath = "${base-data-path}/${site}";

      # Overwriting, to correctly create data dir
      installPhase = ''
        mkdir $out
        cp -r rainloop/* $out
        rm -rf $out/data
        ln -s ${base-data-path}/${site} $out/data
        ln -s ${site-cfg.favicon} $out/favicon.ico
      '';
    })) cfg.sites;

  siteOpts = { site-host, ... }: {
    options = {
      title = mkOption {
        type = types.str;
        description = "Webmail site title";
        example = "My Webmail";
      };

      debug = mkOption {
        type = types.bool;
        description = "Turn debug logs on.";
        default = false;
      };

      mail-server = mkOption {
        type = types.str;
        description = "Mail server from which to send & recieve email.";
        default = "mail.fudo.org";
      };

      favicon = mkOption {
        type = types.str;
        description = "URL of the site favicon";
        example = "https://www.somepage.com/fav.ico";
      };

      messages-per-page = mkOption {
        type = types.int;
        description = "Default number of messages to show per page";
        default = 30;
      };

      max-upload-size = mkOption {
        type = types.int;
        description = "Size limit in MB for uploaded files";
        default = 30;
      };

      theme = mkOption {
        type = types.str;
        description = "Default theme to use for this webmail site.";
        default = "Default";
      };

      # Ideally, don't even allow admin logins, since they'll just add state that can be clobbered
      # admin-password = mkOption {
      #   type = types.str;
      #   description = "Password to use for the admin user";
      # };

      domain = mkOption {
        type = types.str;
        description = "Domain for which the server acts as webmail server";
      };

      edit-mode = mkOption {
        type = types.enum [ "Plain" "Html" "PlainForced" "HtmlForced" ];
        description = "Default text editing mode for email";
        default = "Html";
      };

      layout-mode = mkOption {
        type = types.enum [ "side" "bottom" ];
        description = "Layout mode to use for email preview.";
        default = "side";
      };

      enable-threading = mkOption {
        type = types.bool;
        description = "Whether to enable threading for email.";
        default = true;
      };

      enable-mobile = mkOption {
        type = types.bool;
        description = "Whether to enable a mobile site view.";
        default = true;
      };

      database = mkOption {
        type = with types; nullOr (submodule databaseOpts);
        description = "Database configuration for storing contact data.";
        example = {
          name = "my_db";
          host = "db.domain.com";
          user = "my_user";
          password-file = /path/to/some/file.pw;
        };
        default = null;
      };

      admin-email = mkOption {
        type = types.str;
        description = "Email of administrator of this site.";
        default = "admin@fudo.org";
      };
    };
  };

  databaseOpts = { ... }: {
    options = {
      type = mkOption {
        type = types.enum [ "pgsql" "mysql" ];
        description = "Driver to use when connecting to the database.";
        default = "pgsql";
      };

      hostname = mkOption {
        type = types.str;
        description = "Name of host running the database.";
        example = "my-db.domain.com";
      };

      port = mkOption {
        type = types.int;
        description = "Port on which the database server is listening.";
        default = 5432;
      };

      name = mkOption {
        type = types.str;
        description =
          "Name of the database containing contact info. <user> must have access.";
        default = "rainloop_contacts";
      };

      user = mkOption {
        type = types.str;
        description = "User as which to connect to the database.";
      };

      password-file = mkOption {
        type = types.str;
        description = "Password to use when connecting to the database.";
      };
    };
  };

in {
  options.fudo.webmail = {
    enable = mkEnableOption "Enable a RainLoop webmail server.";

    sites = mkOption {
      type = with types; (attrsOf (submodule siteOpts));
      description = "A map of webmail sites to site configurations.";
      example = {
        "webmail.domain.com" = {
          title = "My Awesome Webmail";
          layout-mode = "side";
          favicon = "/path/to/favicon.ico";
          admin-password = "shh-don't-tell";
        };
      };
    };
  };

  config = mkIf cfg.enable {
    users = {
      users = {
        ${webmail-user} = {
          isSystemUser = true;
          description = "Webmail PHP FPM user";
          group = webmail-group;
        };
      };
      groups = {
        ${webmail-group} = {
          members = [ webmail-user config.services.nginx.user ];
        };
      };
    };

    security.acme.certs = mapAttrs'
      (site: site-cfg: nameValuePair site { email = site-cfg.admin-email; })
      cfg.sites;

    services = {
      phpfpm = {

        pools.webmail = {
          settings = {
            "pm" = "dynamic";
            "pm.max_children" = 50;
            "pm.start_servers" = 5;
            "pm.min_spare_servers" = 1;
            "pm.max_spare_servers" = 8;
          };

          phpOptions = ''
            memory_limit = 500M
          '';

          # Not working....see chmod below
          user = webmail-user;
          group = webmail-group;
        };
      };

      nginx = {
        enable = true;

        virtualHosts = mapAttrs (site: site-cfg: {
          enableACME = true;
          forceSSL = true;

          root = "${site-packages.${site}}";

          locations = {
            "/" = { index = "index.php"; };

            "/data" = {
              extraConfig = ''
                deny all;
                return 403;
              '';
            };
          };

          extraConfig = ''
            location ~ \.php$ {
              expires -1;

              include ${fastcgi-conf};
              fastcgi_index index.php;
              fastcgi_pass unix:${config.services.phpfpm.pools.webmail.socket};
            }
          '';
        }) cfg.sites;
      };
    };

    systemd.services = {
      webmail-init = let
        link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg:
          let
            cfg-file = builtins.toFile "${site}-rainloop.cfg"
              (import ./include/rainloop.nix lib site site-cfg
                site-packages.${site}.version);
            domain-cfg = builtins.toFile "${site}-domain.cfg" ''
              imap_host = "${site-cfg.mail-server}"
              imap_port = 143
              imap_secure = "TLS"
              imap_short_login = On
              sieve_use = Off
              sieve_allow_raw = Off
              sieve_host = ""
              sieve_port = 4190
              sieve_secure = "None"
              smtp_host = "${site-cfg.mail-server}"
              smtp_port = 587
              smtp_secure = "TLS"
              smtp_short_login = On
              smtp_auth = On
              smtp_php_mail = Off
              white_list = ""
            '';
          in ''
            ${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/configs
            ${pkgs.coreutils}/bin/cp ${cfg-file} ${base-data-path}/${site}/_data_/_default_/configs/application.ini

            ${pkgs.coreutils}/bin/mkdir -p ${base-data-path}/${site}/_data_/_default_/domains/
            ${pkgs.coreutils}/bin/cp ${domain-cfg} ${base-data-path}/${site}/_data_/_default_/domains/${site-cfg.domain}.ini
          '') cfg.sites);
        scriptPkg = (pkgs.writeScriptBin "webmail-init.sh" ''
          #!${pkgs.bash}/bin/bash -e
          ${link-configs}
          ${pkgs.coreutils}/bin/chown -R ${webmail-user}:${webmail-group} ${base-data-path}
          ${pkgs.coreutils}/bin/chmod -R ug+w ${base-data-path}
        '');
      in {
        requiredBy = [ "nginx.service" ];
        description =
          "Initialize webmail service directories prior to starting nginx.";
        script = "${scriptPkg}/bin/webmail-init.sh";
      };

      phpfpm-webmail-socket-perm = {
        wantedBy = [ "multi-user.target" ];
        description =
          "Change ownership of the phpfpm socket for webmail once it's started.";
        requires = [ "phpfpm-webmail.service" ];
        after = [ "phpfpm.target" ];
        serviceConfig = {
          ExecStart = ''
            ${pkgs.coreutils}/bin/chown ${webmail-user}:${webmail-group} ${config.services.phpfpm.pools.webmail.socket}
          '';
        };
      };

      nginx = {
        requires =
          [ "webmail-init.service" "phpfpm-webmail-socket-perm.service" ];
      };
    };
  };
}