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

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

  databaseOpts = { ... }: {
    options = {
      name = mkOption {
        type = types.str;
        description = "Database name.";
      };
      hostname = mkOption {
        type = types.str;
        description = "Hostname of the database server.";
      };
      user = mkOption {
        type = types.str;
        description = "Database username.";
      };
      password-file = mkOption {
        type = types.path;
        description = "File containing the database user's password.";
      };
    };
  };

  sshOpts = { ... }: with types; {
    options = {
      listen-ip = mkOption {
        type = str;
        description = "IP on which to listen for SSH connections.";
      };

      listen-port = mkOption {
        type = port;
        description = "Port on which to listen for SSH connections, on <listen-ip>.";
        default = 22;
      };
    };
  };

in {
  options.fudo.git = with types; {
    enable = mkEnableOption "Enable Fudo git web server.";

    hostname = mkOption {
      type = str;
      description = "Hostname at which this git server is accessible.";
      example = "git.fudo.org";
    };

    site-name = mkOption {
      type = str;
      description = "Name to use for the git server.";
      default = "Fudo Git";
    };

    database = mkOption {
      type = (submodule databaseOpts);
      description = "Gitea database options.";
    };

    repository-dir = mkOption {
      type = path;
      description = "Path at which to store repositories.";
      example = /srv/git/repo;
    };

    state-dir = mkOption {
      type = path;
      description = "Path at which to store server state.";
      example = /srv/git/state;
    };

    user = mkOption {
      type = with types; nullOr str;
      description = "System user as which to run.";
      default = "git";
    };

    local-port = mkOption {
      type = port;
      description = "Local port to which the Gitea server will bind. Not globally accessible.";
      default = 3543;
    };

    ssh = mkOption {
      type = nullOr (submodule sshOpts);
      description = "SSH listen configuration.";
      default = null;
    };
  };

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

    services = {
      gitea = {
        enable = true;
        appName = cfg.site-name;
        database = {
          createDatabase = true;
          host = cfg.database.hostname;
          name = cfg.database.name;
          user = cfg.database.user;
          passwordFile = cfg.database.password-file;
          type = "postgres";
        };
        domain = cfg.hostname;
        httpAddress = "127.0.0.1";
        httpPort = cfg.local-port;
        repositoryRoot = toString cfg.repository-dir;
        stateDir = toString cfg.state-dir;
        rootUrl = "https://${cfg.hostname}/";
        user = mkIf (cfg.user != null) cfg.user;
        extraConfig = mkIf (cfg.ssh != null) ''
          [server]
          START_SSH_SERVER = true
          SSH_DOMAIN = ${cfg.hostname}
          SSH_PORT = ${toString cfg.ssh.listen-port}
          SSH_LISTEN_PORT = ${toString cfg.ssh.listen-port}
          SSH_LISTEN_HOST = ${cfg.ssh.listen-ip}
        '';
      };

      nginx = {
        enable = true;

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

            locations."/" = {
              proxyPass = "http://127.0.0.1:${toString cfg.local-port}";

              extraConfig = ''
                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 $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
              '';
            };
          };
        };
      };
    };
  };
}