{ pkgs, config, lib, inputs, ... }:
with lib;
with types;
let
  password-store = config.secretsConfig.password-store;
  password-store-relative = removePrefix config.home-manager.users.${config.mainuser}.home.homeDirectory password-store;
  secret = { name, ... }: {
    options = {
      encrypted = mkOption {
        type = path;
        default = "${password-store}/${name}.gpg";
      };
      decrypted = mkOption {
        type = path;
        default = "/var/secrets/${name}";
      };
      decrypt = mkOption {
        default = pkgs.writeShellScript "gpg-decrypt" ''
          set -euo pipefail
          export GNUPGHOME=${config.secretsConfig.gnupgHome}
          export GPG_TTY="$(tty)"
          ${pkgs.gnupg}/bin/gpg-connect-agent updatestartuptty /bye 1>&2
          ${pkgs.gnupg}/bin/gpg --batch --no-tty --decrypt
        '';
      };
      user = mkOption {
        type = str;
        default = config.mainuser;
      };
      owner = mkOption {
        type = str;
        default = "root:root";
      };
      permissions = mkOption {
        type = lib.types.addCheck lib.types.str
          (perm: (builtins.match "[0-7]{3}" perm) != null);
        default = "400";
      };
      services = mkOption {
        type = listOf str;
        default = [ "${name}" ];
      };
      __toString = mkOption {
        readOnly = true;
        default = s: s.decrypted;
      };
    };
  };

  activate-secrets = pkgs.writeShellScriptBin "activate-secrets" ''
    set -euo pipefail
    PATH="${with pkgs; lib.makeBinPath [ openssh gnupg coreutils ]}:$PATH"
    export SSH_AUTH_SOCK="$1"
    export GNUPGHOME=${config.secretsConfig.gnupgHome}
    if [ -d "${password-store}/.git" ]; then
      ${pkgs.git}/bin/git -C "${password-store}" pull
    else
      echo "${lib.escapeShellArg config.secretsConfig.repo}"
      ${pkgs.git}/bin/git clone ${
        lib.escapeShellArg config.secretsConfig.repo
      } "${password-store}"
    fi
    cat ${password-store}/ssh-builder.gpg | ${pkgs.gnupg}/bin/gpg --decrypt > /dev/null
    [ ! -z "${allServices}" ] && /run/wrappers/bin/sudo systemctl restart ${allServices}
  '';

  decrypt = name: cfg:
    with cfg; let
      doas-user = "/run/wrappers/bin/doas -u ${user}";
    in {
      "${name}-secrets" = rec {
        wantedBy = [ "multi-user.target" ];
        requires = [ "user@1000.service" ];
        after = requires;

        preStart = ''
          ${doas-user} stat '${encrypted}'
          mkdir -p '${builtins.dirOf decrypted}'
        '';

        script = ''
          if ${doas-user} cat '${encrypted}' | ${doas-user} ${cfg.decrypt} > '${decrypted}.tmp'; then
            mv -f '${decrypted}.tmp' '${decrypted}'
            chown '${owner}' '${decrypted}'
            chmod '${permissions}' '${decrypted}'
          else
            echo "Failed to decrypt the secret"
            rm '${decrypted}.tmp'
            if [[ -f '${decrypted}' ]]; then
              echo "The decrypted file exists anyways, not failing"
              exit 0
            else
              exit 1
            fi
          fi
        '';

        serviceConfig = {
          Type = "oneshot";
          RemainAfterExit = "yes";
        };
      };
    };

  addDependencies = name: cfg:
    with cfg;
    genAttrs services (service: rec {
      requires = [ "${name}-secrets.service" ];
      after = requires;
      bindsTo = requires;
    });

  mkServices = name: cfg: [ (decrypt name cfg) (addDependencies name cfg) ];

  allServicesMap = map (name: "${name}-envsubst.service")
    (builtins.attrNames config.secrets-envsubst)
    ++ map (name: "${name}-secrets.service")
    (builtins.attrNames config.secrets);

  allServices = toString allServicesMap;

  # https://github.com/nix-community/home-manager/blob/a993eac1065c6ce63a8d724b7bccf624d0e91ca2/modules/services/gpg-agent.nix#L22
  home-conf = config.home-manager.users.${config.mainuser};
  homedir = home-conf.programs.gpg.homedir;
  gpgconf = dir: let
    hash = substring 0 24 (hexStringToBase32 (builtins.hashString "sha1" homedir));
  in if homedir == "${home-conf.home.homeDirectory}/.gnupg" then
    "%t/gnupg/${dir}"
  else
    "%t/gnupg/d.${hash}/${dir}";
  hexStringToBase32 = with lib; let
    mod = a: b: a - a / b * b;
    pow2 = elemAt [ 1 2 4 8 16 32 64 128 256 ];
    splitChars = s: init (tail (splitString "" s));

    base32Alphabet = splitChars "ybndrfg8ejkmcpqxot1uwisza345h769";
    hexToIntTable = listToAttrs (genList (x: {
      name = toLower (toHexString x);
      value = x;
    }) 16);

    initState = {
      ret = "";
      buf = 0;
      bufBits = 0;
    };
    go = { ret, buf, bufBits }:
      hex:
      let
        buf' = buf * pow2 4 + hexToIntTable.${hex};
        bufBits' = bufBits + 4;
        extraBits = bufBits' - 5;
      in if bufBits >= 5 then {
        ret = ret + elemAt base32Alphabet (buf' / pow2 extraBits);
        buf = mod buf' (pow2 extraBits);
        bufBits = bufBits' - 5;
      } else {
        ret = ret;
        buf = buf';
        bufBits = bufBits';
      };
  in hexString: (foldl' go initState (splitChars hexString)).ret;
in {
  options.secrets = lib.mkOption {
    type = attrsOf (submodule secret);
    default = { };
  };

  options.secretsConfig = {
    password-store = lib.mkOption {
      type = lib.types.path;
      default = "${config.home-manager.users.${config.mainuser}.xdg.dataHome}/password-store";
    };
    gnupgHome = lib.mkOption {
      type = lib.types.path;
      default = "${config.home-manager.users.${config.mainuser}.xdg.dataHome}/gnupg";
    };
    repo = lib.mkOption {
      type = str;
      default = "gitea@code.ataraxiadev.com:AtaraxiaDev/pass.git";
    };
  };

  config.systemd.services =
    mkMerge (concatLists (mapAttrsToList mkServices config.secrets));

  config.security.doas.extraRules = [{
    users = [ config.mainuser ];
    noPass = true;
    keepEnv = true;
    cmd = "/run/current-system/sw/bin/systemctl";
    args = [ "restart" ] ++ allServicesMap;
  }];

  config.security.sudo.extraRules = [{
    users = [ config.mainuser ];
    commands = [{
      command = "/run/current-system/sw/bin/systemctl";
      options = [ "SETENV" "NOPASSWD" ];
    }];
  }];

  config.persist.derivative.directories = [ "/var/secrets" ];
  # config.persist.derivative.homeDirectories = [ password-store-relative ];
  config.persist.derivative.homeDirectories = [ ".local/share/password-store" ];
  # config.persist.derivative.homeDirectories = [{
  #   directory = password-store-relative;
  #   method = "symlink";
  # }];

  config.home-manager.users.${config.mainuser} = {
    systemd.user.services.activate-secrets = let
      ssh-agent = gpgconf "S.gpg-agent.ssh";
    in {
      Service = {
        ExecStart = "${activate-secrets}/bin/activate-secrets '${ssh-agent}'";
        Type = "oneshot";
      };
      Unit = {
        PartOf = [ "graphical-session-pre.target" ];
      };
      Install.WantedBy = [ "graphical-session-pre.target" ];
    };
    systemd.user.services.pass-store-sync = {
      Service = {
        Environment = [
          "PASSWORD_STORE_DIR=${password-store}"
          "PATH=${with pkgs; lib.makeBinPath [ pass inotify-tools gnupg ]}"
        ];
        ExecStart = toString (pkgs.writeShellScript "pass-store-sync" ''
          export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
          while inotifywait "$PASSWORD_STORE_DIR" -r -e move -e close_write -e create -e delete --exclude .git; do
            sleep 0.1
            pass git add --all
            pass git commit -m "$(date +%F)_$(date +%T)"
            pass git pull --rebase
            pass git push
          done
        '');
      };
      Unit = rec {
        After = [ "activate-secrets.service" ];
        Wants = After;
      };
      Install.WantedBy = [ "graphical-session-pre.target" ];
    };
    programs.password-store = {
      enable = true;
      package = pkgs.pass-wayland;
      settings.PASSWORD_STORE_DIR = password-store;
    };
  };
}