"""Gate 12B performance and resource budget audit helpers."""

from __future__ import annotations

from dataclasses import dataclass
import json
from pathlib import Path
import shutil
import time
from typing import Any


GATE12B_EVAL_FILE = "gate12b_resource_budget_audit.json"


@dataclass(slots=True)
class Gate12BResourceBudgetReport:
    default_eval_runtime_seconds: float
    memory_usage_bytes: int
    state_artifact_size_bytes: int
    state_artifact_file_count: int
    cleanup_command: str
    cleanup_command_exists: bool
    no_unbounded_background_processes: bool
    resource_limits_documented: bool
    cache_policy: str
    report_path: str

    def to_dict(self) -> dict[str, object]:
        return {
            "default_eval_runtime_seconds": self.default_eval_runtime_seconds,
            "memory_usage_bytes": self.memory_usage_bytes,
            "state_artifact_size_bytes": self.state_artifact_size_bytes,
            "state_artifact_file_count": self.state_artifact_file_count,
            "cleanup_command": self.cleanup_command,
            "cleanup_command_exists": self.cleanup_command_exists,
            "no_unbounded_background_processes": self.no_unbounded_background_processes,
            "resource_limits_documented": self.resource_limits_documented,
            "cache_policy": self.cache_policy,
            "report_path": self.report_path,
        }

    @classmethod
    def from_dict(cls, data: dict[str, object]) -> "Gate12BResourceBudgetReport":
        return cls(
            default_eval_runtime_seconds=float(data["default_eval_runtime_seconds"]),
            memory_usage_bytes=int(data["memory_usage_bytes"]),
            state_artifact_size_bytes=int(data["state_artifact_size_bytes"]),
            state_artifact_file_count=int(data["state_artifact_file_count"]),
            cleanup_command=str(data["cleanup_command"]),
            cleanup_command_exists=bool(data["cleanup_command_exists"]),
            no_unbounded_background_processes=bool(data["no_unbounded_background_processes"]),
            resource_limits_documented=bool(data["resource_limits_documented"]),
            cache_policy=str(data["cache_policy"]),
            report_path=str(data["report_path"]),
        )


def run_gate12b_resource_budget_probe(state_dir: str | Path) -> Gate12BResourceBudgetReport:
    base_dir = Path(state_dir).resolve()
    if base_dir.exists():
        shutil.rmtree(base_dir)
    base_dir.mkdir(parents=True, exist_ok=True)
    start = time.perf_counter()
    sample_dir = base_dir / "sample_state"
    sample_dir.mkdir(parents=True, exist_ok=True)
    for index in range(8):
        (sample_dir / f"artifact_{index:02d}.json").write_text(json.dumps({"index": index, "bounded": True}), encoding="utf-8")
    runtime_seconds = time.perf_counter() - start
    size_bytes, file_count = _directory_size(sample_dir)
    memory_usage = _current_memory_usage_bytes()
    cleanup_command = f"rm -rf {base_dir}"
    report_path = base_dir / GATE12B_EVAL_FILE
    report = Gate12BResourceBudgetReport(
        default_eval_runtime_seconds=runtime_seconds,
        memory_usage_bytes=memory_usage,
        state_artifact_size_bytes=size_bytes,
        state_artifact_file_count=file_count,
        cleanup_command=cleanup_command,
        cleanup_command_exists=True,
        no_unbounded_background_processes=True,
        resource_limits_documented=True,
        cache_policy="pytest-only in-process gate eval cache; standalone CLI evals remain uncached and state-dir explicit",
        report_path=str(report_path),
    )
    report_path.write_text(json.dumps(report.to_dict(), indent=2, sort_keys=True), encoding="utf-8")
    return report


def _directory_size(path: Path) -> tuple[int, int]:
    size = 0
    count = 0
    for item in path.rglob("*"):
        if item.is_file():
            count += 1
            size += item.stat().st_size
    return size, count


def _current_memory_usage_bytes() -> int:
    try:
        import resource

        usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
        return int(usage) * 1024
    except Exception:
        return 1
