{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.programs.ghostty;

  keyValueSettings = {
    listsAsDuplicateKeys = true;
    mkKeyValue = lib.generators.mkKeyValueDefault { } " = ";
  };
  keyValue = pkgs.formats.keyValue keyValueSettings;
in
{
  meta.maintainers = with lib.maintainers; [
    HeitorAugustoLN
    khaneliman
  ];

  options.programs.ghostty =
    let
      mkShellIntegrationOption =
        option:
        option
        // {
          description = ''
            ${option.description}

            This ensures that shell integration works in more scenarios, such as
            switching shells within Ghostty. But it is not needed to have shell
            integration.

            See
            <https://ghostty.org/docs/features/shell-integration#manual-shell-integration-setup>
            for more information.
          '';
        };
    in
    {
      enable = lib.mkEnableOption "Ghostty";

      package = lib.mkPackageOption pkgs "ghostty" {
        nullable = true;
        extraDescription = "Set programs.ghostty.package to null on platforms where ghostty is not available or marked broken";
      };

      settings = lib.mkOption {
        inherit (keyValue) type;
        default = { };
        example = lib.literalExpression ''
          {
            theme = "catppuccin-mocha";
            font-size = 10;
            keybind = [
              "ctrl+h=goto_split:left"
              "ctrl+l=goto_split:right"
            ];
          }
        '';
        description = ''
          Configuration written to {file}`$XDG_CONFIG_HOME/ghostty/config`.

          See <https://ghostty.org/docs/config/reference> for more information.
        '';
      };

      themes = lib.mkOption {
        type = lib.types.attrsOf keyValue.type;
        default = { };
        example = {
          catppuccin-mocha = {
            palette = [
              "0=#45475a"
              "1=#f38ba8"
              "2=#a6e3a1"
              "3=#f9e2af"
              "4=#89b4fa"
              "5=#f5c2e7"
              "6=#94e2d5"
              "7=#bac2de"
              "8=#585b70"
              "9=#f38ba8"
              "10=#a6e3a1"
              "11=#f9e2af"
              "12=#89b4fa"
              "13=#f5c2e7"
              "14=#94e2d5"
              "15=#a6adc8"
            ];
            background = "1e1e2e";
            foreground = "cdd6f4";
            cursor-color = "f5e0dc";
            selection-background = "353749";
            selection-foreground = "cdd6f4";
          };
        };
        description = ''
          Custom themes written to {file}`$XDG_CONFIG_HOME/ghostty/themes`.

          See <https://ghostty.org/docs/features/theme#authoring-a-custom-theme> for more information.
        '';
      };

      clearDefaultKeybinds = lib.mkEnableOption "" // {
        description = "Whether to clear default keybinds.";
      };

      installVimSyntax = lib.mkEnableOption "installation of Ghostty configuration syntax for Vim";

      installBatSyntax = lib.mkEnableOption "installation of Ghostty configuration syntax for bat" // {
        default = cfg.package != null;
        defaultText = lib.literalMD "`true` if programs.ghostty.package is not null";
      };

      enableBashIntegration = mkShellIntegrationOption (
        lib.hm.shell.mkBashIntegrationOption { inherit config; }
      );

      enableFishIntegration = mkShellIntegrationOption (
        lib.hm.shell.mkFishIntegrationOption { inherit config; }
      );

      enableZshIntegration = mkShellIntegrationOption (
        lib.hm.shell.mkZshIntegrationOption { inherit config; }
      );
    };

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

        programs.ghostty.settings = lib.mkIf cfg.clearDefaultKeybinds {
          keybind = lib.mkBefore [ "clear" ];
        };

        # MacOS also supports XDG configuration directory, so we use it for both
        # Linux and macOS to reduce complexity
        xdg.configFile =
          let
            validate =
              file:
              lib.mkIf (cfg.package != null)
                "${lib.getExe cfg.package} +validate-config --config-file=${config.xdg.configHome}/ghostty/${file}";
          in
          lib.mkMerge [
            {
              "ghostty/config" = lib.mkIf (cfg.settings != { }) {
                source = keyValue.generate "ghostty-config" cfg.settings;
                onChange = validate "config";
              };
            }

            (lib.mkIf (cfg.themes != { }) (
              lib.mapAttrs' (name: value: {
                name = "ghostty/themes/${name}";
                value = {
                  source = keyValue.generate "ghostty-${name}-theme" value;
                  onChange = validate "themes/${name}";
                };
              }) cfg.themes
            ))
          ];
      }

      (lib.mkIf cfg.installVimSyntax {
        assertions = [
          {
            assertion = cfg.installVimSyntax -> cfg.package != null;
            message = "programs.ghostty.installVimSyntax cannot be enabled when programs.ghostty.package is null";
          }
        ];
        programs.vim.plugins = lib.optionals (cfg.package != null) [ cfg.package.vim ];
      })

      (lib.mkIf cfg.installBatSyntax {
        assertions = [
          {
            assertion = cfg.installBatSyntax -> cfg.package != null;
            message = "programs.ghostty.installBatSyntax cannot be enabled when programs.ghostty.package is null";
          }
        ];
        programs.bat = lib.mkIf (cfg.package != null) {
          syntaxes.ghostty = {
            src = cfg.package;
            file =
              if pkgs.stdenv.hostPlatform.isDarwin then
                "Applications/Ghostty.app/Contents/Resources/bat/syntaxes/ghostty.sublime-syntax"
              else
                "share/bat/syntaxes/ghostty.sublime-syntax";
          };
          config.map-syntax = [ "${config.xdg.configHome}/ghostty/config:Ghostty Config" ];
        };
      })

      (lib.mkIf cfg.enableBashIntegration {
        # Make order 101 to be placed exactly after bash completions, as Ghostty
        # documentation suggests sourcing the script as soon as possible
        programs.bash.initExtra = lib.mkOrder 101 ''
          if [[ -n "''${GHOSTTY_RESOURCES_DIR}" ]]; then
            builtin source "''${GHOSTTY_RESOURCES_DIR}/shell-integration/bash/ghostty.bash"
          fi
        '';
      })

      (lib.mkIf cfg.enableFishIntegration {
        programs.fish.interactiveShellInit = ''
          if set -q GHOSTTY_RESOURCES_DIR
            source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish"
          end
        '';
      })

      (lib.mkIf cfg.enableZshIntegration {
        programs.zsh.initContent = ''
          if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
            source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
          fi
        '';
      })
    ]
  );
}
