let
  _pkgs = import <nixpkgs> {};
  _nixpkgs = _pkgs.fetchFromGitHub (_pkgs.lib.importJSON ./pkgs/nixpkgs.json);
in

{ pkgs ? import _nixpkgs {}
, package ? null
, maintainer ? null
, dont_prompt ? false
}:

# TODO: add assert statements

let

  pkgs-mozilla = import ./default.nix { inherit pkgs; };

  dont_prompt_str = if dont_prompt then "yes" else "no";

  packagesWith = cond: return: set:
    pkgs.lib.flatten
      (pkgs.lib.mapAttrsToList
        (name: pkg:
          let
            result = builtins.tryEval (
              if pkgs.lib.isDerivation pkg && cond name pkg
                then [(return name pkg)]
              else if pkg.recurseForDerivations or false || pkg.recurseForRelease or false
                then packagesWith cond return pkg
              else []
            );
          in
            if result.success then result.value
            else []
        )
        set
      );

  packagesWithUpdateScriptAndMaintainer = maintainer':
    let
      maintainer =
        if ! builtins.hasAttr maintainer' pkgs.lib.maintainers then
          builtins.throw "Maintainer with name `${maintainer'} does not exist in `lib/maintainers.nix`."
        else
          builtins.getAttr maintainer' pkgs.lib.maintainers;
    in
      packagesWith (name: pkg: builtins.hasAttr "updateScript" pkg &&
                                 (if builtins.hasAttr "maintainers" pkg.meta
                                   then (if builtins.isList pkg.meta.maintainers
                                           then builtins.elem maintainer pkg.meta.maintainers
                                           else maintainer == pkg.meta.maintainers
                                        )
                                   else false
                                 )
                   )
                   (name: pkg: pkg)
                   pkgs-mozilla;

  packageByName = name:
    let
        package = pkgs.lib.attrByPath (pkgs.lib.splitString "." name) null pkgs-mozilla;
    in
      if package == null then
        builtins.throw "Package with an attribute name `${name}` does not exists."
      else if ! builtins.hasAttr "updateScript" package then
        builtins.throw "Package with an attribute name `${name}` does have an `passthru.updateScript` defined."
      else
        package;

  packages =
    if package != null then
      [ (packageByName package) ]
    else if maintainer != null then
      packagesWithUpdateScriptAndMaintainer maintainer
    else
      builtins.throw "No arguments provided.\n\n${helpText}";

  helpText = ''
    Please run:

        % nix-shell maintainers/scripts/update.nix --argstr maintainer garbas

    to run all update scripts for all packages that lists \`garbas\` as a maintainer
    and have \`updateScript\` defined, or:

        % nix-shell maintainers/scripts/update.nix --argstr package garbas

    to run update script for specific package.
  '';

  runUpdateScript = package: ''
    echo -ne " - ${package.name}: UPDATING ..."\\r
    ${package.updateScript} &> ${(builtins.parseDrvName package.name).name}.log
    CODE=$?
    if [ "$CODE" != "0" ]; then
      echo " - ${package.name}: ERROR       "
      echo ""
      echo "--- SHOWING ERROR LOG FOR ${package.name} ----------------------"
      echo ""
      cat ${(builtins.parseDrvName package.name).name}.log
      echo ""
      echo "--- SHOWING ERROR LOG FOR ${package.name} ----------------------"
      exit $CODE
    else
      rm ${(builtins.parseDrvName package.name).name}.log
    fi
    echo " - ${package.name}: DONE.       "
  '';

in pkgs.stdenv.mkDerivation {
  name = "nixpkgs-mozilla-update-script";
  buildCommand = ''
    echo ""
    echo "----------------------------------------------------------------"
    echo ""
    echo "Not possible to update packages using \`nix-build\`"
    echo ""
    echo "${helpText}"
    echo "----------------------------------------------------------------"
    exit 1
  '';
  shellHook = ''
    echo ""
    echo "Going to be running update for following packages:"
    echo "${builtins.concatStringsSep "\n" (map (x: " - ${x.name}") packages)}"
    echo ""
    if [ "${dont_prompt_str}" = "no" ]; then
      read -n1 -r -p "Press space to continue..." confirm
    else
      confirm=""
    fi
    if [ "$confirm" = "" ]; then
      echo ""
      echo "Running update for:"
      ${builtins.concatStringsSep "\n" (map runUpdateScript packages)}
      echo ""
      echo "Packages updated!"
      exit 0
    else
      echo "Aborting!"
      exit 1
    fi
  '';
}
