{ config, is2305, lib, pkgs, ... }:

let
  #    _____ _______ ____  _____
  #   / ____|__   __/ __ \|  __ \
  #  | (___    | | | |  | | |__) |
  #   \___ \   | | | |  | |  ___/
  #   ____) |  | | | |__| | |
  #  |_____/   |_|  \____/|_|
  #
  # errors such as:
  # mod.zeroconf-publish: error id:47 seq:349 res:-2 (No such file or directory): enum params id:16 (Spa:Enum:ParamId:ProcessLatency) failed
  # are harmless and can be ignored. You most likely want to restart your local avahi-daemon: sudo systemctl restart avahi-daemon
  pipewireCfg = contextExec: let
    pactl = "${pkgs.pulseaudio}/bin/pactl";
  in {
    "context.exec" = contextExec ++ [
      # should be loaded by "server.address" but that is either to late or razy on 23.05
      {
        "path" = pactl;
        "args" = "load-module module-native-protocol-tcp";
      } {
        "path" = pactl;
        "args" = "load-module module-zeroconf-publish";
      }
    ];
    "pulse.properties" = {
      "auth-ip-acl" = [
        "127.0.0.0/8"
        "::1/128"
        "fd23:42:c3d2:500::/56"
        "172.22.99.0/24"
        "172.20.72.0/21"
        "2a00:8180:2c00:200::/56"
        "2a0f:5382:acab:1400::/56"
      ];
      "server.address" = [
        "unix:native"
        "tcp:4713"
      ];
    };
  };

  cfg = config.c3d2.audioServer;
