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

{ depot, lib, pkgs, rebuilder, config, ... }:
let
  inherit (depot.ops) secrets;
in {
  boot.initrd.availableKernelModules = [
    "sd_mod"
    "ahci"
    "usb_storage"
    "usbhid"
  ];
  boot.kernelParams = [ "mitigations=off" ];

  fileSystems = {
    "/" = {
      device = "/dev/disk/by-uuid/fc964ef6-e3d0-4472-bc0e-f96f977ebf11";
      fsType = "ext4";
    };
    "/boot" = {
      device = "/dev/disk/by-uuid/AB36-5BE4";
      fsType = "vfat";
    };
  };

  nix.maxJobs = lib.mkDefault 4;

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # Networking!
  networking = {
    hostName = "swann"; # Define your hostname.
    domain = "house.as205479.net";
    nameservers = ["8.8.8.8" "8.8.4.4"];
    useDHCP = false;
    interfaces = {
      ens-virginmedia = {
        useDHCP = true;
      };
      ens-general = {
        ipv4.addresses = [
          { address = "192.168.1.1"; prefixLength = 23; }
        ];
      };
    };
  };
  my.ip.tailscale = "100.102.224.95";
  services.udev.extraRules = ''
    ATTR{address}=="e4:3a:6e:16:07:62", NAME="ens-virginmedia"
    ATTR{address}=="e4:3a:6e:16:07:67", NAME="ens-general"
  '';
  boot.kernel.sysctl = {
    "net.ipv4.ip_forward" = "1";
    "net.ipv6.conf.default.forwarding" = "1";
    "net.ipv6.conf.all.forwarding" = "1";
  };
  networking.nat = {
    enable = true;
    externalInterface = "ens-virginmedia";
    internalInterfaces = ["ens-general"];
    forwardPorts = [
      { destination = "192.168.1.40:22"; proto = "tcp"; sourcePort = 10022; }
      { destination = "192.168.1.40:41641"; proto = "udp"; sourcePort = 41641; }
      { destination = "192.168.1.40:80"; proto = "tcp"; sourcePort = 80; }
      { destination = "192.168.1.40:443"; proto = "tcp"; sourcePort = 443; }

      # IPFS
      { destination = "192.168.1.40:4001"; proto = "tcp"; sourcePort = 4001; }
      { destination = "192.168.1.40:4001"; proto = "udp"; sourcePort = 4001; }
    ];
  };
  services.dhcpd4 = {
    enable = true;
    interfaces = ["ens-general"];
    authoritative = true;
    extraConfig = ''
      subnet 192.168.1.0 netmask 255.255.255.0 {
        option subnet-mask 255.255.255.0;
        option routers 192.168.1.1;
        option domain-name-servers 192.168.1.1;
        option domain-name "house.as205479.net";
        default-lease-time 600;
        max-lease-time 3600;

        range 192.168.1.100 192.168.1.200;
      }
    '';
    machines = [
      {
        hostName = "totoro";
        ethernetAddress = "40:8d:5c:1f:e8:68";
        ipAddress = "192.168.1.40";
      }
      {
        hostName = "totoro-pfsense";
        ethernetAddress = "52:54:00:cf:cd:94";
        ipAddress = "192.168.1.41";
      }
      {
        hostName = "kvm";
        ethernetAddress = "00:0d:5d:1b:14:ba";
        ipAddress = "192.168.1.50";
      }
    ];
  };
  networking.localCommands = ''
    tc qdisc del dev ens-virginmedia root || true
    tc qdisc add dev ens-virginmedia root cake bandwidth 20Mbit docsis nat dual-srchost
    
    ip link add name ifb-virginmedia type ifb || true
    tc qdisc del dev ens-virginmedia ingress || true
    tc qdisc add dev ens-virginmedia handle ffff: ingress
    tc qdisc del dev ifb-virginmedia root || true
    tc qdisc add dev ifb-virginmedia root cake bandwidth 450Mbit besteffort docsis nat wash dual-dsthost
    ip link set dev ifb-virginmedia up
    tc filter add dev ens-virginmedia parent ffff: matchall action mirred egress redirect dev ifb-virginmedia
  '';

  services.unifi = {
    enable = true;
    openPorts = false;
    unifiPackage = depot.pkgs.unifiHacked;
  };
  services.prometheus.exporters.unifi-poller = {
    enable = true;
    controllers = [{
      url = "https://localhost:8443";
      verify_ssl = false;
      user = "unifipoller";
      pass = pkgs.writeTextFile { name = "unifipoller-password"; text = "unifipoller"; };
    }];
  };

  networking.firewall = {
    interfaces.ens-general = {
      allowedTCPPorts = [
        8080 6789  # Unifi
        53  # DNS
      ];
      allowedUDPPorts = [
        3478 10001  # Unifi
        53  # DNS
      ];
    };
  };

  services.ddclient = {
    enable = false;
    protocol = "cloudflare";
    domains = ["home.lukegb.com"];
    zone = "lukegb.com";
    password = secrets.cloudflareCredentials.token;
    use = "if";
    extraConfig = ''
      if=ens-virginmedia
      daemon=0
    '';
  };
  systemd.services.ddclient.serviceConfig.ExecStart = let
    ddclient = pkgs.perlPackages.buildPerlPackage rec {
      pname = "ddclient";
      version = "3.9.1";
      src = pkgs.fetchFromGitHub {
        owner = "ddclient";
        repo = "ddclient";
        rev = "11a583b003920f8e15591813598b70061d1a4654";
        sha256 = "sha256:1xz09vkii3mc2jmfwx9is07i06iiryv51571vdnl4m5mdnvsmlwb";
      };
      outputs = [ "out" ];
      doCheck = false;
      buildInputs = with pkgs.perlPackages; [ IOSocketSSL DigestSHA1 DataValidateIP JSONPP ];
      nativeBuildInputs = with pkgs; [ autoreconfHook makeWrapper ];
      preConfigure = ''
        touch Makefile.PL
      '';
      postInstall = ''
        patchShebangs $out/bin/ddclient
        wrapProgram $out/bin/ddclient \
          --suffix PATH : ${lib.makeBinPath (with pkgs; [ pkgs.iproute ])} \
          --prefix PERL5LIB : $PERL5LIB
      '';
    };
    RuntimeDirectory = "ddclient";
  in lib.mkForce "${lib.getBin ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";

  environment.systemPackages = with pkgs; [];

  services.coredns = {
    enable = true;
    config = ''
      .:53 {
        bind 192.168.1.1 127.0.0.53
        acl {
          allow net 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.0/8
          block
        }
        loadbalance
        forward . tls://8.8.8.8 tls://8.8.4.4 {
          tls_servername dns.google
        }
        cache {
          success 4096
          denial 1024
          prefetch 512
        }
        prometheus :9153
        errors
        log
      }
    '';
  };
  my.prometheus.additionalExporterPorts.coredns = 9153;
  networking.resolvconf.extraConfig = ''
    name_servers='127.0.0.53'
  '';

  services.prometheus.exporters.smokeping = {
    enable = true;
    hosts = [
      "8.8.8.8"  # Google Public DNS
      "youtube.com" "ads.google.com" "google.com"

      "1.1.1.1"  # Cloudflare DNS

      "twitter.com"

      "store.steampowered.com"
      "api.steampowered.com"

      "prod.euw1.lol.riotgames.com"  # League of Legends EUW

      "eu.battle.net"
      "185.60.112.157" "185.60.112.158"  # Diablo 3/HotS/Hearthstone
      "185.60.114.159"  # Overwatch
    ];
  };

  system.stateVersion = "21.03";
}