{ clanLib, ... }:
{
  _class = "clan.service";
  manifest.name = "prometheus";
  manifest.description = "The Prometheus monitoring system and time series database.";
  manifest.readme = builtins.readFile ./README.md;
  manifest.categories = [ "System" ];

  roles.server = {
    description = "Prometheus server that scraps all data from nodes";

    interface =
      { lib, ... }:
      {
        options = {
          scrape_interval = lib.mkOption {
            type = with lib.types; nullOr str;
            default = "5m";
            description = "How often to scrape targets. Default is 5 minutes";
          };
        };
      };

    perInstance =
      {
        settings,
        roles,
        ...
      }:
      {
        nixosModule =
          {
            config,
            lib,
            ...
          }:
          let
            getYggdrasilIP =
              machineName:
              if config.clan.core.vars.generators.yggdrasil.files.address ? value then
                clanLib.getPublicValue {
                  flake = config.clan.core.settings.directory;
                  machine = machineName;
                  generator = "yggdrasil";
                  file = "address";
                  default = null;
                }
              else
                throw "clanService/yggdrasil is required";
          in
          {
            networking.firewall.allowedTCPPorts = [
              9090
            ];
            services.prometheus = {
              enable = true;

              globalConfig = {
                scrape_interval = settings.scrape_interval;
              };

              scrapeConfigs = lib.mapAttrsToList (machineName: machineVal: {
                tls_config.insecure_skip_verify = true;
                job_name = "${machineName}";
                static_configs = lib.mapAttrsToList (
                  exporterName: exporterVal:
                  let
                    targetPort =
                      if exporterVal ? port then
                        exporterVal.port
                      else
                        config.services.prometheus.exporters."${exporterName}".port;
                    targetHost = getYggdrasilIP machineName;
                  in
                  {
                    targets = [ "[${targetHost}]:${lib.toString targetPort}" ];
                  }
                ) machineVal.settings.exporters;
              }) roles.nodes.machines;

            };

          };
      };
  };

  roles.nodes = {
    description = "A node will expose metrics for server to harvest";

    interface =
      { lib, ... }:
      {
        options = {
          exporters = lib.mkOption {
            type = lib.types.attrsOf (lib.types.submodule { });
            default = { };
            description = "Mirror of services.prometheus.exporters";
          };
        };
      };

    perInstance =
      { settings, ... }:
      let
        enabledExporters = builtins.mapAttrs (
          name: value:
          value
          // {
            enable = true;
            openFirewall = true;
          }
        ) settings.exporters;
      in
      {
        nixosModule =
          { ... }:
          {
            services.prometheus.exporters = enabledExporters;
          };
      };
  };

}
