{ stdenv
, lib
, sage-env
, blas
, lapack
, pkg-config
, three
, singular
, gap
, giac
, maxima
, pari
, gmp
, gfan
, python3
, eclib
, ntl
, ecm
, pythonEnv
}:

# lots of segfaults with (64 bit) blas
assert (!blas.isILP64) && (!lapack.isILP64);

# Wrapper that combined `sagelib` with `sage-env` to produce an actually
# executable sage. No tests are run yet and no documentation is built.

let
  nativeBuildInputs = [ pkg-config ];
  buildInputs = [
    pythonEnv # for patchShebangs
    blas lapack
    singular
    three
    giac
    gap
    pari
    gmp
    gfan
    maxima
    eclib
    ntl
    ecm
  ];

  # remove python prefix, replace "-" in the name by "_", apply patch_names
  # python3.8-some-pkg-1.0 -> some_pkg-1.0
  pkg_to_spkg_name = pkg: patch_names: let
    parts = lib.splitString "-" pkg.name;
    # remove python3.8-
    stripped_parts = if (builtins.head parts) == python3.libPrefix then builtins.tail parts else parts;
    version = lib.last stripped_parts;
    orig_pkgname = lib.init stripped_parts;
    pkgname = patch_names (lib.concatStringsSep "_" orig_pkgname);
  in pkgname + "-" + version;


  # return the names of all dependencies in the transitive closure
  transitiveClosure = dep:
  if dep == null then
    # propagatedBuildInputs might contain null
    # (although that might be considered a programming error in the derivation)
    []
  else
    [ dep ] ++ (
      if builtins.hasAttr "propagatedBuildInputs" dep then
        lib.unique (builtins.concatLists (map transitiveClosure dep.propagatedBuildInputs))
      else
      []
    );

  allInputs = lib.remove null (nativeBuildInputs ++ buildInputs ++ pythonEnv.extraLibs);
  transitiveDeps = lib.unique (builtins.concatLists (map transitiveClosure allInputs ));
  # fix differences between spkg and sage names
  # (could patch sage instead, but this is more lightweight and also works for packages depending on sage)
  patch_names = builtins.replaceStrings [
    "zope.interface"
    "node_three"
  ] [
    "zope-interface"
    "threejs"
  ];
  # spkg names (this_is_a_package-version) of all transitive deps
  input_names = map (dep: pkg_to_spkg_name dep patch_names) transitiveDeps;
in
stdenv.mkDerivation rec {
  version = src.version;
  pname = "sage-with-env";
  src = sage-env.lib.src;

  inherit nativeBuildInputs buildInputs;

  configurePhase = "#do nothing";

  buildPhase = ''
    mkdir installed
    for pkg in ${lib.concatStringsSep " " input_names}; do
      touch "installed/$pkg"
    done

    # threejs version is in format 0.<version>.minor, but sage currently still
    # relies on installed_packages for the online version of threejs to work
    # and expects the format r<version>. This is a hotfix for now.
    # upstream: https://trac.sagemath.org/ticket/26434
    rm "installed/threejs"*
    touch "installed/threejs-r${lib.versions.minor three.version}"
  '';

  installPhase = ''
    mkdir -p "$out/var/lib/sage"
    cp -r installed "$out/var/lib/sage"

    mkdir -p "$out/etc"
    # sage tests will try to create this file if it doesn't exist
    touch "$out/etc/sage-started.txt"

    mkdir -p "$out/build"

    # the scripts in src/bin will find the actual sage source files using environment variables set in `sage-env`
    cp -r src/bin "$out/bin"
    cp -r build/bin "$out/build/bin"

    # sage assumes the existence of sage-src-env-config.in means it's being executed in-tree. in this case, it
    # adds SAGE_SRC/bin to PATH, breaking our wrappers
    rm "$out/bin"/*.in "$out/build/bin"/*.in

    cp -f '${sage-env}/sage-env' "$out/bin/sage-env"
    substituteInPlace "$out/bin/sage-env" \
      --subst-var-by sage-local "$out"
  '';

  passthru = {
    env = sage-env;
  };
}
