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

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

METRIC_FILE = "tmpfs_junk.prom"


def stale_junk_bytes(root: Path, stale_hours: int, uid: int) -> int:
    cutoff = time.time() - stale_hours * 3600
    total = 0
    for dirpath, _dirnames, filenames in os.walk(root):
        for name in filenames:
            path = Path(dirpath) / name
            try:
                st = path.lstat()
            except OSError:
                continue
            if st.st_uid == uid and st.st_mtime <= cutoff:
                total += st.st_size
    return total


def collect(root: Path, stale_hours: int, uid: int) -> dict[str, float | int]:
    try:
        st = os.statvfs(root)
        blocks = st.f_blocks or 1
        files = st.f_files or 1
        used_pct = round((blocks - st.f_bfree) * 100.0 / blocks, 2)
        inodes_pct = round((files - st.f_ffree) * 100.0 / files, 2)
    except OSError:
        used_pct = 0.0
        inodes_pct = 0.0
    return {
        "node_tmp_stale_bytes": stale_junk_bytes(root, stale_hours, uid),
        "node_tmp_used_percent": used_pct,
        "node_tmp_inodes_percent": inodes_pct,
    }


def render(metrics: dict[str, float | int]) -> str:
    lines = [
        "# HELP node_tmp_stale_bytes Bytes in files owned by the configured user and older than the stale threshold under /tmp.",
        "# TYPE node_tmp_stale_bytes gauge",
        f"node_tmp_stale_bytes {metrics['node_tmp_stale_bytes']}",
        "# HELP node_tmp_used_percent /tmp filesystem block usage percentage from tmpfs_guard.plugin logic.",
        "# TYPE node_tmp_used_percent gauge",
        f"node_tmp_used_percent {metrics['node_tmp_used_percent']}",
        "# HELP node_tmp_inodes_percent /tmp inode usage percentage from tmpfs_guard.plugin logic.",
        "# TYPE node_tmp_inodes_percent gauge",
        f"node_tmp_inodes_percent {metrics['node_tmp_inodes_percent']}",
    ]
    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("--tmp-path", default=os.environ.get("SM_TMP_PATH", "/tmp"))
    parser.add_argument("--stale-hours", type=int, default=int(os.environ.get("SM_TMP_STALE_HOURS", "72")))
    parser.add_argument("--uid", type=int, default=int(os.environ.get("SM_TMP_UID", "1000")))
    parser.add_argument("--stdout", action="store_true")
    args = parser.parse_args()

    content = render(collect(Path(args.tmp_path), args.stale_hours, args.uid))
    if args.stdout:
        sys.stdout.write(content)
    else:
        write_atomic(Path(args.output_dir), METRIC_FILE, content)
    return 0


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