{ lib, stdenv, fetchurl, fetchFromGitLab, fetchFromGitHub, runCommand, writeText
# docs deps
, libxslt, docbook_xml_dtd_412, docbook_xml_dtd_43, docbook_xsl, xmlto
# runtime deps
, resholve, bash, coreutils, dbus, file, gawk, glib, gnugrep, gnused, jq, nettools, procmail, procps, which, xdg-user-dirs
, shared-mime-info
, perl, perlPackages
, mimiSupport ? false
, withXdgOpenUsePortalPatch ? true }:

let
  # A much better xdg-open
  mimisrc = fetchFromGitHub {
    owner = "march-linux";
    repo = "mimi";
    rev = "8e0070f17bcd3612ee83cb84e663e7c7fabcca3d";
    sha256 = "15gw2nyrqmdsdin8gzxihpn77grhk9l97jp7s7pr7sl4n9ya2rpj";
  };

  # Required by the common desktop detection code
  commonDeps = [ dbus coreutils gnugrep gnused ];
  # These are all faked because the current desktop is detected
  # based on their presence, so we want them to be missing by default.
  commonFakes = [
    "explorer.exe"
    "gnome-default-applications-properties"
    "kde-config"
    "xprop"
  ];

  # This is still required to work around the eval trickery some scripts do
  commonPrologue = "${writeText "xdg-utils-prologue" ''
    export PATH=$PATH:${lib.makeBinPath [ coreutils ]}
  ''}";

  solutions = [
    {
      scripts = [ "bin/xdg-desktop-icon" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ xdg-user-dirs ];
      execer = [
        "cannot:${xdg-user-dirs}/bin/xdg-user-dir"
      ];
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "gconftool-2"   # GNOME2
      ];
      keep."$KDE_SESSION_VERSION" = true;
      prologue = commonPrologue;
    }

    {
      scripts = [ "bin/xdg-desktop-menu" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ gawk ];
      fake.external = commonFakes;
      keep."$KDE_SESSION_VERSION" = true;
      prologue = commonPrologue;
    }

    {
      scripts = [ "bin/xdg-email" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ gawk glib.bin "${placeholder "out"}/bin" ];
      execer = [
        "cannot:${placeholder "out"}/bin/xdg-mime"
        "cannot:${placeholder "out"}/bin/xdg-open"
      ];
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "exo-open"           # XFCE
        "gconftool-2"        # GNOME
        "gio"                # GNOME (new)
        "gnome-open"         # GNOME (very old)
        "gvfs-open"          # GNOME (old)
        "qtxdg-mat"          # LXQT
        "xdg-email-hook.sh"  # user-defined hook that may be available ambiently
      ];
      fix."/bin/echo" = true;
      keep = {
        "$command" = true;
        "$kreadconfig" = true;
        "$THUNDERBIRD" = true;
        "$utf8" = true;
      };
    }

    {
      scripts = [ "bin/xdg-icon-resource" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps;
      fake.external = commonFakes;
      keep."$KDE_SESSION_VERSION" = true;
      prologue = commonPrologue;
    }

    {
      scripts = [ "bin/xdg-mime" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ file gawk ];
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "gio"                # GNOME (new)
        "gnomevfs-info"      # GNOME (very old)
        "gvfs-info"          # GNOME (old)
        "kde4-config"        # Plasma 4
        "kfile"              # KDE 3
        "kmimetypefinder"    # Plasma (generic)
        "kmimetypefinder5"   # Plasma 5
        "ktraderclient"      # KDE 3
        "ktradertest"        # KDE 3
        "mimetype"           # alternative tool for file, pulls in perl, avoid
        "qtpaths"            # Plasma
        "qtxdg-mat"          # LXQT
      ];
      fix."/usr/bin/file" = true;
      keep = {
        "$KDE_SESSION_VERSION" = true;
        "$KTRADER" = true;
      };
      prologue = "${writeText "xdg-mime-prologue" ''
        export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${shared-mime-info}/share"
        export PERL5LIB=${with perlPackages; makePerlPath [ FileMimeInfo ]}
        export PATH=$PATH:${lib.makeBinPath [ coreutils perlPackages.FileMimeInfo ]}
      ''}";
    }

    {
      scripts = [ "bin/xdg-open" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ nettools glib.bin "${placeholder "out"}/bin" ];
      execer = [
        "cannot:${placeholder "out"}/bin/xdg-mime"
      ];
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "cygstart"            # Cygwin
        "dde-open"            # Deepin
        "enlightenment_open"  # Enlightenment
        "exo-open"            # XFCE
        "gio"                 # GNOME (new)
        "gnome-open"          # GNOME (very old)
        "gvfs-open"           # GNOME (old)
        "kde-open"            # Plasma
        "kfmclient"           # KDE3
        "mate-open"           # MATE
        "mimeopen"            # alternative tool for file, pulls in perl, avoid
        "open"                # macOS
        "pcmanfm"             # LXDE
        "qtxdg-mat"           # LXQT
        "run-mailcap"         # generic
        "rundll32.exe"        # WSL
        "wslpath"             # WSL
      ];
      fix."$printf" = [ "printf" ];
      keep = {
        "env:$command" = true;
        "$browser" = true;
        "$KDE_SESSION_VERSION" = true;
      };
    }

    {
      scripts = [ "bin/xdg-screensaver" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ nettools perl procps ]
        # procmail's funky build system is currently broken in cross-build.
        # xdg-screensaver will gracefully degrade if it's not available.
        ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) procmail;
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "dcop"                      # KDE3
        "mate-screensaver-command"  # MATE
        "xautolock"                 # Xautolock
        "xscreensaver-command"      # Xscreensaver
        "xset"                      # generic-ish X
      ] ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "lockfile"; # procmail
      keep = {
        "$MV" = true;
        "$XPROP" = true;
        "$lockfile_command" = true;
      };
      execer = [
        "cannot:${perl}/bin/perl"
      ];
      prologue = "${writeText "xdg-screensaver-prologue" ''
        export PERL5LIB=${with perlPackages; makePerlPath [ NetDBus XMLTwig XMLParser X11Protocol ]}
        export PATH=$PATH:${coreutils}/bin
      ''}";
    }

    {
      scripts = [ "bin/xdg-settings" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ jq "${placeholder "out"}/bin" ];
      execer = [
        "cannot:${placeholder "out"}/bin/xdg-mime"
      ];
      # These are desktop-specific, so we don't want xdg-utils to be able to
      # call them when in a different setup.
      fake.external = commonFakes ++ [
        "gconftool-2"    # GNOME
        "kreadconfig"    # Plasma (generic)
        "kreadconfig5"   # Plasma 5
        "kreadconfig6"   # Plasma 6
        "ktradertest"    # KDE3
        "kwriteconfig"   # Plasma (generic)
        "kwriteconfig5"  # Plasma 5
        "kwriteconfig6"  # Plasma 6
        "qtxdg-mat"      # LXQT
      ];
      keep = {
        "$KDE_SESSION_VERSION" = true;
        # get_browser_$handler
        "$handler" = true;
      };
    }

    {
      scripts = [ "bin/xdg-terminal" ];
      interpreter = "${bash}/bin/bash";
      inputs = commonDeps ++ [ bash glib.bin which ];
      fake.external = commonFakes ++ [
        "gconftool-2"    # GNOME
        "exo-open"       # XFCE
        "lxterminal"     # LXQT
        "qterminal"      # LXQT
        "terminology"    # Englightenment
      ];
      keep = {
        "$command" = true;
        "$kreadconfig" = true;
        "$terminal_exec" = true;
      };
      prologue = commonPrologue;
    }
  ];
