{config, lib, pkgs, ...}:
let
  cfg = config.cos.wireguard;
in
{
  options.cos.wireguard = {
    enable = lib.mkEnableOption "enable";
    interface = lib.mkOption {
      type = lib.types.str;
      default = "wg0";
    };
    clientPubOptionsMap = lib.mkOption {
      type = lib.types.submodule {
        options = lib.attrsets.genAttrs config.cos.knownHosts (host: lib.mkOption {
          type = lib.types.nullOr (lib.types.submodule {
            options = {
              clientNumber = lib.mkOption {
                type = lib.types.ints.u8;
                description = "appended to IP";
              };
              ip = lib.mkOption {
                type = lib.types.str;
                default = "${cfg.baseIP}.${builtins.toString cfg.clientPubOptionsMap.${host}.clientNumber}";
              };
              ipv6 = lib.mkOption {
                default = "${cfg.baseIPv6}${builtins.toString cfg.clientPubOptionsMap.${host}.clientNumber}";
              };
              port = lib.mkOption {
                type = lib.types.port;
                default = 51820;
              };
              publicKey = lib.mkOption {
                type = lib.types.str;
              };
            };
          });
          default = null;
        });
      };
    };
    baseIP = lib.mkOption {
      type = lib.types.str;
      default = "10.100.0";
    };
    baseIPv6 = lib.mkOption {
      type = lib.types.str;
      default = "fc10::";
    };
    clientInternalIP = lib.mkOption {
      type = lib.types.str;
      default = cfg.clientPubOptionsMap.${config.cos.hostName}.ip;
    };
    clientInternalIPv6 = lib.mkOption {
      type = lib.types.str;
      default = cfg.clientPubOptionsMap.${config.cos.hostName}.ipv6;
    };
    clientInternalPort = lib.mkOption {
      type = lib.types.port;
      default = cfg.clientPubOptionsMap.${config.cos.hostName}.port;
    };
    clientPublicKey = lib.mkOption {
      type = lib.types.str;
      default = cfg.clientPubOptionsMap.${config.cos.hostName}.publicKey;
    };
    privateKeyFile = lib.mkOption {
      type = lib.types.str;
    };
  };

  config = lib.mkIf cfg.enable {
    networking.firewall.interfaces.${cfg.interface}.allowedUDPPorts = [
      cfg.clientInternalPort
    ];

    environment.systemPackages = with pkgs; [
      wireguard-tools
    ];

    environment.etc."resolv.conf".text = ''
      nameserver 10.100.0.1
      nameserver 1.1.1.1
    '';

    networking.wireguard = {
      enable = true;
      interfaces = {
        ${cfg.interface} = {
          ips = [ "${cfg.clientInternalIP}/32" "${cfg.clientInternalIPv6}/128" ];
          listenPort = cfg.clientInternalPort;

          privateKeyFile = cfg.privateKeyFile;

          peers = [
            {
              publicKey = cfg.clientPubOptionsMap.loadedskypotato.publicKey;
              allowedIPs = [
                "${cfg.baseIP}.0/24"
                "${cfg.baseIPv6}/64"
              ];
              endpoint = "${config.cos.knownPublicIPs.loadedskypotato}:${builtins.toString cfg.clientPubOptionsMap.loadedskypotato.port}";
              persistentKeepalive = 25;
            }
          ];
        };
      };
    };
  };
}
