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

let
  cfg = config.services.dufs;

  # User credential submodule
  userCredential = lib.types.submodule {
    options = {
      username = lib.mkOption {
        type = lib.types.str;
        description = "Username for authentication";
      };
      passwordHash = lib.mkOption {
        type = lib.types.str;
        description = ''
          SHA-512 password hash (generate with: mkpasswd -m sha-512)
          Note: Must start with $6$ for dufs compatibility
        '';
      };
    };
  };

  # Helper to create auth arguments for different permission levels
  mkAuthArgs = rwUsers: roUsers:
    let
      writers = lib.concatMapStringsSep " "
        (user: "-a '${user.username}:${user.passwordHash}@/:rw'")
        rwUsers;
      readers = lib.concatMapStringsSep " "
        (user: "-a '${user.username}:${user.passwordHash}@/:ro'")
        roUsers;
    in
    lib.concatStringsSep " " (lib.filter (s: s != "") [writers readers]);
in
{
  options.services.dufs = {
    enable = lib.mkEnableOption "dufs file server";

    package = lib.mkOption {
      type = lib.types.package;
      default = pkgs.dufs;
      description = "The dufs package to use";
    };

    servePathPublic = lib.mkOption {
      type = lib.types.str;
      default = "/nfs_export/kage/dufs/public";
      description = "Directory path to serve files from";
    };

    servePathPrivate = lib.mkOption {
      type = lib.types.str;
      default = "/nfs_export/kage/dufs/private";
      description = "Directory path to serve files from";
    };

    user = lib.mkOption {
      type = lib.types.str;
      default = "dufs";
      description = "User to run dufs service as (should match NFS share owner)";
    };

    group = lib.mkOption {
      type = lib.types.str;
      default = "dufs";
      description = "Group to run dufs service as (should match NFS share group)";
    };

    publicInstance = {
      enable = lib.mkEnableOption "public read-only instance";

      port = lib.mkOption {
        type = lib.types.port;
        default = 5000;
        description = "Port for public read-only instance";
      };

      bind = lib.mkOption {
        type = lib.types.str;
        default = "0.0.0.0";
        description = "Bind address for public instance";
      };

      users = lib.mkOption {
        type = lib.types.listOf userCredential;
        default = [];
        description = "Users with read-write access";
      };

      allowSearch = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = "Allow searching in public instance";
      };
    };

    privateInstance = {
      enable = lib.mkEnableOption "private read-write instance";

      port = lib.mkOption {
        type = lib.types.port;
        default = 5001;
        description = "Port for private authenticated instance";
      };

      bind = lib.mkOption {
        type = lib.types.str;
        default = "0.0.0.0";
        description = "Bind address for private instance";
      };

      users = lib.mkOption {
        type = lib.types.listOf userCredential;
        default = [];
        description = "Users with read-write access";
      };

      guestUsers = lib.mkOption {
        type = lib.types.listOf userCredential;
        default = [];
        description = "Guest users with read-only access";
      };

      allowUpload = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = "Allow file uploads";
      };

      allowDelete = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = "Allow file deletion";
      };

      allowSearch = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = "Allow searching";
      };
    };

    openFirewall = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Open firewall ports for enabled instances";
    };
  };

  config = lib.mkIf cfg.enable {
    # Create dufs user and group only if using default user/group
    users.users.${cfg.user} = lib.mkIf (cfg.user == "dufs") {
      isSystemUser = true;
      group = cfg.group;
      extraGroups = [ "users" ];  # Add to users group for access to shared files
      description = "dufs file server user";
    };

    users.groups.${cfg.group} = lib.mkIf (cfg.group == "dufs") {};

    # Ensure directories exist (ownership should be managed by NFS or external system)
    systemd.tmpfiles.rules = [
      "d ${cfg.servePathPublic} 0755 - - -"
      "d ${cfg.servePathPrivate} 0755 - - -"
    ];

    # Public read-only instance
    systemd.services.dufs-public = lib.mkIf cfg.publicInstance.enable {
      description = "dufs public read-only file server";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];

      serviceConfig = {
        Type = "simple";
        User = cfg.user;
        Group = cfg.group;
        ExecStart =
          let
            hasAuth = (cfg.publicInstance.users != []);
            authArgs = lib.optionalString hasAuth
              (mkAuthArgs cfg.publicInstance.users []) + " -a @/"; # allows read only on all paths (public)
          in ''
            ${cfg.package}/bin/dufs ${cfg.servePathPublic} \
              --bind ${cfg.publicInstance.bind} \
              --port ${toString cfg.publicInstance.port} \
              ${authArgs} \
              ${lib.optionalString cfg.publicInstance.allowSearch "--allow-search"}
          '';
        Restart = "on-failure";
        RestartSec = "10s";

        # Hardening
        NoNewPrivileges = true;
        PrivateTmp = true;
        ProtectSystem = "strict";
        ProtectHome = true;
        ReadOnlyPaths = [ cfg.servePathPublic ];
      };
    };

    # Private read-write instance
    systemd.services.dufs-private = lib.mkIf cfg.privateInstance.enable {
      description = "dufs private read-write file server";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];

      serviceConfig = {
        Type = "simple";
        User = cfg.user;
        Group = cfg.group;
        ExecStart = ''
          ${cfg.package}/bin/dufs ${cfg.servePathPrivate} \
            --bind ${cfg.privateInstance.bind} \
            --port ${toString cfg.privateInstance.port} \
            ${mkAuthArgs cfg.privateInstance.users cfg.privateInstance.guestUsers} \
            ${lib.optionalString cfg.privateInstance.allowUpload "--allow-upload"} \
            ${lib.optionalString cfg.privateInstance.allowDelete "--allow-delete"} \
            ${lib.optionalString cfg.privateInstance.allowSearch "--allow-search"}
        '';
        Restart = "on-failure";
        RestartSec = "10s";

        # Hardening
        NoNewPrivileges = true;
        PrivateTmp = true;
        ProtectSystem = "strict";
        ProtectHome = true;
        ReadWritePaths = [ cfg.servePathPrivate ];
      };
    };

    # Firewall configuration
    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall (
      (lib.optional cfg.publicInstance.enable cfg.publicInstance.port) ++
      (lib.optional cfg.privateInstance.enable cfg.privateInstance.port)
    );
  };
}
