{
  config,
  lib,
  inputs,
  ...
}:
let
  cfg = config.khscodes.infrastructure.hetzner-instance;
  fqdn = config.khscodes.networking.fqdn;
  provisioningUserData = config.khscodes.infrastructure.provisioning.instanceUserData;
  firewallTcpRules = lib.lists.map (p: {
    direction = "in";
    protocol = "tcp";
    port = p;
    source_ips = [
      "0.0.0.0/0"
      "::/0"
    ];
  }) config.networking.firewall.allowedTCPPorts;
  firewallUdpRules = lib.lists.map (p: {
    direction = "in";
    protocol = "udp";
    port = p;
    source_ips = [
      "0.0.0.0/0"
      "::/0"
    ];
  }) config.networking.firewall.allowedUDPPorts;
  firewallIcmpRules = lib.lists.optional config.networking.firewall.allowPing {
    direction = "in";
    protocol = "icmp";
    source_ips = [
      "0.0.0.0/0"
      "::/0"
    ];
    description = "ping";
  };
  firewallRules =
    firewallTcpRules
    ++ firewallUdpRules
    ++ firewallIcmpRules
    ++ cfg.extraFirewallRules
    ++ [
      {
        direction = "out";
        protocol = "tcp";
        port = 80;
        destination_ips = [
          "0.0.0.0/0"
          "::/0"
        ];
        description = "http";
      }
      {
        direction = "out";
        protocol = "tcp";
        port = 443;
        destination_ips = [
          "0.0.0.0/0"
          "::/0"
        ];
        description = "http";
      }
      {
        direction = "out";
        protocol = "udp";
        port = 443;
        destination_ips = [
          "0.0.0.0/0"
          "::/0"
        ];
        description = "quic";
      }
      {
        direction = "out";
        protocol = "udp";
        port = 53;
        destination_ips = [
          "0.0.0.0/0"
          "::/0"
        ];
        description = "dns";
      }
      {
        direction = "out";
        protocol = "tcp";
        port = 53;
        destination_ips = [
          "0.0.0.0/0"
          "::/0"
        ];
        description = "dns";
      }
    ];
  firewallEnable = config.networking.firewall.enable;
in
{
  options.khscodes.infrastructure.hetzner-instance = {
    enable = lib.mkEnableOption "enables generating a opentofu config";
    dnsName = lib.mkOption {
      type = lib.types.str;
      default = fqdn;
    };
    dnsAliases = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = lib.lists.unique (
        lib.lists.filter (alias: alias != cfg.dnsName) config.khscodes.networking.aliases
      );
    };
    bucket = {
      key = lib.mkOption {
        type = lib.types.str;
        description = "Key for use in the bucket";
        default = "${fqdn}.tfstate";
      };
    };
    datacenter = lib.mkOption {
      type = lib.types.str;
      description = "The Hetzner datacenter to create a server in";
      default = "hel1-dc2";
    };
    mapRdns = lib.mkOption {
      type = lib.types.bool;
      description = "Sets up RDNS for the server";
      default = false;
    };
    server_type = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      description = "The server type to create";
      default = null;
    };
    extraFirewallRules = lib.mkOption {
      type = lib.types.listOf lib.types.attrs;
      description = "Extra firewall rules added to the instance";
      default = [
        {
          direction = "out";
          protocol = "tcp";
          port = 80;
          destination_ips = [
            "0.0.0.0/0"
            "::/0"
          ];
          description = "http";
        }
        {
          direction = "out";
          protocol = "tcp";
          port = 443;
          destination_ips = [
            "0.0.0.0/0"
            "::/0"
          ];
          description = "https";
        }
        {
          direction = "out";
          protocol = "udp";
          port = 443;
          destination_ips = [
            "0.0.0.0/0"
            "::/0"
          ];
          description = "quic";
        }
        {
          direction = "out";
          protocol = "icmp";
          destination_ips = [
            "0.0.0.0/0"
            "::/0"
          ];
          description = "Ping";
        }
      ];
    };
  };
  config = lib.mkIf cfg.enable (
    let
      labels = {
        app = fqdn;
      };
      modules = [
        (
          { config, ... }:
          {
            imports = [
              inputs.self.terranixModules.cloudflare
              inputs.self.terranixModules.hcloud
              inputs.self.terranixModules.s3
            ];
            config = {
              khscodes.s3 = {
                enable = true;
                bucket.key = cfg.bucket.key;
              };
              khscodes.hcloud.data.ssh_key.khs = {
                name = "ca.kaareskovgaard.net";
              };
              khscodes.hcloud.enable = true;
              khscodes.hcloud.server.compute = {
                inherit (cfg) server_type datacenter;
                inherit labels;
                name = fqdn;
                initial_image = "debian-12";
                rdns = lib.mkIf cfg.mapRdns fqdn;
                ssh_keys = [ config.khscodes.hcloud.output.data.ssh_key.khs.id ];
                user_data = builtins.toJSON provisioningUserData;
              };
              khscodes.cloudflare = {
                enable = true;
                dns = {
                  enable = true;
                  aRecords = [
                    {
                      fqdn = cfg.dnsName;
                      content = config.khscodes.hcloud.output.server.compute.ipv4_address;
                    }
                  ];
                  aaaaRecords = [
                    {
                      fqdn = cfg.dnsName;
                      content = config.khscodes.hcloud.output.server.compute.ipv6_address;
                    }
                  ];
                  cnameRecords = lib.lists.map (domain: {
                    fqdn = domain;
                    content = cfg.dnsName;
                  }) cfg.dnsAliases;
                };
              };
              resource.hcloud_firewall.fw = lib.mkIf firewallEnable {
                inherit labels;
                name = fqdn;
                apply_to = {
                  server = config.khscodes.hcloud.output.server.compute.id;
                };
                rule = firewallRules;
              };
              output.ipv4_address = {
                value = config.khscodes.hcloud.output.server.compute.ipv4_address;
                sensitive = false;
              };

              output.ipv6_address = {
                value = config.khscodes.hcloud.output.server.compute.ipv6_address;
                sensitive = false;
              };
            };
          }
        )
      ];
    in
    {
      assertions = [
        {
          assertion = config.khscodes.networking.fqdn != null;
          message = "Must set config.khscodes.networking.fqdn when using opentofu";
        }
      ];
      khscodes.services.openssh = {
        enable = true;
        hostCertificate = {
          enable = true;
        };
      };
      khscodes.services.read-vault-auth-from-userdata = {
        url = "http://169.254.169.254/latest/user-data";
        doubleDecodeJsonData = true;
      };
      khscodes.infrastructure.provisioning.pre = {
        modules = modules;
      };
    }
  );
}