in
{
  options.c3d2.audioServer = {
    enable = lib.mkEnableOption "PulseAudio and Bluetooth sinks";

    ledfx = lib.mkEnableOption "LedFX service";
  };

  config = lib.mkIf cfg.enable {
    boot.kernelPackages = lib.mkOverride 900 pkgs.linuxPackages-rt_latest;

    environment = {
      etc = lib.optionalAttrs is2305 {
        "pipewire/pipewire.conf.d/audio-server.conf".text = builtins.toJSON (pipewireCfg [ ]);
      };
      systemPackages = with pkgs; [
        mpd
        mpv
        ncmpcpp
        ncpamixer
        pulseaudio # required for pactl
        somafm-cli
      ];
    };

    hardware = {
      bluetooth.settings = {
        Policy.AutoEnable = true;
        General.DiscoverableTimeout = 0;
      };

      pulseaudio = {
        enable = !config.services.pipewire.pulse.enable;
        systemWide = true;
        tcp = {
          enable = true;
          anonymousClients.allowedIpRanges = [
            "127.0.0.0/8"
            "::1/128"
            "fd23:42:c3d2:500::/56"
            "172.22.99.0/24"
            "172.20.72.0/21"
            "2a00:8180:2c00:200::/56"
            "2a0f:5382:acab:1400::/56"
          ];
        };
        zeroconf.publish.enable = true;
        package = (pkgs.pulseaudio.override {
          bluetoothSupport = true;
          advancedBluetoothCodecs = true;
          zeroconfSupport = true;
        }).overrideAttrs (_: {
          # one test times out
          doCheck = false;
        });
      };
    };

    networking.firewall = {
      allowedTCPPorts = lib.optional cfg.ledfx 80 ++ [
        4713 # pulseaudio/pipewire network sync
      ];
      allowedUDPPorts = [
        5353 # mdns
      ];
    };

    nixpkgs.overlays = [
      (final: prev: {
        ledfx = prev.ledfx.overrideAttrs ({ postPatch ? "", ... }: {
          postPatch = postPatch + ''
            substituteInPlace setup.py \
              --replace '"pystray>=0.17",' ""
          '';
        });

        python3 = prev.python3.override {
          packageOverrides = python-final: python-prev:
          (lib.optionalAttrs config.environment.noXlibs {
            # remove x11 dependencies from pkgs.ledfx
            pystray = null;
          } // {
            # avoid dependency on x11 libraries
            samplerate = python-prev.samplerate.overrideAttrs (_: {
              dontUseSetuptoolsCheck = true;
            });
          });
        };
        python3Packages = final.python3.pkgs;
      })
    ];

    security = {
      polkit.extraConfig = /* javascript */ ''
        // https://www.reddit.com/r/voidlinux/comments/o74i76/comment/h2z9u11/?utm_source=reddit&utm_medium=web2x&context=3
        polkit.addRule(function(action, subject) {
          if (action.id == "org.freedesktop.RealtimeKit1.acquire-high-priority"
            || action.id == "org.freedesktop.RealtimeKit1.acquire-real-time") {
            return polkit.Result.YES;
          }
        });

        // broader alternative if the above ever breaks
        // polkit.addRule(function(action, subject) {
        //   if (subject.isInGroup("rtkit")) {
        //     if (action.id.indexOf("org.freedesktop.RealtimeKit1.") == 0) {
        //       return polkit.Result.YES;
        //     }
        //   }
        // });
      '';
      rtkit.enable = true;
    };

    services.pipewire = {
      enable = true;
      alsa.enable = lib.mkIf cfg.ledfx true; # required for ledfx
      config = lib.mkIf (!is2305) {
        pipewire-pulse =
          let
            default-pipewire-pulse = lib.importJSON (pkgs.path + "/nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json");
          in
          default-pipewire-pulse // (pipewireCfg default-pipewire-pulse."context.exec");
      };
      pulse.enable = true;
    };

    # tell Avahi to publish services like Pipewire/PulseAudio
    services.avahi = {
      enable = true;
      nssmdns = true;
      publish = {
        enable = true;
        addresses = true;
        userServices = true;
      };
    };

    sound.enable = true;

    # TODO: change to users.k-ot.lingering when updating to 23.11
    system.activationScripts.enableLingering = lib.optionalString config.services.pipewire.pulse.enable (''
      rm -r /var/lib/systemd/linger
      mkdir /var/lib/systemd/linger
    '' + lib.optionalString config.c3d2.k-ot.enable ''
      touch /var/lib/systemd/linger/k-ot
    '');

    systemd = {
      services = {
        bluetooth-agent = lib.mkIf config.hardware.bluetooth.enable {
          description = "Allow anyone to pair via Bluetooth";
          wantedBy = [ "multi-user.target" ];
          requires = [ "bluetooth.target" ];
          after = [ "bluetooth.service" ];
          serviceConfig = {
            Type = "simple";
            ExecStart = "${pkgs.bluez-tools}/bin/bt-agent -c NoInputNoOutput";
            Restart = "on-failure";
            RestartSec = 60;
          };
        };

        mpv-pause = {
          script = ''
            echo '{ "command": ["set_property", "pause", true] }' | ${pkgs.socat}/bin/socat - /tmp/mpvsocket
          '';
          serviceConfig = {
            Type = "oneshot";
            User = "root";
          };
        };
      };

      timers.mpv-pause = {
        wantedBy = [ "timers.target" ];
        timerConfig = {
          Unit = "mpv-pause.service";
          OnCalendar = "*-*-* 05:30:00";
        };
      };

      user.services = {
        ledfx = lib.mkIf cfg.ledfx {
          after = [ "pipewire.target" ];
          wantedBy = [ "default.target" ];
          serviceConfig = {
            ExecStart = lib.getExe pkgs.ledfx;
          };
        };
        pipewire-pulse.enable = true;
      };
    };

    users = {
      groups.pulse-access = { };
      users.k-ot = lib.mkIf config.c3d2.k-ot.enable {
        extraGroups = [
          "pipewire"
          "pulse-access" # required for system wide pulseaudio
          "rtkit"
        ];
        packages = with pkgs; [
          (yt-dlp.override { withAlias = true; })
        ];
      };
    };
  };
}
