# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0

{ depot, lib, pkgs, config, ... }:
{
  imports = [
    ../lib/zfs.nix
    ../lib/bgp.nix
    ../lib/minotarproxy.nix
    ../lib/whitby-distributed.nix
    ../lib/macmini-distributed.nix
    ../lib/oracle-lon01-distributed.nix
    ../lib/quotes.bfob.gg.nix
    ../lib/coredns/default.nix
  ];

  boot.initrd = {
    availableKernelModules = [
      "xhci_pci"
      "ahci"
      "nvme"
      "usbhid"
      "usb_storage"
      "sd_mod"
      "sr_mod"
      "igb"
    ];
    network = {
      enable = true;
      ssh = {
        enable = true;
        hostKeys = ["/persist/etc/ssh/ssh_host_ed25519_key"];
        authorizedKeys = map builtins.readFile config.users.users.lukegb.openssh.authorizedKeys.keyFiles;
      };
      postCommands = ''
        echo "zfs load-key -a; killall zfs" >> /root/.profile
      '';
    };
  };
  boot.kernelParams = [
    "ip=185.198.188.29::185.198.188.28:255.255.255.254:clouvider-lon01:enp1s0f0:none"
  ];
  boot.kernelModules = [ "kvm-intel" ];

  powerManagement.cpuFreqGovernor = lib.mkDefault "performance";

  fileSystems = let
    zfs = device: {
      device = device;
      fsType = "zfs";
    };
  in {
    "/" = zfs "tank/local/root";
    "/nix" = zfs "tank/local/nix";
    "/persist" = zfs "tank/safe/persist";
    "/home" = zfs "tank/safe/home";

    "/boot1" = {
      device = "/dev/disk/by-partlabel/boota";
      fsType = "vfat";
    };
    "/boot2" = {
      device = "/dev/disk/by-partlabel/bootb";
      fsType = "vfat";
    };
  };

  nix.settings.max-jobs = lib.mkDefault 8;

  # Use GRUB, so we can have mirrored bootloaders.
  boot.loader.efi.canTouchEfiVariables = true;
  boot.loader.grub = {
    enable = true;
    zfsSupport = true;
    efiSupport = true;
    mirroredBoots = map (path: {
      inherit path;
      devices = ["nodev"];
      efiSysMountPoint = path;
    }) ["/boot1" "/boot2"];
  };

  # Networking!
  networking = {
    hostName = "clouvider-lon01";
    domain = "as205479.net";
    hostId = "29aaa324";

    nameservers = [
      "2001:4860:4860::8888"
      "2001:4860:4860::8844"
      "8.8.8.8"
      "8.8.4.4"
    ];
    defaultGateway = {
      address = "185.198.188.28";
      interface = "br-ext";
    };
    defaultGateway6 = {
      address = "2a0a:54c0:0:17::1";
      interface = "br-ext";
    };
    bridges.br-ext.interfaces = ["enp1s0f0"];
    interfaces.br-ext = {
      ipv4.addresses = [{ address = "185.198.188.29"; prefixLength = 31; }];
      ipv6.addresses = [{ address = "2a0a:54c0:0:17::2"; prefixLength = 126; }];
    };
    interfaces.lo = {
      ipv4.addresses = [{ address = "127.0.0.1"; prefixLength = 8; }];
      ipv6.addresses = [{ address = "::1"; prefixLength = 128; }];
    };
    firewall = {
      allowPing = true;
      allowedTCPPorts = [
        80 443  # HTTP/nginx
        1935    # RTMP/nginx
        6697    # znc
        34197   # factorio
      ];
      allowedUDPPorts = [
        34197   # factorio
      ];
      extraCommands = ''
        # Allow SSH on public interfaces.
        iptables -A INPUT -p tcp --dport 22 --dst 185.198.188.29 -j ACCEPT
        iptables -A INPUT -p tcp --dport 22 --dst ${config.my.ip.tailscale} -j ACCEPT 
        ip6tables -A INPUT -p tcp --dport 22 --dst 2a0a:54c0:0:17::2 -j ACCEPT
        ip6tables -A INPUT -p tcp --dport 22 --dst ${config.my.ip.tailscale6} -j ACCEPT 
      '';
    };
  };
  my.ip.tailscale = "100.79.173.25";
  my.ip.tailscale6 = "fd7a:115c:a1e0:ab12:4843:cd96:624f:ad19";
  my.coredns.bind = [ "br-ext" "tailscale0" "127.0.0.1" "::1" ];

  services.openssh.openFirewall = false;  # allowed by networking.firewall.extraCommands
  services.openssh.hostKeys = [
    {
      path = "/persist/etc/ssh/ssh_host_ed25519_key";
      type = "ed25519";
    }
    {
      path = "/persist/etc/ssh/ssh_host_rsa_key";
      type = "rsa";
      bits = 4096;
    }
  ];

  users.users = {
    lukegb.extraGroups = [ "bird2" ];
  };
  users.groups = {
    znc-acme = {
      members = [ "znc" "nginx" ];
    };
  };

  services.lukegbgp = let local = {
    asn = 205479;
  }; in {
    enable = true;
    config = {
      local = {
        routerID = "185.198.188.29";
      };
      export = {
        v4 = ["92.118.29.0/24"];
      };
      peering = {
        clouvider = {
          local = local // {
            v4 = "185.198.188.29";
            v6 = "2a0a:54c0:0:17::2";
          };
          remote = {
            asn = 62240;
            export_community = 3000;
            routers = [{
              v4 = "185.198.188.28";
              v6 = "2a0a:54c0:0:17::1";
            }];
          };
        };
      };
    };
  };

  services.znc = {
    enable = true;
    mutable = true;
    dataDir = "/persist/etc/znc";
    useLegacyConfig = false;
  };
  my.vault.acmeCertificates."znc.lukegb.com" = {
    hostnames = [ "znc.lukegb.com" "akiichiro.lukegb.com" ];
    group = "znc-acme";
  };
  services.nginx = {
    enable = true;
    package = pkgs.nginxMainline;
    additionalModules = with pkgs.nginxModules; [ rtmp ];
    appendConfig = ''
      rtmp_auto_push on;
      rtmp {
        server {
          listen 1935;
          chunk_size 4096;
          application live1 {
            allow publish 213.41.69.70;
            allow publish 37.26.186.120;
            allow publish 94.229.74.42;
            live on; record off;
          }
        }
      }
    '';
    virtualHosts = {
      "clouvider-lon01.as205479.net" = {
        default = true;
        listen = [
          {addr = "185.198.188.29"; port = 80; ssl = false;}
          {addr = "[2a0a:54c0:0:17::2]"; port = 80; ssl = false;}
        ];
        locations."/.well-known/acme-challenge" = {
          root = "/var/lib/acme/.challenges";
        };
        locations."/" = {
          return = "301 https://$host$request_uri";
        };
      };
    };
  };

  systemd.mounts = let
    bindMount' = dir: {
      unitConfig.RequiresMountsFor = dir;
      options = "bind";
      what = "/persist${dir}";
      where = dir;
    };
    bindMountSvc = dir: svc: (bindMount' dir) // {
      bindsTo = [svc];
      partOf = [svc];
    };
    bindMountSvcDynamic = dir: svc: (bindMount' "/var/lib/private/${dir}") // {
      requiredBy = [svc];
      before = [svc];
      wantedBy = ["multi-user.target"];
    };
    bindMount = dir: (bindMount' dir) // {
      wantedBy = ["multi-user.target"];
    };
  in [
    (bindMountSvcDynamic "factorio" "factorio.service")
  ];

  services.factorio = {
    inherit (depot.ops.secrets.factorio) username token;
    enable = true;
    package = pkgs.factorio-headless-experimental;
    saveName = "lukegb20220131-ws";
    game-name = "Briefcase Full of Bees";
    extraSettings = {
      admins = ["lukegb"];
      auto_pause = true;
      only_admins_can_pause_the_game = false;
      game_password = depot.ops.secrets.factorioServerPassword;
      non_blocking_saving = true;
      autosave_only_on_server = true;
      autosave_interval = 5;
      autosave_slots = 60;
    };
  };

  my.quotesdb.listen = [
    "185.198.188.29"
    "[2a0a:54c0:0:17::2]"
  ];

  environment.systemPackages = [
    depot.ops.factorio.multiworld
  ];

  boot.binfmt.emulatedSystems = [ "aarch64-linux" ];

  system.stateVersion = "23.11";
}