in

stdenv.mkDerivation (self: {
  pname = "xdg-utils";
  version = "1.2.1";

  src = fetchFromGitLab {
    domain = "gitlab.freedesktop.org";
    owner = "xdg";
    repo = "xdg-utils";
    rev = "v${self.version}";
    hash = "sha256-58ElbrVlk+13DUODSEHBPcDDt9H+Kuee8Rz9CIcoy0I=";
  };

  patches = lib.optionals withXdgOpenUsePortalPatch [
    # Allow forcing the use of XDG portals using NIXOS_XDG_OPEN_USE_PORTAL environment variable.
    # Upstream PR: https://github.com/freedesktop/xdg-utils/pull/12
    ./allow-forcing-portal-use.patch
    #  Enable build of xdg-terminal
    ./enable-xdg-terminal.patch
  ];

  # just needed when built from git
  nativeBuildInputs = [ libxslt docbook_xml_dtd_412 docbook_xml_dtd_43 docbook_xsl xmlto ];

  # explicitly provide a runtime shell so patchShebangs is consistent across build platforms
  buildInputs = [ bash ];

  postInstall = lib.optionalString mimiSupport ''
    cp ${mimisrc}/xdg-open $out/bin/xdg-open
  '';

  preFixup = lib.concatStringsSep "\n" (map (resholve.phraseSolution "xdg-utils-resholved") solutions);

  passthru.tests.xdg-mime = runCommand "xdg-mime-test" {
    nativeBuildInputs = [ self.finalPackage ];
    preferLocalBuild = true;
    xenias = lib.mapAttrsToList (hash: urls: fetchurl { inherit hash urls; }) {
      "sha256-SL95tM1AjOi7vDnCyT10s0tvQvc+ZSZBbkNOYXfbOy0=" = [
        "https://staging.cohostcdn.org/attachment/0f5d9832-0cda-4d07-b35f-832b287feb6c/kernelkisser.png"
        "https://static1.e621.net/data/0e/76/0e7672980d48e48c2d1373eb2505db5a.png"
      ];
      "sha256-Si9AtB7J9o6rK/oftv+saST77CNaeWomWU5ECfbRioM=" = [
        "https://static1.e621.net/data/25/3d/253dc77fbc60d7214bc60e4a647d1c32.jpg"
      ];
      "sha256-Z+onQRY5zlDWPp5/y4E6crLz3TaMCNipcxEEMSHuLkM=" = [
        "https://d.furaffinity.net/art/neotheta/1691409857/1691409857.neotheta_quickmakeanentry_by_neotheta-sig.png"
        "https://static1.e621.net/data/bf/e4/bfe43ba264ad68e5d8a101ecef69c03e.png"
      ];
    };
  } ''
    for x in $xenias; do
      ext=''${x##*.}
      type="$(xdg-mime query filetype $x)"
      [ $? -eq 0 ] && [ "$type" = "image/''${ext/jpg/jpeg}" ] || {
        echo "Incorrect MIME type '$type' for '$x'" >&2
        exit 1
      }
    done
    touch $out
  '';

  meta = with lib; {
    homepage = "https://www.freedesktop.org/wiki/Software/xdg-utils/";
    description = "Set of command line tools that assist applications with a variety of desktop integration tasks";
    license = if mimiSupport then licenses.gpl2Only else licenses.mit;
    maintainers = [ maintainers.eelco ];
    platforms = platforms.all;
  };
})
