{ config, lib, pkgs, ... }:

let cfg = config.sydnix.git-annex;
in {
  options = {
    sydnix.git-annex = {
      enable = lib.mkEnableOption "git-annex";

      user = {
        name = lib.mkOption {
          type = lib.types.str;
        };
        email = lib.mkOption {
          type = lib.types.str;
        };
      };

      keyFiles = lib.mkOption {
        type = lib.types.listOf lib.types.path;
        default = [];
      };

      repos = lib.mkOption {
        type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
          options = {
            enable = lib.mkOption {
              description = ''
                Git-annex repo ${name};
              '';
              type = lib.types.bool;
              default = true;
            };

            symlinkToAnnexHome = lib.mkOption {
              default = null;
              type = lib.types.nullOr lib.types.str;
            };

            path = lib.mkOption {
              description = ''
                Path to the repo.
              '';
              type = lib.types.str;
              default = name;
            };

            remotes = lib.mkOption {
              type = lib.types.attrsOf lib.types.str;
              default = {};
            };

            user = lib.mkOption {
              type = lib.types.str;
              default = "annex";
              description = ''
                The user that the Git-annex assistant will be run as.
              '';
            };

            group = lib.mkOption {
              type = lib.types.str;
              default = "annex";
            };

            managed = lib.mkOption {
              description = ''
                If true, the repo will be automatically managed by Nix.
                'Management' includes creating and initialising the repo and
                setting permissions.
              '';
              type = lib.types.bool;
              default = false;
            };
          };
        }));
      };
    };
  };

  config = lib.mkIf cfg.enable {
    # Git-annex assistant requires remotes to have git-annex on the PATH.
    environment.systemPackages = [
      pkgs.git-annex
    ];

    # The user that users log in as to access managed repos.
    users.groups.annex = {};
    users.users.annex = {
      # Necessary to enable cloning over SSH.
      isNormalUser = true;
      group = "annex";
      # TODO: Don't hardcode extra groups!
      extraGroups = [ "jellyfin" ];
      home = "/var/sydnix/annex";
      homeMode = "770";
      createHome = true;
      openssh.authorizedKeys.keyFiles = cfg.keyFiles;
    };

    # HACK: Some Jellyfin libraries are served by Git-annex.  Give Jellyfin
    # permission to symlink some of those libraries into the annex user's home
    # dir.
    users.users.${config.services.jellyfin.user}.extraGroups =
      lib.mkIf config.sydnix.deertopia.jellyfin.enable
        [ "annex" ];

    system.activationScripts.initialiseUserAnnex =
      let gitconfig-file = pkgs.writeText "gitconfig" ''
            [user]
            	email = ${cfg.user.email}
            	name = ${cfg.user.name}
            [init]
            	defaultBranch = main
            [core]
            	symlinks = true
            [safe]
            ${lib.strings.concatMapStrings
              (repo: "\tdirectory = ${repo.path}\n")
              (builtins.attrValues cfg.repos)}
          '';
      in ''
        set -e

        annexHome="${config.users.users.annex.home}"
        ln -sf "${gitconfig-file}" "$annexHome/.gitconfig"
      '';

    systemd.services =
      let
        init-if-necessary = repo:
          pkgs.writeScript "git-annex-init-if-necessary" ''
            #!/usr/bin/env bash
            set -e

            [ -e .git ] || git init
            [ -e .git/annex ] || git annex init

            # git config set user.name "${cfg.user.name}"
            # git config set user.email "${cfg.user.email}"

            # Symlink repos into annex's home for easy access.  This is
            # particularly nice for cloning:
            #   git clone annex@deertopia.net:org
            # instead of
            #   git clone annex@deertopia.net:/persist/deertopia.net/dav/org
            # Less assumptions about the host's file system!
            annexHome="${config.users.users.annex.home}"
            ${lib.optionalString (repo.symlinkToAnnexHome != null)
              ''ln -sf "$PWD" "$annexHome/${repo.symlinkToAnnexHome}"''}

            ${lib.strings.toShellVar "remotes" repo.remotes}
            for remoteName in ''${!remotes[@]}; do
              err=0
              git remote get-url "$remoteName" || err=$?
              if [ $err -eq 0 ]; then
                git remote set-url "$remoteName" "''${remotes["$remoteName"]}"
              else
                git remote add "$remoteName" "''${remotes["$remoteName"]}"
              fi
            done
          '';

        mkAssistantService = repo: {
          description = "git-annex assistant (${repo.path})";
          after = [ "network.target" ];
          wantedBy = [ "multi-user.target" ];
          serviceConfig.User = repo.user;
          serviceConfig.WorkingDirectory = repo.path;
          path = [ pkgs.git pkgs.git-annex pkgs.openssh ];
          # Set the default file mode for new files created by this process to
          # 775.  Oddly, `UMask = x` will set the mode to `0777 & x`, so 002
          # gets us the desired figure of 775.  We want 775 because Nginx,
          # running under user `nginx` needs to be able to read/write these
          # files to server them over WebDAV.
          serviceConfig.UMask = "002";
          serviceConfig.ExecStartPre =
            lib.optionalString
              repo.managed
              "${pkgs.bash}/bin/bash ${init-if-necessary repo}";
          serviceConfig.ExecStart = ''
            ${pkgs.git-annex}/bin/git-annex assistant --foreground
          '';
        };
      in builtins.listToAttrs
        (builtins.map
          (repo: {
            name = "annex-${lib.strings.sanitizeDerivationName repo.path}";
            value = mkAssistantService repo;
          })
          (builtins.attrValues cfg.repos));
  };
}
