{ generateStickerpicker }:
{ config, pkgs, lib, ... }: let
  cfg = config.services.maunium-stickerpicker;
in {
  options.services.maunium-stickerpicker = let
    inherit (lib) mkEnableOption mkOption types mkIf literalExpression;
    json = pkgs.formats.json { };

    stickerType = types.submodule ({ config, ... }: {
      freeformType = json.type;
      options = {
        path = mkOption {
          type = with types; nullOr path;
          default = null;
          description =  ''
            Filesystem path to the sticker.
          '';
        };

        url = mkOption {
          type = with types; nullOr str;
          default = null;
          description =  ''
            URL to download the sticker.

            ::: {.note}
              If {option}`path` is set, this will not have any effect.
            :::

            ::: {.note}
              This will require the pack to set a {option}`hash`.
            :::
          '';
        };

        id = mkOption {
          type = with types; nullOr str;
          default = null;
          example = "my-cool-sticker";
          description =  ''
            A unique id for the sticker.

            ::: {.warning}
              Be careful to make these different across all stickerpacks.
            :::

            ::: {.note}
              This will be autogenerated if left empty.
            :::
          '';
        };

        title = mkOption {
          type = with types; nullOr str;
          default = config.id;
          example = "My cool sticker";
          description =  ''
            A human readable name for the sticker.
          '';
        };

        dontConvert = mkOption {
          type = types.bool;
          default = false;
          example = true;
          description =  ''
            Disable conversion for this sticker in particular,
            even though a conversion has been set for the entire pack.
          '';
        };

        outputType = mkOption {
          type = with types; nullOr str;
          default = null;
          example = "png";
          description =  ''
            Convert this sticker to another format.

            ::: {.note}
              This will override any pack-wide configuration
              set for the option with the same name.
            :::
          '';
        };

        extraConversionArgs = mkOption {
          type = with types; listOf str;
          default = [ ];
          description =  ''
            Extra args to pass to imagemagick when converting formats.

            ::: {.note}
              This will override any pack-wide configuration
              set for the option with the same name.
            :::
          '';
        };
      };
    });

    stickerPackType = types.submodule ({ config, ... }: {
      freeformType = json.type;
      options = {
        id = mkOption {
          type = types.str;
          example = "my-stickerpack";
          description =  ''
            A unique id of the stickerpack.
          '';
        };

        title = mkOption {
          type = types.str;
          default = config.id;
          defaultText = literalExpression "services.maunium-stickerpicker.instances.<name>.stickerPacks.*.id";
          example = "My Stickerpack";
          description =  ''
            A human readable name for the stickerpack.
          '';
        };

        stickers = mkOption {
          type = with types; let
            coerce = x: builtins.fromJSON (builtins.readFile x);
          in coercedTo path coerce (listOf stickerType);
          example = literalExpression ''
            [
              {
                url = "https://example.com/sticker.png";
              }
              {
                url = "https://example.com/sticker.webp";
                id = "my-cool-sticker";
                title = "My cool sticker";
                outputType = "png";
              }
              {
                path = ./sticker.jpg;
                id = "my-cool-sticker-2";
                title = "My cool sticker 2";
                outputType = "png";
              }
            ]
          '';
          description =  ''
            Json-like attrset or path to json file describing the stickerpack contents.
          '';
        };

        hash = mkOption {
          type = types.nullOr types.str;
          default = lib.fakeSha256;
          defaultText = literalExpression "lib.fakeSha256";
          description =  ''
            Hash of the stickerpack.

            This is only required if any of the stickers are missing
            a {option}`path`, but has specified an {option}`url`
          '';
        };

        titlePrefix = mkOption {
          type = types.nullOr types.str;
          example = "My Sticker Pack";
          default = config.id;
          defaultText = literalExpression "services.maunium-stickerpicker.instances.<name>.stickerPacks.*.stickers.*.id";
          description =  ''
            This will get passed into {option}`generateTitle` for every sticker
            that is missing a title.
          '';
        };

        generateTitle = mkOption {
          type = with types; functionTo (functionTo str);
          default = titlePrefix: n: "${titlePrefix} - Sticker ${toString n}";
          description =  ''
            This will generate a title for every sticker that is missing a title.
            The first argument will be the value of {option}`titlePrefix`.
          '';
        };

        outputType = mkOption {
          type = with types; nullOr str;
          default = null;
          example = "png";
          description =  ''
            Convert stickers in this pack to another format.

            ::: {.note}
              This will be overriden any sticker with where
              option with the same name is set.
            :::
          '';
        };

        extraConversionArgs = mkOption {
          type = with types; listOf str;
          default = [ ];
          description =  ''
            Extra args to pass to imagemagick when converting formats.

            ::: {.note}
              This will be overriden any sticker with where
              option with the same name is set.
            :::
          '';
        };
      };
    }) // {
      description =  "A stickerpack that should be fetched from a JSON file";
      check = e: builtins.isAttrs e && builtins.all (x: builtins.hasAttr x e) [
        "id"
        "src"
      ];
    };

  in {
    enable = mkEnableOption "maunium-stickerpicker";

    instances = mkOption {
      type = types.attrsOf (types.submodule ({ self, name, ... }: {
        options = {
          enableACME = mkOption {
            default = true;
            example = false;
            description =  ''
              Whether to enable ACME cert for nginx virtualhost

              ::: {.note}
                You might want to turn this off if you have a wildcard
                certificate, and override it in nginx using {option}`useACMECert`
              :::
            '';
            type = types.bool;
          };

          realMatrixDomain = mkOption {
            type = types.str;
            default = "matrix.org";
            description =  ''
              The domain of a (preferably your own) real matrix server.
            '';
          };

          stickerMatrixDomain = mkOption {
            type = types.str;
            default = "${name}.stickers.${self.realMatrixDomain}";
            defaultText = literalExpression ''
              mkIf
                config.services.matrix-synapse.enable
                "stickers.''${config.services.maunium-stickerpicker.''${name}.realMatrixDomain}"
              '';
            description =  ''
              The "fake" domain to use for the stickerpicker. This will be
              part of the mxc uris, and the stickers will be hosted on the
              correct paths, as if it was a real matrix server.
            '';
          };

          stickerPacks = mkOption {
            type = let
              coerce = lib.mapAttrsToList (name: pack: pack // { id = name; });
            in with types; coercedTo attrs coerce (listOf stickerPackType);
            default = [ ];
            description =  ''
              List of stickerpacks to load.
            '';
          };

        };
      }));
      default = { };
      description =  ''
        Instances of the stickerpicker to host.
      '';
    };
  };

  config = {
    services.nginx = lib.mkIf cfg.enable {
      enable = true;
      virtualHosts = lib.mkMerge (lib.flip lib.mapAttrsToList cfg.instances (name: instance: let
        wellKnown = pkgs.writeTextDir "server" ''
          {"m.server": "${instance.stickerMatrixDomain}:443"}
        '';

        stickerpicker = generateStickerpicker {
          inherit (instance) stickerMatrixDomain realMatrixDomain stickerPacks;
          instanceName = instance.stickerMatrixDomain;
        };

        stickerdir = lib.pipe stickerpicker.passthru.stickerPacks [
          (map (x: x.stickers))
          lib.flatten
          (map (sticker: lib.nameValuePair sticker.id sticker.path))
          builtins.listToAttrs
          (pkgs.linkFarm "maunium-stickerpicker-${name}-image-dir")
        ];
      in {
        ${instance.stickerMatrixDomain} = {
          enableACME = instance.enableACME;
          forceSSL = true;

          locations = {
            "/.well-known/matrix/".alias = "${wellKnown}/";
            "/_matrix/media/r0/download/${instance.stickerMatrixDomain}/".alias = "${stickerdir}/";
            "/stickerpicker/".alias = "${stickerpicker}/";
          };
        };
      }));
    };
  };
}
