#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
INVENTORY_FILE="${1:-${REPO_ROOT}/homelab.yml}"

if [[ ! -s "${INVENTORY_FILE}" ]]; then
    printf 'Missing homelab inventory: %s\n' "${INVENTORY_FILE}" >&2
    exit 1
fi

if command -v python3 >/dev/null 2>&1 &&
    python3 - "${INVENTORY_FILE}" <<'PY'
import ipaddress
import sys

try:
    import yaml
except ImportError:
    raise SystemExit(42)

inventory_file = sys.argv[1]

with open(inventory_file, encoding="utf-8") as handle:
    document = yaml.safe_load(handle)

failures = []


def get(path):
    value = document
    for part in path.split("."):
        if not isinstance(value, dict) or part not in value:
            return None
        value = value[part]
    return value


def require(path, expected_type=str):
    value = get(path)
    if value is None or value == "":
        failures.append(f"{path} is required")
        return None
    if expected_type is not None and not isinstance(value, expected_type):
        failures.append(f"{path} must be {expected_type.__name__}, got {type(value).__name__}")
        return None
    return value


if not isinstance(document, dict):
    failures.append("inventory root must be a YAML mapping")
else:
    for path in (
        "metadata.main_script",
        "domain.base",
        "domain.public_url",
        "domain.gitea_url",
        "network.lan_cidr",
        "network.lan_ip_prefix",
        "network.metallb.traefik_ip",
        "hosts.debian.lan_ip",
        "hosts.debian.tailscale_ip",
        "hosts.debian.docker_root",
        "hosts.rpi4.lan_ip",
        "hosts.rpi4.tailscale_ip",
        "hosts.rpi4.docker_root",
        "hosts.opi5_pimox.lan_ip",
        "hosts.opi5_pimox.worker_storage",
        "hosts.oci_edge.public_ip",
        "services.gitea.root_url",
        "services.heimdall.public_url",
        "services.local_registry.endpoint",
        "services.traefik.load_balancer_ip",
    ):
        require(path)
    require("pimox.default_worker_count", None)
    require("services.heimdall.http_port", None)

    subdomains = require("domain.subdomains", list)
    if isinstance(subdomains, list):
        for index, value in enumerate(subdomains):
            if not isinstance(value, str) or not value:
                failures.append(f"domain.subdomains[{index}] must be a non-empty string")

    for path in (
        "network.lan_cidr",
        "network.metallb.traefik_ip",
        "hosts.debian.lan_ip",
        "hosts.debian.tailscale_ip",
        "hosts.rpi4.lan_ip",
        "hosts.rpi4.tailscale_ip",
        "hosts.opi5_pimox.lan_ip",
        "hosts.oci_edge.public_ip",
        "services.traefik.load_balancer_ip",
    ):
        value = get(path)
        if not value:
            continue
        try:
            if path.endswith("lan_cidr"):
                ipaddress.ip_network(str(value), strict=False)
            else:
                ipaddress.ip_address(str(value))
        except ValueError:
            failures.append(f"{path} is not a valid IP address/CIDR: {value}")

    worker_count = get("pimox.default_worker_count")
    if worker_count is not None:
        try:
            if int(worker_count) < 0:
                failures.append("pimox.default_worker_count must be non-negative")
        except (TypeError, ValueError):
            failures.append("pimox.default_worker_count must be an integer")

if failures:
    for failure in failures:
        print(f"inventory error: {failure}", file=sys.stderr)
    raise SystemExit(1)

print(f"inventory ok: {inventory_file}")
PY
then
    exit 0
else
    status=$?
    if [[ "${status}" != "42" ]]; then
        exit "${status}"
    fi
fi

if command -v ruby >/dev/null 2>&1; then
    ruby - "${INVENTORY_FILE}" <<'RUBY'
require "ipaddr"
require "yaml"

inventory_file = ARGV.fetch(0)
document = YAML.load_file(inventory_file)
failures = []

def get(document, path)
  path.split(".").reduce(document) do |value, part|
    return nil unless value.is_a?(Hash) && value.key?(part)
    value[part]
  end
end

required_paths = %w[
  metadata.main_script
  domain.base
  domain.public_url
  domain.gitea_url
  network.lan_cidr
  network.lan_ip_prefix
  network.metallb.traefik_ip
  hosts.debian.lan_ip
  hosts.debian.tailscale_ip
  hosts.debian.docker_root
  hosts.rpi4.lan_ip
  hosts.rpi4.tailscale_ip
  hosts.rpi4.docker_root
  hosts.opi5_pimox.lan_ip
  hosts.opi5_pimox.worker_storage
  hosts.oci_edge.public_ip
  services.gitea.root_url
  services.heimdall.http_port
  services.heimdall.public_url
  services.local_registry.endpoint
  services.traefik.load_balancer_ip
  pimox.default_worker_count
]

unless document.is_a?(Hash)
  failures << "inventory root must be a YAML mapping"
else
  required_paths.each do |path|
    value = get(document, path)
    failures << "#{path} is required" if value.nil? || value == ""
  end

  subdomains = get(document, "domain.subdomains")
  if !subdomains.is_a?(Array)
    failures << "domain.subdomains must be Array"
  else
    subdomains.each_with_index do |value, index|
      failures << "domain.subdomains[#{index}] must be a non-empty string" unless value.is_a?(String) && !value.empty?
    end
  end

  %w[
    network.metallb.traefik_ip
    hosts.debian.lan_ip
    hosts.debian.tailscale_ip
    hosts.rpi4.lan_ip
    hosts.rpi4.tailscale_ip
    hosts.opi5_pimox.lan_ip
    hosts.oci_edge.public_ip
    services.traefik.load_balancer_ip
  ].each do |path|
    value = get(document, path)
    next if value.nil? || value == ""
    begin
      IPAddr.new(value.to_s)
    rescue ArgumentError
      failures << "#{path} is not a valid IP address: #{value}"
    end
  end

  cidr = get(document, "network.lan_cidr")
  begin
    IPAddr.new(cidr.to_s) if cidr
  rescue ArgumentError
    failures << "network.lan_cidr is not a valid CIDR: #{cidr}"
  end

  worker_count = get(document, "pimox.default_worker_count")
  failures << "pimox.default_worker_count must be non-negative" if worker_count && worker_count.to_i < 0
end

if failures.any?
  failures.each { |failure| warn "inventory error: #{failure}" }
  exit 1
end

puts "inventory ok: #{inventory_file}"
RUBY
    exit 0
fi

cat >&2 <<'EOF'
No real YAML parser is available.
Install python3-yaml or ruby, then rerun scripts/validate-homelab-inventory.
EOF
exit 1
