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

let
  cfg = config.my.services.seaweedfs;

  tomlFormat = pkgs.formats.toml {};

  makeCommandLine' = prefix: options: let
    makeFlag = name: value:
      if builtins.isAttrs value then makeCommandLine' "${prefix}${name}." value
      else if builtins.isString value then "-${prefix}${name}=${lib.strings.escapeShellArg value}"
      else if builtins.isInt value then "-${prefix}${name}=${toString value}"
      else "-${name}";
    optionsList = lib.attrsets.mapAttrsToList makeFlag options;
  in
    lib.strings.concatStringsSep " " optionsList;
  makeCommandLine = makeCommandLine' "";

  commandLineType = lib.types.oneOf [
    lib.types.bool
    lib.types.int
    lib.types.str
    (lib.types.attrsOf commandLineType)
  ];
in {
  options.my.services.seaweedfs = {
    package = lib.mkOption {
      type = lib.types.package;
      default = depot.nix.pkgs.seaweedfs;  # pkgs.seaweedfs;
    };

    securitySettings = lib.mkOption {
      type = lib.types.submodule {
        freeformType = tomlFormat.type;
      };
      default = {};
    };

    filer = {
      enable = lib.mkEnableOption "SeaweedFS filer";

      package = lib.mkOption {
        type = lib.types.package;
      };

      earlyOptions = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
        default = {};
      };
      options = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
      };

      settings = lib.mkOption {
        type = lib.types.submodule {
          freeformType = tomlFormat.type;
        };
        default = {};
      };
      notificationSettings = lib.mkOption {
        type = lib.types.submodule {
          freeformType = tomlFormat.type;
        };
        default = {};
      };
      replicationSettings = lib.mkOption {
        type = lib.types.submodule {
          freeformType = tomlFormat.type;
        };
        default = {};
      };
    };

    master = {
      enable = lib.mkEnableOption "SeaweedFS master";

      package = lib.mkOption {
        type = lib.types.package;
      };

      earlyOptions = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
        default = {};
      };
      options = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
        default = {};
      };

      settings = lib.mkOption {
        type = lib.types.submodule {
          freeformType = tomlFormat.type;
        };
        default = {};
      };
    };

    volume = {
      enable = lib.mkEnableOption "SeaweedFS volume";

      package = lib.mkOption {
        type = lib.types.package;
      };

      earlyOptions = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
        default = {};
      };
      options = lib.mkOption {
        type = lib.types.submodule {
          freeformType = commandLineType;
        };
        default = {};
      };
    };

    cli = {
      enable = lib.mkEnableOption "add SeaweedFS CLI";

      package = lib.mkOption {
        type = lib.types.package;
      };

      settings = lib.mkOption {
        type = lib.types.submodule {
          freeformType = tomlFormat.type;
        };
        default = {};
      };
    };

    sync = lib.mkOption {
      type = lib.types.attrsOf (lib.types.submodule {
        options = {
          package = lib.mkOption {
            type = lib.types.package;
            default = cfg.package;
          };

          earlyOptions = lib.mkOption {
            type = lib.types.submodule {
              freeformType = commandLineType;
            };
            default = {};
          };
          options = lib.mkOption {
            type = lib.types.submodule {
              freeformType = commandLineType;
            };
            default = {};
          };
        };
      });
      default = {};
    };
  };

  config = lib.mkMerge [{
    my.services.seaweedfs.master.package = lib.mkDefault cfg.package;
    my.services.seaweedfs.filer.package = lib.mkDefault cfg.package;
    my.services.seaweedfs.volume.package = lib.mkDefault cfg.package;
    my.services.seaweedfs.cli.package = lib.mkDefault cfg.package;
  } (lib.mkIf (cfg.filer.enable || cfg.master.enable || cfg.volume.enable) {
    environment.etc."seaweedfs/security.toml".source = tomlFormat.generate "seaweedfs-security.toml" cfg.securitySettings;

    my.services.seaweedfs.cli.enable = lib.mkDefault true;

    users.groups.seaweedfs = {};

    systemd.targets.seaweedfs = {
      description = "SeaweedFS components";
      wantedBy = [ "multi-user.target" ];
    };
  }) (lib.mkIf (cfg.cli.enable) {
    environment.systemPackages = [ cfg.cli.package ];

    environment.etc."seaweedfs/shell.toml".source = tomlFormat.generate "seaweedfs-shell.toml" cfg.cli.settings;
  }) (lib.mkIf (cfg.filer.enable) {
    environment.etc."seaweedfs/filer.toml".source = tomlFormat.generate "seaweedfs-filer.toml" cfg.filer.settings;
    environment.etc."seaweedfs/notification.toml".source = tomlFormat.generate "seaweedfs-notification.toml" cfg.filer.notificationSettings;
    environment.etc."seaweedfs/replication.toml".source = tomlFormat.generate "seaweedfs-replication.toml" cfg.filer.replicationSettings;

    my.services.seaweedfs.filer.options = {
      localSocket = lib.mkDefault "/run/seaweedfs-filer/filer.sock";
    };

    systemd.services."seaweedfs-filer" = {
      wantedBy = [ "seaweedfs.target" ];
      restartTriggers = [
        (config.environment.etc."seaweedfs/security.toml".source)
        (config.environment.etc."seaweedfs/filer.toml".source)
        (config.environment.etc."seaweedfs/notification.toml".source)
        (config.environment.etc."seaweedfs/replication.toml".source)
      ];
      serviceConfig = {
        ExecStart = "${cfg.filer.package}/bin/weed ${makeCommandLine cfg.filer.earlyOptions} filer ${makeCommandLine cfg.filer.options}";
        User = "seaweedfs-filer";
        Group = "seaweedfs";
        DynamicUser = true;
        RuntimeDirectory = "seaweedfs-filer";
        StateDirectory = "seaweedfs-filer";
        Restart = "always";
      };
    };
  }) (lib.mkIf (cfg.master.enable) {
    environment.etc."seaweedfs/master.toml".source = tomlFormat.generate "seaweedfs-master.toml" cfg.master.settings;

    systemd.services."seaweedfs-master" = {
      wantedBy = [ "seaweedfs.target" ];
      restartTriggers = [
        (config.environment.etc."seaweedfs/security.toml".source)
      ];
      serviceConfig = {
        ExecStart = "${cfg.master.package}/bin/weed ${makeCommandLine cfg.master.earlyOptions} master ${makeCommandLine cfg.master.options}";
        User = "seaweedfs-master";
        Group = "seaweedfs";
        DynamicUser = true;
        RuntimeDirectory = "seaweedfs-master";
        StateDirectory = "seaweedfs-master";
        Restart = "always";
      };
    };
  }) (lib.mkIf (cfg.volume.enable) {
    my.services.seaweedfs.volume.options = {
      dir = lib.mkDefault "/var/lib/seaweedfs-volume/data";
      "dir.idx" = lib.mkDefault "/var/lib/seaweedfs-volume/idx";
    };

    systemd.services."seaweedfs-volume" = {
      wantedBy = [ "seaweedfs.target" ];
      restartTriggers = [
        (config.environment.etc."seaweedfs/security.toml".source)
      ];
      serviceConfig = {
        ExecStart = "${cfg.volume.package}/bin/weed ${makeCommandLine cfg.volume.earlyOptions} volume ${makeCommandLine cfg.volume.options}";
        User = "seaweedfs-volume";
        Group = "seaweedfs";
        DynamicUser = true;
        RuntimeDirectory = "seaweedfs-volume";
        StateDirectory = "seaweedfs-volume";
        Restart = "always";
      };
    };
  }) {
    systemd.services = lib.mapAttrs' (k: v: lib.nameValuePair "seaweedfs-sync-${k}" {
      wantedBy = [ "seaweedfs.target" ];
      unitConfig = {
        StartLimitIntervalSec = "0";
      };
      serviceConfig = {
        ExecStart = "${v.package}/bin/weed ${makeCommandLine v.earlyOptions} filer.sync ${makeCommandLine v.options}";
        User = "seaweedfs-sync";
        DynamicUser = true;
        Restart = "always";

        RestartSec = "100ms";
        RestartSteps = 10;
        RestartMaxDelaySec = "1min";
      };
    }) cfg.sync;
  }];
}