{
  lib,
  helpers,
  config,
  pkgs,
  ...
}:
with lib; let
  cfg = config.plugins.coverage;

  keymapsDef = {
    coverage = {
      command = "";
      description = "Loads a coverage report and immediately displays the coverage signs.";
    };
    load = {
      command = "Load";
      description = "Loads a coverage report but does not display the coverage signs.";
    };
    show = {
      command = "Show";
      description = ''
        Shows the coverage signs.
        Must call `:Coverage` or `:CoverageLoad` first.
      '';
    };
    hide = {
      command = "Hide";
      description = ''
        Hides the coverage signs.
        Must call `:Coverage` or `:CoverageLoad` first.
      '';
    };
    toggle = {
      command = "Toggle";
      description = ''
        Toggles the coverage signs.
        Must call `:Coverage` or `:CoverageLoad` first.
      '';
    };
    clear = {
      command = "Clear";
      description = ''
        Unloads the cached coverage signs.
        `:Coverage` or `:CoverageLoad` must be called again to relad the data.
      '';
    };
    summary = {
      command = "Summary";
      description = "Displays a coverage summary report in a floating window.";
    };
  };
in {
  options.plugins.coverage =
    helpers.neovim-plugin.extraOptionsOptions
    // {
      enable = mkEnableOption "nvim-coverage";

      package = helpers.mkPackageOption "nvim-coverage" pkgs.vimPlugins.nvim-coverage;

      keymapsSilent = mkOption {
        type = types.bool;
        description = "Whether nvim-coverage keymaps should be silent";
        default = false;
      };

      keymaps =
        mapAttrs
        (
          optionName: properties:
            helpers.mkNullOrOption types.str properties.description
        )
        keymapsDef;

      autoReload = helpers.defaultNullOpts.mkBool false ''
        If true, the `coverage_file` for a language will be watched for changes after executing
        `:CoverageLoad` or `coverage.load()`.
        The file watcher will be stopped after executing `:CoverageClear` or `coverage.clear()`.
      '';

      autoReloadTimeoutMs = helpers.defaultNullOpts.mkInt 500 ''
        The number of milliseconds to wait before auto-reloading coverage after detecting a change.
      '';

      commands = helpers.defaultNullOpts.mkBool true "If true, create commands.";

      highlights = {
        covered =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{fg = "#B7F071";}''
          "Highlight group for covered signs.";

        uncovered =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{fg = "#F07178";}''
          "Highlight group for uncovered signs.";

        partial =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{fg = "#AA71F0";}''
          "Highlight group for partial coverage signs.";

        summaryBorder =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{link = "FloatBorder";}''
          "Border highlight group of the summary pop-up.";

        summaryNormal =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{link = "NormalFloat";}''
          "Normal text highlight group of the summary pop-up.";

        summaryCursorLine =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{link = "CursorLine";}''
          "Cursor line highlight group of the summary pop-up.";

        summaryHeader =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{ style = "bold,underline"; sp = "bg"; }''
          "Header text highlight group of the summary pop-up.";

        summaryPass =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{link = "CoverageCovered";}''
          "Pass text highlight group of the summary pop-up.";

        summaryFail =
          helpers.defaultNullOpts.mkNullable
          types.attrs
          ''{link = "CoverageUncovered";}''
          "Fail text highlight group of the summary pop-up.";
      };

      loadCoverageCb = helpers.defaultNullOpts.mkLuaFn "nil" ''
        A lua function that will be called when a coverage file is loaded.

        Example:
        ```
          function (ftype)
            vim.notify("Loaded " .. ftype .. " coverage")
          end
        ```
      '';

      signs =
        mapAttrs
        (
          optionName: {
            prettyName ? optionName,
            defaults,
          }: {
            hl =
              helpers.defaultNullOpts.mkStr
              defaults.hl
              "The highlight group used for ${prettyName} signs.";

            text =
              helpers.defaultNullOpts.mkStr
              defaults.text
              "The text used for ${prettyName} signs.";
          }
        )
        {
          covered = {
            defaults = {
              hl = "CoverageCovered";
              text = "▎";
            };
          };
          uncovered = {
            defaults = {
              hl = "CoverageUncovered";
              text = "▎";
            };
          };
          partial = {
            prettyName = "partial coverage";
            defaults = {
              hl = "CoveragePartial";
              text = "▎";
            };
          };
        };

      signGroup = helpers.defaultNullOpts.mkStr "coverage" ''
        Name of the sign group used when placing the signs.
        See `:h sign-group`.
      '';

      summary = {
        widthPercentage =
          helpers.defaultNullOpts.mkNullable
          (types.numbers.between 0.0 1.0)
          "0.70"
          "Width of the pop-up window.";

        heightPercentage =
          helpers.defaultNullOpts.mkNullable
          (types.numbers.between 0.0 1.0)
          "0.50"
          "Height of the pop-up window.";

        borders =
          mapAttrs
          (
            optionName: default:
              helpers.defaultNullOpts.mkStr default ""
          )
          {
            topleft = "╭";
            topright = "╮";
            top = "─";
            left = "│";
            right = "│";
            botleft = "╰";
            botright = "╯";
            bot = "─";
            highlight = "Normal:CoverageSummaryBorder";
          };

        minCoverage =
          helpers.defaultNullOpts.mkNullable
          (types.numbers.between 0 100)
          "80"
          ''
            Minimum coverage percentage.
            Values below this are highlighted with the fail group, values above are highlighted with
            the pass group.
          '';
      };

      lang = helpers.defaultNullOpts.mkNullable types.attrs "see upstream documentation" ''
        Each key corresponds with the `filetype` of the language and maps to an attrs of
        configuration values that differ.
        See plugin documentation for language specific options.

        Example:
        ```nix
          {
            python = {
              coverage_file = ".coverage";
              coverage_command = "coverage json --fail-under=0 -q -o -";
            };
            ruby = {
                coverage_file = "coverage/coverage.json";
            };
          }
        ```
      '';

      lcovFile =
        helpers.mkNullOrOption
        types.str
        "File that the plugin will try to read lcov coverage from.";
    };

  config = let
    setupOptions = with cfg;
      {
        auto_reload = autoReload;
        auto_reload_timeout_ms = autoReloadTimeoutMs;
        inherit commands;
        highlights = with highlights; {
          inherit covered uncovered partial;
          summary_border = summaryBorder;
          summary_normal = summaryNormal;
          summary_cursor_line = summaryCursorLine;
          summary_header = summaryHeader;
          summary_pass = summaryPass;
          summary_fail = summaryFail;
        };
        load_coverage_cb = loadCoverageCb;
        inherit signs;
        sign_group = signGroup;
        summary = with summary; {
          width_percentage = widthPercentage;
          height_percentage = heightPercentage;
          inherit borders;
          min_coverage = minCoverage;
        };
        inherit lang;
        lcov_file = lcovFile;
      }
      // cfg.extraOptions;
  in
    mkIf cfg.enable {
      extraPlugins = [cfg.package];

      extraConfigLua = ''
        require("coverage").setup(${helpers.toLuaObject setupOptions})
      '';

      keymaps =
        flatten
        (
          mapAttrsToList
          (
            optionName: properties: let
              key = cfg.keymaps.${optionName};
            in
              optional
              (key != null)
              {
                mode = "n";
                inherit key;
                action = ":Coverage${properties.command}<CR>";
                options.silent = cfg.keymapsSilent;
              }
          )
          keymapsDef
        );
    };
}