#!/usr/bin/env python3
from __future__ import annotations

import argparse
import os
import sys
import time
from pathlib import Path

METRIC_FILE = "backup_age.prom"
DEFAULT_CONFIG = "/etc/system-monitor/backup-paths"


def configured_paths(config: str | None, explicit: str | None) -> list[Path]:
    raw: list[str] = []
    if explicit:
        raw.extend(explicit.split(":"))
    elif config:
        try:
            raw.extend(line.strip() for line in Path(config).read_text().splitlines())
        except OSError:
            pass
    return [Path(item).expanduser() for item in raw if item.strip() and not item.strip().startswith("#")]


def newest_mtime(path: Path) -> float | None:
    try:
        if path.is_file():
            return path.stat().st_mtime
        if not path.is_dir():
            return None
    except OSError:
        return None
    newest = None
    for dirpath, _dirnames, filenames in os.walk(path):
        for name in filenames:
            try:
                ts = (Path(dirpath) / name).stat().st_mtime
            except OSError:
                continue
            newest = ts if newest is None else max(newest, ts)
    return newest


def collect(paths: list[Path], now: float) -> dict[str, float]:
    ages = []
    for path in paths:
        ts = newest_mtime(path)
        if ts is not None:
            ages.append(max(0.0, now - ts))
    if not ages:
        return {}
    return {"node_backup_age_seconds": min(ages)}


def render(metrics: dict[str, float]) -> str:
    if not metrics:
        return ""
    lines = [
        "# HELP node_backup_age_seconds Age in seconds of the newest configured backup artifact.",
        "# TYPE node_backup_age_seconds gauge",
        f"node_backup_age_seconds {metrics['node_backup_age_seconds']:.0f}",
    ]
    return "\n".join(lines) + "\n"


def write_atomic(out_dir: Path, name: str, content: str) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    tmp = out_dir / f"{name}.{os.getpid()}.tmp"
    tmp.write_text(content)
    os.replace(tmp, out_dir / name)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output-dir", default=os.environ.get("NODE_TEXTFILE_DIR", "/var/lib/node_exporter/textfile"))
    parser.add_argument("--config", default=os.environ.get("SM_BACKUP_CONFIG", DEFAULT_CONFIG))
    parser.add_argument("--paths", default=os.environ.get("SM_BACKUP_PATHS"))
    parser.add_argument("--stdout", action="store_true")
    args = parser.parse_args()

    content = render(collect(configured_paths(args.config, args.paths), time.time()))
    if args.stdout:
        sys.stdout.write(content)
    elif content:
        write_atomic(Path(args.output_dir), METRIC_FILE, content)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
