{
  config,
  lib,
  pkgs,
  ...
}:
let
  inherit (lib)
    literalExpression
    mkIf
    mkOption
    types
    ;

  cfg = config.programs.lapce;

  options = {
    enable = lib.mkEnableOption "lapce";
    package = lib.mkPackageOption pkgs "lapce" { nullable = true; };
    channel = mkOption {
      type = types.enum [
        "stable"
        "nightly"
      ];
      default = "stable";
      description = ''
        Lapce channel to configure.
        Should correspond to the package channel.
        This is used to determine the correct configuration and data directories.
      '';
    };
    settings = mkOption {
      type = settingsFormat.type;
      default = { };
      description = ''
        Configuration written to {file}`$XDG_CONFIG_HOME/lapce/settings.toml`.
        See <https://github.com/lapce/lapce/blob/master/extra/schemas/settings.json> for schema.
      '';
      example = literalExpression ''
        {
          core = {
            custom-titlebar = false;
            color-theme = "Custom";
            icon-theme = "Material Icons";
          };
          editor = {
            font-family = "FiraCode Nerd Bold Font, monospace";
            font-size = 22;
            tab-width = 2;
            cursor-surrounding-lines = 4;
            render-whitespace = "all";
            bracket-pair-colorization = true;
            highlight-matching-brackets = true;
          };
          ui = {
            font-size = 20;
            open-editors-visible = false;
          };
          lapce-nix.lsp-path = "$\{pkgs.nil\}/bin/nil";
        }
      '';
    };
    plugins = mkOption {
      type = types.listOf (
        types.submodule {
          options = {
            author = mkOption {
              type = types.str;
              description = ''
                Author of the plugin.
              '';
            };
            name = mkOption {
              type = types.str;
              description = ''
                Name of the plugin.
              '';
            };
            version = mkOption {
              type = types.str;
              description = ''
                Version of the plugin.
              '';
            };
            hash = mkOption {
              type = types.str;
              description = ''
                Hash of the plugin tarball.
                To find the hash leave this empty, rebuild and copy the hash from the error message.
              '';
              default = "";
            };
          };
        }
      );
      default = [ ];
      description = ''
        Plugins to install.
      '';
      example = literalExpression ''
        [
          {
            author = "MrFoxPro";
            name = "lapce-nix";
            version = "0.0.1";
            hash = "sha256-...";
          }
          {
            author = "dzhou121";
            name = "lapce-rust";
            version = "0.3.1932";
            hash = "sha256-...";
          }
        ]
      '';
    };
    keymaps = mkOption {
      type = settingsFormat.type;
      default = [ ];
      description = ''
        Keymaps written to {file}`$XDG_CONFIG_HOME/lapce/keymaps.toml`.
        See <https://github.com/lapce/lapce/blob/master/defaults/keymaps-common.toml> for examples.
      '';
      example = literalExpression ''
        [
          {
            command = "open_log_file";
            key = "Ctrl+Shift+L";
          }
        ]
      '';
    };
  };

  settingsFormat = pkgs.formats.toml { };

  fetchPluginTarballFromRegistry =
    {
      author,
      name,
      version,
      hash,
    }:
    pkgs.stdenvNoCC.mkDerivation (
      let
        url = "https://plugins.lapce.dev/api/v1/plugins/${author}/${name}/${version}/download";
        file = "lapce-plugin-${author}-${name}-${version}.tar.zstd";
      in
      {
        name = file;
        nativeBuildInputs = [
          pkgs.curl
          pkgs.cacert
        ];
        dontUnpack = true;
        dontBuild = true;
        installPhase = ''
          runHook preInstall

          url="$(curl ${url})"
          curl -L "$url" -o "$out"

          runHook postInstall
        '';
        outputHashAlgo = "sha256";
        outputHashMode = "flat";
        outputHash = hash;
        inherit (lib) meta;
      }
    );
  pluginFromRegistry =
    {
      author,
      name,
      version,
      hash,
    }:
    pkgs.stdenvNoCC.mkDerivation {
      pname = "lapce-plugin-${author}-${name}";
      inherit version;
      src = fetchPluginTarballFromRegistry {
        inherit
          author
          name
          version
          hash
          ;
      };
      nativeBuildInputs = [ pkgs.zstd ];
      phases = [ "installPhase" ];
      installPhase = ''
        runHook preInstall

        mkdir -p $out
        tar -C $out -xvf $src

        runHook postInstall
      '';
    };
  pluginsFromRegistry =
    plugins:
    pkgs.linkFarm "lapce-plugins" (
      builtins.listToAttrs (
        builtins.map (
          {
            author,
            name,
            version,
            ...
          }@plugin:
          {
            name = "${author}-${name}-${version}";
            value = pluginFromRegistry plugin;
          }
        ) plugins
      )
    );
in
{
  meta.maintainers = [ lib.hm.maintainers.timon-schelling ];

  options.programs.lapce = options;

  config = mkIf cfg.enable {
    home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];

    xdg =
      let
        dir = "lapce-${cfg.channel}";
      in
      {
        configFile = {
          "${dir}/settings.toml".source = settingsFormat.generate "settings.toml" cfg.settings;
          "${dir}/keymaps.toml".source = settingsFormat.generate "keymaps.toml" { keymaps = cfg.keymaps; };
        };
        dataFile."${dir}/plugins".source = pluginsFromRegistry cfg.plugins;
      };
  };
}
