{
  globals,
  config,
  lib,
  utils,
  ...
}: let
  cfg = config.media;
in {
  options.media = {
    enable = lib.mkEnableOption "media";
    jellyfin.enable = (lib.mkEnableOption "jellyfin") // {default = true;};
    deluge.enable = (lib.mkEnableOption "deluge") // {default = true;};
    jellyseerr.enable = (lib.mkEnableOption "jellyseerr") // {default = true;};
    radarr.enable = (lib.mkEnableOption "radarr") // {default = true;};
    prowlarr.enable = (lib.mkEnableOption "prowlarr") // {default = true;};
    sonarr.enable = (lib.mkEnableOption "sonarr") // {default = true;};
    bazarr.enable = (lib.mkEnableOption "bazarr") // {default = true;};
  };

  config = lib.mkIf cfg.enable {
    kubernetes.resources = {
      deployments = {
        jellyfin = lib.mkIf cfg.jellyfin.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "jellyfin";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "jellyfin";
              };

              spec = {
                containers.jellyfin = {
                  image = globals.images.jellyfin;
                  ports.web.containerPort = 8096;
                  imagePullPolicy = "IfNotPresent";

                  env.JELLYFIN_PublishedServerUrl.value = "https://media.kun.is";

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                    {
                      name = "media";
                      mountPath = "/media";
                    }
                    {
                      name = "cache";
                      mountPath = "/config/transcodes";
                    }
                    {
                      name = "music";
                      mountPath = "/media/music";
                    }
                  ];
                };

                volumes = {
                  config.persistentVolumeClaim.claimName = "jellyfin";
                  cache.persistentVolumeClaim.claimName = "jellyfin-cache";
                  music.persistentVolumeClaim.claimName = "music";

                  media.hostPath = {
                    path = "/mnt/longhorn/persistent/media";
                    type = "Directory";
                  };
                };

                securityContext = {
                  fsGroup = 0;
                  fsGroupChangePolicy = "OnRootMismatch";
                };

                affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
                  {
                    matchExpressions = [
                      {
                        key = "hasMedia";
                        operator = "In";
                        values = ["true"];
                      }
                    ];
                  }
                ];
              };
            };
          };
        };

        deluge = lib.mkIf cfg.deluge.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "deluge";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "deluge";
              };

              spec = {
                containers.deluge = {
                  image = utils.mkNixNGImage "deluge";
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    PUID.value = "1000";
                    PGID.value = "1000";
                    TZ.value = "Europe/Amsterdam";
                    DELUGE_LOGLEVEL.value = "info";
                  };

                  ports = {
                    web.containerPort = 8112;
                    bittorrent.containerPort = 31780;
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                    {
                      name = "media";
                      mountPath = "/media";
                    }
                  ];
                };

                volumes = {
                  config.persistentVolumeClaim.claimName = "deluge";

                  media.hostPath = {
                    path = "/mnt/longhorn/persistent/media";
                    type = "Directory";
                  };
                };

                securityContext = {
                  fsGroup = 51;
                  fsGroupChangePolicy = "OnRootMismatch";
                };

                affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
                  {
                    matchExpressions = [
                      {
                        key = "hasMedia";
                        operator = "In";
                        values = ["true"];
                      }
                    ];
                  }
                ];
              };
            };
          };
        };

        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "jellyseerr";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "jellyseerr";
              };

              spec = {
                volumes.config.persistentVolumeClaim.claimName = "jellyseerr";

                containers.jellyseerr = {
                  image = utils.mkNixNGImage "jellyseerr";
                  ports.web.containerPort = 5055;
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    LOG_LEVEL.value = "debug";
                    TZ.value = "Europe/Amsterdam";
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/app/config";
                    }
                  ];
                };

                securityContext = {
                  # TODO: don't hardcode this
                  fsGroup = 51;
                  fsGroupChangePolicy = "OnRootMismatch";
                };
              };
            };
          };
        };

        radarr = lib.mkIf cfg.radarr.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "radarr";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "radarr";
              };

              spec = {
                containers.radarr = {
                  image = utils.mkNixNGImage "radarr";
                  ports.web.containerPort = 7878;
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    PUID.value = "1000";
                    PGID.value = "1000";
                    TZ.value = "Europe/Amsterdam";
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                    {
                      name = "media";
                      mountPath = "/media";
                    }
                  ];
                };

                volumes = {
                  config.persistentVolumeClaim.claimName = "radarr";

                  media.hostPath = {
                    path = "/mnt/longhorn/persistent/media";
                    type = "Directory";
                  };
                };

                securityContext = {
                  fsGroup = 410;
                  fsGroupChangePolicy = "OnRootMismatch";
                };

                affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
                  {
                    matchExpressions = [
                      {
                        key = "hasMedia";
                        operator = "In";
                        values = ["true"];
                      }
                    ];
                  }
                ];
              };
            };
          };
        };

        prowlarr = lib.mkIf cfg.prowlarr.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "prowlarr";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "prowlarr";
              };

              spec = {
                volumes.config.persistentVolumeClaim.claimName = "prowlarr";

                containers.prowlarr = {
                  image = utils.mkNixNGImage "prowlarr";
                  ports.web.containerPort = 9696;
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    PUID.value = "1000";
                    PGID.value = "1000";
                    TZ.value = "Europe/Amsterdam";
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                  ];
                };

                securityContext = {
                  fsGroup = 413;
                  fsGroupChangePolicy = "OnRootMismatch";
                };
              };
            };
          };
        };

        sonarr = lib.mkIf cfg.sonarr.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "sonarr";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "sonarr";
              };

              spec = {
                containers.sonarr = {
                  image = utils.mkNixNGImage "sonarr";
                  ports.web.containerPort = 8989;
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    PUID.value = "1000";
                    PGID.value = "1000";
                    TZ.value = "Europe/Amsterdam";
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                    {
                      name = "media";
                      mountPath = "/media";
                    }
                  ];
                };

                volumes = {
                  config.persistentVolumeClaim.claimName = "sonarr";

                  media.hostPath = {
                    path = "/mnt/longhorn/persistent/media";
                    type = "Directory";
                  };
                };

                securityContext = {
                  fsGroup = 411;
                  fsGroupChangePolicy = "OnRootMismatch";
                };

                affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
                  {
                    matchExpressions = [
                      {
                        key = "hasMedia";
                        operator = "In";
                        values = ["true"];
                      }
                    ];
                  }
                ];
              };
            };
          };
        };

        bazarr = lib.mkIf cfg.bazarr.enable {
          spec = {
            selector.matchLabels = {
              app = "media";
              component = "bazarr";
            };

            strategy = {
              type = "RollingUpdate";

              rollingUpdate = {
                maxSurge = 0;
                maxUnavailable = 1;
              };
            };

            template = {
              metadata.labels = {
                app = "media";
                component = "bazarr";
              };

              spec = {
                containers.bazarr = {
                  image = utils.mkNixNGImage "bazarr";
                  ports.web.containerPort = 6767;
                  imagePullPolicy = "IfNotPresent";

                  env = {
                    PUID.value = "1000";
                    PGID.value = "1000";
                    TZ.value = "Europe/Amsterdam";
                  };

                  volumeMounts = [
                    {
                      name = "config";
                      mountPath = "/config";
                    }
                    {
                      name = "media";
                      mountPath = "/media";
                    }
                  ];
                };

                volumes = {
                  config.persistentVolumeClaim.claimName = "bazarr";

                  media.hostPath = {
                    path = "/mnt/longhorn/persistent/media";
                    type = "Directory";
                  };
                };

                securityContext = {
                  fsGroup = 412;
                  fsGroupChangePolicy = "OnRootMismatch";
                };

                affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
                  {
                    matchExpressions = [
                      {
                        key = "hasMedia";
                        operator = "In";
                        values = ["true"];
                      }
                    ];
                  }
                ];
              };
            };
          };
        };
      };

      services = {
        jellyfin = lib.mkIf cfg.jellyfin.enable {
          spec = {
            selector = {
              app = "media";
              component = "jellyfin";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };

        deluge = lib.mkIf cfg.deluge.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.transmissionIPv4;

            selector = {
              app = "media";
              component = "deluge";
            };

            ports = {
              bittorrent = {
                port = 31780;
                targetPort = "bittorrent";
              };

              web = {
                port = 80;
                targetPort = "web";
              };
            };
          };
        };

        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.jellyseerrIPv4;

            selector = {
              app = "media";
              component = "jellyseerr";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };

        radarr = lib.mkIf cfg.radarr.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.radarrIPv4;

            selector = {
              app = "media";
              component = "radarr";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };

        prowlarr = lib.mkIf cfg.prowlarr.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.prowlarrIPv4;

            selector = {
              app = "media";
              component = "prowlarr";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };

        sonarr = lib.mkIf cfg.sonarr.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.sonarrIPv4;

            selector = {
              app = "media";
              component = "sonarr";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };

        bazarr = lib.mkIf cfg.bazarr.enable {
          spec = {
            type = "LoadBalancer";
            loadBalancerIP = globals.bazarrIPv4;

            selector = {
              app = "media";
              component = "bazarr";
            };

            ports.web = {
              port = 80;
              targetPort = "web";
            };
          };
        };
      };

      persistentVolumeClaims.jellyfin-cache = lib.mkIf cfg.jellyfin.enable {
        spec = {
          accessModes = ["ReadWriteOnce"];
          resources.requests.storage = "20Gi";
        };
      };
    };

    lab = {
      ingresses = {
        jellyfin = lib.mkIf cfg.jellyfin.enable {
          host = "media.kun.is";

          service = {
            name = "jellyfin";
            portName = "web";
          };
        };

        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
          host = "jellyseerr.kun.is";
          entrypoint = "localsecure";

          service = {
            name = "jellyseerr";
            portName = "web";
          };
        };
      };

      tailscaleIngresses = {
        tailscale-jellyseerr = lib.mkIf cfg.jellyseerr.enable {
          host = "jellyseerr";
          service.name = "jellyseerr";
        };

        tailscale-radarr = lib.mkIf cfg.radarr.enable {
          host = "radarr";
          service.name = "radarr";
        };

        tailscale-sonarr = lib.mkIf cfg.sonarr.enable {
          host = "sonarr";
          service.name = "sonarr";
        };

        tailscale-bazarr = lib.mkIf cfg.bazarr.enable {
          host = "bazarr";
          service.name = "bazarr";
        };

        tailscale-prowlarr = lib.mkIf cfg.prowlarr.enable {
          host = "prowlarr";
          service.name = "prowlarr";
        };

        tailscale-deluge = lib.mkIf cfg.deluge.enable {
          host = "deluge";
          service.name = "deluge";
        };
      };

      longhorn.persistentVolumeClaim = {
        jellyfin = lib.mkIf cfg.jellyfin.enable {
          volumeName = "jellyfin";
          storage = "10Gi";
        };

        deluge = lib.mkIf cfg.deluge.enable {
          volumeName = "deluge";
          storage = "500Mi";
        };

        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
          volumeName = "jellyseerr";
          storage = "75Mi";
        };

        radarr = lib.mkIf cfg.radarr.enable {
          volumeName = "radarr";
          storage = "300Mi";
        };

        prowlarr = lib.mkIf cfg.prowlarr.enable {
          volumeName = "prowlarr";
          storage = "150Mi";
        };

        sonarr = lib.mkIf cfg.sonarr.enable {
          volumeName = "sonarr";
          storage = "250Mi";
        };

        bazarr = lib.mkIf cfg.bazarr.enable {
          volumeName = "bazarr";
          storage = "25Mi";
        };

        music = lib.mkIf cfg.jellyfin.enable {
          volumeName = "music";
          storage = "70Gi";
        };
      };
    };
  };
}