{ lib, ... }:
{
  _class = "clan.service";
  manifest.name = "samba";
  manifest.description = "Samba configuration for NAS";
  manifest.readme = "Samba configuration for NAS";
  manifest.categories = [ "System" ];

  roles.server = {
    description = "A server role that host files";

    interface =
      { lib, ... }:
      let
        userOptions = {
          readPerm = lib.mkOption {
            type = with lib.types; bool;
            description = "Permission to read";
            default = false;
          };
          writePerm = lib.mkOption {
            type = with lib.types; bool;
            description = "Permission to write";
            default = false;
          };
        };
      in
      {
        options = {

          globalUsers = lib.mkOption {
            type =
              with lib.types;
              attrsOf (submodule {
                options = userOptions;
              });
            description = "List of global users with permissions, this will be applied to all the folders.";
            default = {
              admin = {
                readPerm = true;
                writePerm = true;
              };
            };
          };

          sharedFolders = lib.mkOption {
            type =
              with lib.types;
              attrsOf (submodule {
                options = {
                  users = lib.mkOption {
                    type =
                      with lib.types;
                      attrsOf (submodule {
                        options = userOptions;
                      });
                    description = "List of users with permissions, this will only applied to this particular folder.";
                    default = { };
                  };
                  allowedGuest = lib.mkOption {
                    type = with lib.types; bool;
                    description = "Whether to allow guest access to this folder.";
                    default = false;
                  };
                };
              });
            description = "List of folders with users permissions.";
            default = [
              {
                name = "DEFAULT";
              }
            ];

          };

          dataDir = lib.mkOption {
            type =
              with lib.types;
              oneOf [
                str
                path
              ];
            description = "A directory where all samba folders will be.";
          };

        };
      };
    perInstance =
      { settings, ... }:
      let
        allUsernameList = lib.uniqueStrings (
          lib.flatten (
            (lib.mapAttrsToList (name: share: builtins.attrNames share.users) settings.sharedFolders)
            ++ builtins.attrNames settings.globalUsers
          )
        );
      in
      {
        nixosModule =
          {
            lib,
            config,
            pkgs,
            ...
          }:
          {

            users.users = builtins.listToAttrs (
              map (
                username:
                lib.nameValuePair username {
                  isSystemUser = true;
                  group = username;
                }
              ) (allUsernameList ++ builtins.attrNames settings.sharedFolders)
            );

            users.groups = builtins.listToAttrs (
              map (username: lib.nameValuePair username { }) (
                allUsernameList ++ builtins.attrNames settings.sharedFolders
              )
            );

            clan.core.vars.generators = builtins.listToAttrs (
              map (
                username:
                lib.nameValuePair "${username}-smb-password" {
                  files.password = { };
                  runtimeInputs = with pkgs; [
                    coreutils
                    xkcdpass
                    mkpasswd
                  ];
                  script = ''
                    xkcdpass --numwords 3 --delimiter - --count 1 > $out/password
                  '';
                }
              ) allUsernameList
            );

            systemd.services.samba-smbd.postStart =
              lib.concatMapStrings (
                user:
                let
                  passwordPath = config.clan.core.vars.generators."${user}-smb-password".files.password.path;
                  userDir = "${settings.dataDir}/${user}";
                in
                ''
                  mkdir -p ${userDir}
                  chown ${user}:users ${userDir}
                  # if a password is unchanged, this will error
                  (echo $(<${passwordPath}); echo $(<${passwordPath})) | ${config.services.samba.package}/bin/smbpasswd -s -a ${user}
                ''
              ) allUsernameList
              + lib.concatMapStrings (
                share:
                let
                  shareDir = "${settings.dataDir}/${share}";
                in
                ''
                  mkdir -p ${shareDir}
                  chown ${share}:${share} ${shareDir}
                ''
              ) (builtins.attrNames settings.sharedFolders);

            services.samba = {
              enable = true;
              openFirewall = true;
              settings = {
                global = {
                  security = "user";
                  workgroup = "WORKGROUP";
                  "server string" = "WhiteHouse NAS";
                  "max log size" = "50";
                  "dns proxy" = false;
                  "syslog only" = true;
                  "map to guest" = "Bad User";
                  "guest account" = "nobody";
                };
              }
              // lib.mapAttrs (
                shareName: value:
                {
                  path = "${settings.dataDir}/${shareName}";
                  comment = shareName;
                  "force user" = shareName;
                  "force group" = shareName;
                  "create mask" = "0640";
                  "directory mask" = "0750";
                  "read only" = "yes";
                  browseable = "yes";
                  printable = "no";
                  "read list" = lib.concatStringsSep " " (
                    lib.uniqueStrings (
                      lib.flatten (
                        (builtins.attrNames (lib.filterAttrs (n: v: v.readPerm) value.users))
                        ++ (builtins.attrNames (lib.filterAttrs (n: v: v.readPerm) settings.globalUsers))
                      )
                    )
                  );
                  "write list" = lib.concatStringsSep " " (
                    lib.uniqueStrings (
                      lib.flatten (
                        (builtins.attrNames (lib.filterAttrs (n: v: v.writePerm) value.users))
                        ++ (builtins.attrNames (lib.filterAttrs (n: v: v.writePerm) settings.globalUsers))
                      )
                    )
                  );
                }
                // lib.optionalAttrs (value.allowedGuest) {
                  public = "yes";
                  "guest ok" = "yes";
                }
              ) settings.sharedFolders
              // builtins.listToAttrs (
                map (
                  user:
                  lib.nameValuePair user {
                    comment = user;
                    path = "${settings.dataDir}/${user}";
                    "force user" = user;
                    "force group" = "users";
                    public = "yes";
                    "guest ok" = "no";
                    "create mask" = "0640";
                    "directory mask" = "0750";
                    writable = "yes";
                    browseable = "yes";
                    printable = "no";
                    "valid users" = user;
                  }
                ) allUsernameList
              );
            };

            services.samba-wsdd = {
              enable = true;
              openFirewall = true;
            };

            services.avahi = {
              publish.enable = true;
              publish.userServices = true;
              # ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
              nssmdns4 = true;
              # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
              enable = true;
              openFirewall = true;
            };
          };
      };
  };
}
