{
  config,
  lib,
  nodes,
  ...
}:
with lib; let
  cfg = config.homelab.monitoring;
  homelabCfg = config.homelab;
  hasNodes = length (attrNames nodes) > 0;

  # Local aggregation from this instance
  localAggregation = {
    # Metrics from manually configured + automatic node exporter
    allMetrics =
      cfg.metrics
      ++ (optional cfg.nodeExporter.enable {
        name = "node-exporter";
        host = homelabCfg.hostname;
        port = cfg.nodeExporter.port;
        path = "/metrics";
        jobName = "node";
        scrapeInterval = "30s";
        labels = {
          instance = "${homelabCfg.hostname}.${homelabCfg.domain}";
          environment = homelabCfg.environment;
          location = homelabCfg.location;
        };
      });

    allHealthChecks = cfg.healthChecks;
  };

  # Global aggregation from all nodes (when nodes available)
  globalAggregation = let
    baseAgg = import ../aggregators/base.nix {inherit lib;};

    # Aggregate metrics from all nodes
    metricsAgg = baseAgg.aggregateFromNodes {
      inherit nodes;
      attributePath = "homelab.monitoring.allMetrics";
      enhancer = endpoint:
        endpoint
        // {
          _fullAddress = "${endpoint.host}:${toString endpoint.port}";
          _metricsUrl = "http://${endpoint.host}:${toString endpoint.port}${endpoint.path}";
        };
    };

    # Aggregate health checks from all nodes
    healthChecksAgg = baseAgg.aggregateFromNodes {
      inherit nodes;
      attributePath = "homelab.monitoring.allHealthChecks";
      enhancer = check: let
        actualHost = check.host;
        portPart =
          if check.port != null
          then ":${toString check.port}"
          else "";
        url = "${check.protocol or "http"}://${actualHost}${portPart}${check.path}";
      in
        check
        // {
          _actualHost = actualHost;
          _url = url;
        };
    };
  in {
    allMetrics = metricsAgg.all;
    allHealthChecks = healthChecksAgg.all;

    # Useful groupings for services
    metricsByJobName = groupBy (m: m.jobName) metricsAgg.all;
    healthChecksByGroup = groupBy (hc: hc.group or "default") healthChecksAgg.all;

    summary = {
      totalMetrics = length metricsAgg.all;
      totalHealthChecks = length healthChecksAgg.all;
      nodesCovered = unique (map (m: m._nodeName or m.host) metricsAgg.all);
    };
  };
in {
  # Instance-level monitoring options
  options.homelab.monitoring = {
    enable = mkEnableOption "monitoring system";

    # Node exporter (automatically enabled)
    nodeExporter = {
      enable = mkOption {
        type = types.bool;
        default = true;
      };
      port = mkOption {
        type = types.port;
        default = 9100;
      };
    };

    # Manual metrics (in addition to service auto-registration)
    metrics = mkOption {
      type = types.listOf (types.submodule {
        options = {
          name = mkOption {type = types.str;};
          host = mkOption {
            type = types.str;
            default = homelabCfg.hostname;
          };
          port = mkOption {type = types.port;};
          path = mkOption {
            type = types.str;
            default = "/metrics";
          };
          jobName = mkOption {type = types.str;};
          scrapeInterval = mkOption {
            type = types.str;
            default = "30s";
          };
          labels = mkOption {
            type = types.attrsOf types.str;
            default = {};
          };
        };
      });
      default = [];
    };

    # Manual health checks (in addition to service auto-registration)
    healthChecks = mkOption {
      type = types.listOf (types.submodule {
        options = {
          name = mkOption {type = types.str;};
          host = mkOption {
            type = types.str;
            default = homelabCfg.hostname;
          };
          port = mkOption {
            type = types.nullOr types.port;
            default = null;
          };
          path = mkOption {
            type = types.str;
            default = "/";
          };
          protocol = mkOption {
            type = types.enum ["http" "https" "tcp" "icmp"];
            default = "http";
          };
          method = mkOption {
            type = types.str;
            default = "GET";
          };
          interval = mkOption {
            type = types.str;
            default = "30s";
          };
          timeout = mkOption {
            type = types.str;
            default = "10s";
          };
          conditions = mkOption {
            type = types.listOf types.str;
            default = ["[STATUS] == 200"];
          };
          group = mkOption {
            type = types.str;
            default = "manual";
          };
          labels = mkOption {
            type = types.attrsOf types.str;
            default = {};
          };
          enabled = mkOption {
            type = types.bool;
            default = true;
          };
        };
      });
      default = [];
    };

    # Read-only aggregated data (always exposed)
    allMetrics = mkOption {
      type = types.listOf types.attrs;
      default = localAggregation.allMetrics;
      readOnly = true;
    };

    allHealthChecks = mkOption {
      type = types.listOf types.attrs;
      default = localAggregation.allHealthChecks;
      readOnly = true;
    };

    # Global aggregation (always available, empty if no nodes)
    global = mkOption {
      type = types.attrs;
      default = globalAggregation;
      readOnly = true;
    };
  };

  # Configuration - always includes both local and global
  config = mkIf cfg.enable {
    # Basic instance setup
    services.prometheus.exporters.node = mkIf cfg.nodeExporter.enable {
      enable = true;
      port = cfg.nodeExporter.port;
      enabledCollectors = ["systemd" "textfile" "filesystem" "loadavg" "meminfo" "netdev" "stat"];
    };

    networking.firewall.allowedTCPPorts = optionals cfg.nodeExporter.enable [cfg.nodeExporter.port];

    # homelab.monitoring = {
    #   allMetrics = localAggregation.allMetrics;
    #   allHealthChecks = localAggregation.allHealthChecks;
    #   global =
    #     if hasNodes
    #     then globalAggregation
    #     else {};
    # };
  };
}
