{ 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 =
      let domain-name = config.fudo.hosts.${config.instance.hostname}.domain;
      in config.fudo.domains.${domain-name}.admin-email;

    networking.firewall.allowedTCPPorts =
      mkIf (cfg.ssh != null) [ cfg.ssh.listen-port ];

    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;
        ssh = {
          enable = true;
          clonePort = cfg.ssh.listen-port;
        };
        settings = mkIf (cfg.ssh != null) {
          server = {
            SSH_DOMAIN = cfg.hostname;
            SSH_LISTEN_PORT = 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;
              '';
            };
          };
        };
      };
    };
  };
}