{ config, lib, ... }:

let
  cfg = config.services.coturn;

  inherit (lib)
    mkDefault
    mkEnableOption
    mkIf
    mkOption
    optionalString
    optionals
    types
    ;
in
{
  options.services.coturn = {
    openFirewall = mkOption {
      type = types.bool;
      default = false;
      description = "Whether to open the firewall for coturn.";
    };
    debug = mkOption {
      type = types.bool;
      default = true;
      description = "Enable debug logging for coturn.";
    };
    sops = mkEnableOption "SOPS integration";
  };

  config = mkIf cfg.enable {
    services.coturn = {
      no-cli = mkDefault true;
      no-tcp-relay = mkDefault true;
      no-tls = mkDefault false;
      min-port = mkDefault 49000;
      max-port = mkDefault 50000;
      listening-port = mkDefault 3478;
      tls-listening-port = mkDefault 5349;

      use-auth-secret = true;
      static-auth-secret-file = mkIf cfg.sops config.sops.secrets."coturn/static-auth-secret".path;
      realm = mkDefault "turn.${config.networking.domain}";

      cert =
        mkIf (!cfg.no-tls && cfg.sops)
          "${config.security.acme.certs.${cfg.realm}.directory}/full.pem";
      pkey =
        mkIf (!cfg.no-tls && cfg.sops)
          "${config.security.acme.certs.${cfg.realm}.directory}/key.pem";

      extraConfig = ''
        # ban private IP ranges
        no-multicast-peers
        denied-peer-ip=0.0.0.0-0.255.255.255
        denied-peer-ip=10.0.0.0-10.255.255.255
        denied-peer-ip=100.64.0.0-100.127.255.255
        denied-peer-ip=127.0.0.0-127.255.255.255
        denied-peer-ip=169.254.0.0-169.254.255.255
        denied-peer-ip=172.16.0.0-172.31.255.255
        denied-peer-ip=192.0.0.0-192.0.0.255
        denied-peer-ip=192.0.2.0-192.0.2.255
        denied-peer-ip=192.88.99.0-192.88.99.255
        denied-peer-ip=192.168.0.0-192.168.255.255
        denied-peer-ip=198.18.0.0-198.19.255.255
        denied-peer-ip=198.51.100.0-198.51.100.255
        denied-peer-ip=203.0.113.0-203.0.113.255
        denied-peer-ip=240.0.0.0-255.255.255.255
        denied-peer-ip=::1
        denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
        denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
        denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
        denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
        denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
        denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
        denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
      ''
      + optionalString cfg.debug ''
        # debugging
        syslog
        verbose
      '';
    };

    networking.firewall = mkIf cfg.openFirewall {
      allowedUDPPortRanges = [
        {
          from = cfg.min-port;
          to = cfg.max-port;
        }
      ];
      allowedUDPPorts = [
        cfg.listening-port
        cfg.alt-listening-port
      ]
      ++ optionals (!cfg.no-tls) [
        cfg.tls-listening-port
        cfg.alt-tls-listening-port
      ];
      allowedTCPPorts = [
        cfg.listening-port
        cfg.alt-listening-port
      ]
      ++ optionals (!cfg.no-tls) [
        cfg.tls-listening-port
        cfg.alt-tls-listening-port
      ];
    };

    security.acme.certs = mkIf (!cfg.no-tls) {
      ${cfg.realm} = {
        postRun = "systemctl restart coturn.service";
        group = "turnserver";
      };
    };

    sops = mkIf cfg.sops {
      secrets."coturn/static-auth-secret" = {
        owner = "turnserver";
        group = "turnserver";
        mode = "0400";
      };
    };
  };
}
