{ depot
, fetchFromGitLab
, pkgs
, mkYarnPackage
, fetchYarnDeps
, lib
, stdenv
, makeWrapper
, python312
, nodejs_22
, ossOnly ? true
}:

let
  python3 = python312;
  nodejs = nodejs_22;
  version = "1.29.1";
  suffix = lib.optionalString ossOnly "-oss";
  src' = fetchFromGitLab {
    owner = "baserow";
    repo = "baserow";
    rev = version;
    hash = "sha256-EdSrcs2+jiURpf3Nrv5FJzQkkJ2mcA+UY8ceSkeLOvU=";
  };
  src = if ossOnly then pkgs.runCommand "${src'.name}${suffix}" {} ''
    cp -R ${src'} $out
    chmod -R u+w $out
    rm -rf $out/premium
    rm -rf $out/enterprise

    sed -i -e '/baserow_premium/d' -e '/baserow_enterprise/d' $out/backend/src/baserow/config/settings/base.py
  '' else src';

  inherit (depot.third_party) poetry2nix;
  poetry2nixOverrides = poetry2nix.defaultPoetryOverrides.overrideOverlay (self: super: let
    addBuildInputs = f: buildInputs: f.overridePythonAttrs (old: {
      buildInputs = (old.buildInputs or []) ++ buildInputs;
    });
  in {
    #kombu = let
    #  kombuVersion = "5.2.4";
    #in assert lib.assertMsg (super.kombu.version == kombuVersion) "kombu (${super.kombu.version}) is different version to what I expected (${kombuVersion}); maybe remove the override?"; super.kombu.overridePythonAttrs (old: {
    #  buildInputs = (old.buildInputs or []) ++ [ self.setuptools ];
    #  postPatch = ''
    #    ${old.postPatch or ""}
    #    substituteInPlace requirements/test.txt --replace "pytz>dev" "pytz"
    #  '';
    #});
    opentelemetry-instrumentation-aiohttp = addBuildInputs super.opentelemetry-instrumentation-aiohttp [ self.hatchling ];
    opentelemetry-instrumentation-aiohttp-client = addBuildInputs super.opentelemetry-instrumentation-aiohttp-client [ self.hatchling ];
    opentelemetry-instrumentation-botocore = addBuildInputs super.opentelemetry-instrumentation-botocore [ self.hatchling ];
    opentelemetry-instrumentation-celery = addBuildInputs super.opentelemetry-instrumentation-celery [ self.hatchling ];
    opentelemetry-instrumentation-dbapi = addBuildInputs super.opentelemetry-instrumentation-dbapi [ self.hatchling ];
    opentelemetry-instrumentation-django = addBuildInputs super.opentelemetry-instrumentation-django [ self.hatchling ];
    opentelemetry-instrumentation-grpc = addBuildInputs super.opentelemetry-instrumentation-grpc [ self.hatchling ];
    opentelemetry-instrumentation-logging = addBuildInputs super.opentelemetry-instrumentation-logging [ self.hatchling ];
    opentelemetry-instrumentation-psycopg2 = addBuildInputs super.opentelemetry-instrumentation-psycopg2 [ self.hatchling ];
    opentelemetry-instrumentation-redis = addBuildInputs super.opentelemetry-instrumentation-redis [ self.hatchling ];
    opentelemetry-instrumentation-requests = addBuildInputs super.opentelemetry-instrumentation-requests [ self.hatchling ];
    opentelemetry-instrumentation-wsgi = addBuildInputs super.opentelemetry-instrumentation-wsgi [ self.hatchling ];
    opentelemetry-propagator-aws-xray = addBuildInputs super.opentelemetry-propagator-aws-xray [ self.setuptools ];
    django-health-check = super.django-health-check.overridePythonAttrs (old: {
      buildInputs = (old.buildInputs or []) ++ [
        self.sphinx
        self.setuptools-scm
      ];
      postPatch = ''
        sed -i '/pytest-runner/d' setup.cfg
      '';
    });
    pysaml2 = addBuildInputs super.pysaml2 [ self.poetry-core ];
    jira2markdown = addBuildInputs super.jira2markdown [ self.poetry-core ];
    mistralai = addBuildInputs super.mistralai [ self.poetry-core ];
    pystemmer = addBuildInputs super.pystemmer [ self.cython ];
    pytest-runner = null;
    tokenizers = null;
    uvloop = super.uvloop.overridePythonAttrs (old: {
      nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
        self.pythonRelaxDepsHook
        self.setuptools
      ];
      pythonRemoveDeps = [ "pytest-runner" ];
    });
    orjson = super.orjson.override { preferWheel = true; };
    jiter = super.jiter.override { preferWheel = true; };
    numpy = python3.pkgs.numpy;
    django-cachalot = addBuildInputs super.django-cachalot [ self.setuptools ];
    celery-singleton = super.celery-singleton.overridePythonAttrs (old: {
      postPatch = ''
        substituteInPlace pyproject.toml \
          --replace-fail "poetry.masonry.api" "poetry.core.masonry.api"
      '';
      buildInputs = (old.buildInputs or []) ++ [ self.poetry-core ];
    });
    prosemirror = super.prosemirror.overridePythonAttrs (old: {
      buildInputs = (old.buildInputs or []) ++ [ self.poetry-core ];
      nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
        pkgs.unzip
      ];
    });
    anthropic = addBuildInputs super.anthropic [ self.hatch-fancy-pypi-readme ];
  });

  mkBackendSrc = { type, fromDir, pyproject, poetrylock, extra ? "" }: pkgs.runCommand "baserow-${type}-src" {
    inherit src pyproject poetrylock fromDir;
  } ''
    cp -r $src/$fromDir $out
    chmod -R +w $out

    cp $pyproject $out/pyproject.toml
    cp $poetrylock $out/poetry.lock
    cp $src/README.md $out
    ${extra}
  '';
  mkBackendApp = {
    type, fromDir, pyproject, poetrylock, srcExtra ? "", postInstall ? ""
  }: let
    src = mkBackendSrc { inherit type fromDir pyproject poetrylock; extra = srcExtra; };
  in poetry2nix.mkPoetryApplication {
    projectDir = src;
    inherit pyproject poetrylock postInstall;
    overrides = poetry2nixOverrides;
    python = python3;
    passthru.src = src;
  };

  templates = pkgs.runCommand "baserow-templates" {
    inherit src;
  } ''
    cp -r "$src/backend/templates" $out
  '';
  backendApp = mkBackendApp {
    type = "backend";
    fromDir = "backend";
    pyproject = ./backend/pyproject.toml;
    poetrylock = ./backend/poetry.lock;
    srcExtra = ''
      substituteInPlace "$out/src/baserow/config/settings/base.py" \
        --replace 'APPLICATION_TEMPLATES_DIR = os.path.join(BASE_DIR, "../../../templates")' "APPLICATION_TEMPLATES_DIR = '${templates}'"
    '';
    postInstall = ''
      install -m 0755 ${./backend/gunicorn.py} $out/bin/baserow-gunicorn
      install -m 0755 ${./backend/celery.py} $out/bin/baserow-celery
    '';
  };
  premiumBackendApp = mkBackendApp {
    type = "premium-backend";
    fromDir = "premium/backend";
    pyproject = ./premium-backend/pyproject.toml;
    poetrylock = ./premium-backend/poetry.lock;
  };
  enterpriseBackendApp = mkBackendApp {
    type = "enterprise-backend";
    fromDir = "enterprise/backend";
    pyproject = ./enterprise-backend/pyproject.toml;
    poetrylock = ./enterprise-backend/poetry.lock;
  };
  backendEnv = backendApp.python.buildEnv.override (old: {
    extraLibs = [ backendApp ] ++ lib.optionals (!ossOnly) [ premiumBackendApp enterpriseBackendApp ];
  });
