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

let
  inherit (builtins) toString;
  inherit (import ./modules/hardware/networks.nix) interfaces;

  # The head of the Borgbase hostname.
  hetzner-boxes-user = "u332477";
  hetzner-boxes-server = "${hetzner-boxes-user}.your-storagebox.de";
in
{
  imports = [
    # Since this will be rarely configured, make sure to import the appropriate
    # hardware modules depending on the hosting provider (and even just the
    # server).
    ./modules/hardware/hetzner-cloud-cx21.nix

    # The users for this host.
    (lib.getUser "nixos" "admin")
    (lib.getUser "nixos" "plover")

    # Hardened profile from nixpkgs.
    "${modulesPath}/profiles/hardened.nix"

    # The primary DNS server that is completely hidden.
    ./modules/services/bind.nix

    # The reverse proxy of choice.
    ./modules/services/nginx.nix

    # The single-sign on setup.
    ./modules/services/kanidm.nix
    ./modules/services/vouch-proxy.nix

    # The monitoring stack.
    ./modules/services/prometheus.nix
    ./modules/services/grafana.nix

    # The database of choice which is used by most self-managed services on
    # this server.
    ./modules/services/postgresql.nix

    # The application services for this server. They are modularized since
    # configuring it here will make it too big.
    ./modules/services/atuin.nix
    ./modules/services/gitea.nix
    ./modules/services/vaultwarden.nix
    ./modules/services/wireguard.nix
    ./modules/services/wezterm-mux-server.nix
  ];

  # Automatic format and partitioning.
  disko.devices = import ./disko.nix {
    disks = [ "/dev/sda" ];
  };

  networking = {
    nftables.enable = true;
    domain = "foodogsquared.one";
    firewall = {
      enable = true;
      allowedTCPPorts = [
        22 # Secure Shells.
      ];
    };
  };

  services.fail2ban = {
    ignoreIP = [
      # VPN clients.
      "${interfaces.wireguard0.IPv4.address}/13"
      "${interfaces.wireguard0.IPv6.address}/64"
    ];

    # We're going to be unforgiving with this one since we only have key
    # authentication and password authentication is disabled anyways.
    jails.sshd.settings = {
      enabled = true;
      maxretry = 1;
    };
  };

  sops.secrets = lib.getSecrets ./secrets/secrets.yaml {
    "ssh-key" = { };
    "lego/env" = { };

    "borg/repos/host/patterns/keys" = { };
    "borg/repos/host/password" = { };
    "borg/repos/services/password" = { };
    "borg/ssh-key" = { };
  };

  # All of the keys required to deploy the secrets.
  sops.age.keyFile = "/var/lib/sops-nix/key.txt";

  profiles.server = {
    enable = true;
    headless.enable = true;
    hardened-config.enable = true;
    cleanup.enable = true;
  };

  # DNS-related settings. We're settling by configuring the ACME setup with a
  # self-hosted DNS server.
  security.acme.defaults = {
    email = "admin+acme@foodogsquared.one";
    dnsProvider = "rfc2136";
    dnsResolver = "1.1.1.1";
    credentialsFile = config.sops.secrets."lego/env".path;
  };

  # Enable generating new DH params.
  security.dhparams.enable = true;

  # !!! The keys should be rotated at an interval here.
  services.openssh.hostKeys = [{
    path = config.sops.secrets."ssh-key".path;
    type = "ed25519";
  }];

  # Of course, what is a server without a backup? A professionally-handled
  # production system. However, we're not professionals so we do have backups.
  services.borgbackup.jobs =
    let
      jobCommonSettings = { patternFiles ? [ ], patterns ? [ ], paths ? [ ], repo, passCommand }: {
        inherit paths repo;
        compression = "zstd,11";
        dateFormat = "+%F-%H-%M-%S-%z";
        doInit = true;
        encryption = {
          inherit passCommand;
          mode = "repokey-blake2";
        };
        extraCreateArgs =
          let
            args = lib.flatten [
              (builtins.map
                (patternFile: "--patterns-from ${lib.escapeShellArg patternFile}")
                patternFiles)
              (builtins.map
                (pattern: "--pattern ${lib.escapeShellArg pattern}")
                patterns)
            ];
          in
          lib.concatStringsSep " " args;
        extraInitArgs = "--make-parent-dirs";
        persistentTimer = true;
        preHook = ''
          extraCreateArgs="$extraCreateArgs --stats"
        '';
        prune.keep = {
          weekly = 4;
          monthly = 12;
          yearly = 6;
        };
        startAt = "monthly";
        environment.BORG_RSH = "ssh -i ${config.sops.secrets."borg/ssh-key".path}";
      };

      borgRepo = path: "ssh://${hetzner-boxes-user}@${hetzner-boxes-server}:23/./borg/plover/${path}";
    in
    {
      # Backup for host-specific files. They don't change much so it is
      # acceptable for it to be backed up monthly.
      host-backup = jobCommonSettings {
        patternFiles = [
          config.sops.secrets."borg/repos/host/patterns/keys".path
        ];
        repo = borgRepo "host";
        passCommand = "cat ${config.sops.secrets."borg/repos/host/password".path}";
      };

      # Backups for various services.
      services-backup = jobCommonSettings
        {
          paths = [
            # ACME accounts and TLS certificates
            "/var/lib/acme"
          ];
          repo = borgRepo "services";
          passCommand = "cat ${config.sops.secrets."borg/repos/services/password".path}";
        } // { startAt = "daily"; };
    };

  programs.ssh.extraConfig = ''
    Host ${hetzner-boxes-server}
     IdentityFile ${config.sops.secrets."borg/ssh-key".path}
  '';

  system.stateVersion = "23.11";
}