{
  description = "Nix flake for the activity logging script";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/25.11";
    sudoku-solver.url = "path:./src/sudoku-hs";
  };
  outputs =
    { nixpkgs, ... }@inputs:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs {
        inherit system;
        config.allowUnfree = true;
        overlays = [ inputs.self.overlays.default ];
      };
      generatePackages =
        args:
        let
          inherit (args) dir ext handler;
        in
        ./${dir}
        |> builtins.readDir
        |> builtins.attrNames
        |> builtins.filter (file: builtins.match ".*\\.${ext}$" file != null)
        |> builtins.map (file: rec {
          name = builtins.replaceStrings [ ".${ext}" ] [ "" ] file;
          value = handler ./${dir}/${file} name;
        })
        |> builtins.listToAttrs;
    in
    {
      overlays.default = _final: prev: {
        python3Packages = prev.python3Packages.override {
          overrides = _pyself: _pysuper: {
            webcomix = prev.python3Packages.callPackage ./pkgs_pr/webcomix.nix { };
            download = prev.python3Packages.callPackage ./pkgs/download.nix {
              webcomix = _pyself.webcomix;
            };
          };
        };
        download = _final.python3Packages.download;
        # Fix curl-impersonate-ff build issue with CMake 4.0+ requiring CMake >= 3.5
        curl-impersonate-ff = prev.curl-impersonate-ff.overrideAttrs (oldAttrs: {
          # Patch before configure so Makefile.in is patched
          postPatch = (oldAttrs.postPatch or "") + ''
            # Patch Makefile.in to add CMAKE_POLICY_VERSION_MINIMUM flag
            if [ -f Makefile.in ]; then
              sed -i 's|cmake -DCMAKE_BUILD_TYPE=Release|cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_BUILD_TYPE=Release|g' Makefile.in
            fi
          '';
          # Patch after configure when Makefile exists and brotli is extracted
          postConfigure = (oldAttrs.postConfigure or "") + ''
            # Patch brotli CMakeLists.txt after extraction
            for brotli_dir in brotli-*/; do
              if [ -d "$brotli_dir" ] && [ -f "$brotli_dir/CMakeLists.txt" ]; then
                sed -i 's/cmake_minimum_required(VERSION [0-9.]\+)/cmake_minimum_required(VERSION 3.5)/g' "$brotli_dir/CMakeLists.txt"
              fi
            done
            # Also patch the generated Makefile
            if [ -f Makefile ]; then
              sed -i 's|cmake -DCMAKE_BUILD_TYPE=Release|cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_BUILD_TYPE=Release|g' Makefile
            fi
          '';
        });
        # Also fix curl-impersonate if it has the same issue
        curl-impersonate = prev.curl-impersonate.overrideAttrs (oldAttrs: {
          postPatch = (oldAttrs.postPatch or "") + ''
            if [ -f Makefile.in ]; then
              sed -i 's|cmake -DCMAKE_BUILD_TYPE=Release|cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_BUILD_TYPE=Release|g' Makefile.in
            fi
          '';
          postConfigure = (oldAttrs.postConfigure or "") + ''
            for brotli_dir in brotli-*/; do
              if [ -d "$brotli_dir" ] && [ -f "$brotli_dir/CMakeLists.txt" ]; then
                sed -i 's/cmake_minimum_required(VERSION [0-9.]\+)/cmake_minimum_required(VERSION 3.5)/g' "$brotli_dir/CMakeLists.txt"
              fi
            done
            if [ -f Makefile ]; then
              sed -i 's|cmake -DCMAKE_BUILD_TYPE=Release|cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_BUILD_TYPE=Release|g' Makefile
            fi
          '';
        });
      };
      packages.x86_64-linux =
        let
          scriptBin = path: name: pkgs.writeScriptBin name (builtins.readFile path);
          pkgsBin =
            path: _name:
            let
              content = builtins.readFile path;
            in
            if builtins.match ".*(buildPythonPackage|buildPythonApplication).*" content != null then
              pkgs.python3Packages.callPackage path { }
            else
              pkgs.callPackage path { };
        in
        {
          citra = pkgs.callPackage ./pkgs/citra/default.nix { branch = "nightly"; };
          torzu = pkgs.callPackage ./pkgs/torzu/package.nix { };
          pano = pkgs.callPackage ./pkgs/pano/default.nix { };
          vdhcoapp = pkgs.callPackage ./pkgs/vdhcoapp/default.nix { };
          sudoku-solver = inputs.sudoku-solver.packages.${system}.default;
        }
        // generatePackages {
          dir = "pkgs";
          ext = "nix";
          handler = pkgsBin;
        }
        // generatePackages {
          dir = "pkgs_pr";
          ext = "nix";
          handler = pkgsBin;
        }
        // generatePackages {
          dir = "pkgs_wip";
          ext = "nix";
          handler = pkgsBin;
        }
        // generatePackages {
          dir = "src/scripts";
          ext = "sh";
          handler = scriptBin;
        }
        // generatePackages {
          dir = "src/scripts";
          ext = "py";
          handler = scriptBin;
        };

      apps.x86_64-linux = {
        download = {
          type = "app";
          program = "${pkgs.download}/bin/download";
        };
        download-admin = {
          type = "app";
          program = "${pkgs.download}/bin/download-admin";
        };
        download-tests = {
          type = "app";
          program = "${
            pkgs.writeShellApplication {
              name = "download-tests";
              runtimeInputs = [
                (pkgs.python3.withPackages (ps: [ ps.pyyaml ]))
              ];
              text = ''
                set -euo pipefail
                export PYTHONPATH="${inputs.self}/src/download"
                python -m unittest discover -s "${inputs.self}/src/download/tests" -p "test_*.py"
              '';
            }
          }/bin/download-tests";
        };
      };
    };
}