in
{
  inherit src;

  web-frontend = mkYarnPackage {
    name = "baserow${suffix}-web-frontend";
    inherit src nodejs;

    sourceRoot = "source${lib.optionalString ossOnly "-oss"}/web-frontend";

    # upgraded nan to work with our newer node
    packageJSON = ./web-frontend/package.json;
    yarnLock = ./web-frontend/yarn.lock;

    offlineCache = fetchYarnDeps {
      yarnLock = ./web-frontend/yarn.lock;
      hash = "sha256-AILUgSCvqBtl5sEA5/k1ZCV0WhZMeJUlZL/frDbaWPo=";
    };

    nativeBuildInputs = [ makeWrapper ];

    env.BASEROW_OSS_ONLY = ossOnly;
    env.NUXT_TELEMETRY_DISABLED = true;

    pkgConfig = {
      node-sass = {
        buildInputs = with pkgs; [ python3 python3.pkgs.distutils libsass pkg-config ];
        postInstall = ''
          mkdir -p $HOME/.node-gyp/${nodejs.version}
          echo 9 > $HOME/.node-gyp/${nodejs.version}/installVersion
          ln -sfv ${nodejs}/include $HOME/.node-gyp/${nodejs.version}
          export npm_config_nodedir=${nodejs}

          LIBSASS_EXT=auto yarn --offline run build
          rm build/config.gypi
        '';
      };
    };

    postPatch = ''
      substituteInPlace package.json \
        --replace-fail '"node": ' '"_node_ignored_": '
    '';

    # Have to override configurePhase since we have a monorepo structure.
    configurePhase = ''
      runHook preConfigure

      cp -r $node_modules node_modules
      chmod -R +w node_modules

      runHook postConfigure
    '';

    buildPhase = ''
      runHook preBuild

      export HOME=$(mktemp -d)
      yarn --offline build

      runHook postBuild
    '';

    doDist = false;

    installPhase = ''
      runHook preInstall

      outpath="$out/share/baserow"
      mkdir -p "$outpath"
      cp -R . "$outpath/web-frontend"
      rm -rf "$outpath/web-frontend/node_modules"
      ln -sf "$node_modules" "$outpath/web-frontend/node_modules"

      ${lib.optionalString (!ossOnly) ''
        mkdir -p $outpath/premium
        cp -R ../premium/web-frontend $outpath/premium/web-frontend

        mkdir -p $outpath/enterprise
        cp -R ../enterprise/web-frontend $outpath/enterprise/web-frontend
      ''}

      mkdir $out/bin
      makeWrapper $node_modules/nuxt/bin/nuxt.js $out/bin/baserow-web-frontend \
        --run "cd $outpath/web-frontend" \
        --set BASEROW_OSS_ONLY "${if ossOnly then "true" else "false"}" \
        --add-flags "start --config-file config/nuxt.config.prod.js"

      runHook postInstall
    '';
  };

  backend = backendEnv;
}