from __future__ import annotations

import time
from pathlib import Path
from typing import Any

from jarvis_gateway.adapters.base import BaseAdapter
from jarvis_gateway.adapters.health_sanitizer import sanitize_health_probe
from jarvis_gateway.contracts import ModuleId, ModuleSnapshot, utcnow
from jarvis_gateway.redaction import redact_error_message

MAX_SCAN_ENTRIES = 300
STALE_AFTER_SECONDS = 86400 * 3


class HealthLocalProbeAdapter(BaseAdapter):
    module_id = ModuleId.HEALTH
    title = "HealthManager"

    def __init__(self, runtime_base: str | None, legacy_dashboard_url: str | None = None) -> None:
        self.runtime_base = runtime_base
        self.legacy_dashboard_url = legacy_dashboard_url

    def get_snapshot(self) -> ModuleSnapshot:
        now = utcnow()
        raw = self._probe(now.timestamp())
        return sanitize_health_probe(raw, legacy_dashboard_url=self.legacy_dashboard_url, last_attempt_at=now)

    def _probe(self, now_ts: float) -> dict[str, Any]:
        if not self.runtime_base:
            return {"runtime_exists": False, "reachable": False, "pipeline_stale": True, "review_count": 0, "error": "HEALTH_RUNTIME_BASE is required for local_probe"}
        base = Path(self.runtime_base).expanduser()
        try:
            exists = base.exists()
            if not exists or not base.is_dir():
                return {"runtime_exists": False, "reachable": False, "pipeline_stale": True, "review_count": 0, "error": "Health runtime base missing or not a directory"}
            latest_mtime = 0.0
            total_count = 0
            extension_counts: dict[str, int] = {}
            for child in base.rglob("*"):
                if total_count >= MAX_SCAN_ENTRIES:
                    break
                try:
                    if not child.is_file():
                        continue
                    # Metadata only: stat + suffix, never read file content or names into output.
                    stat = child.stat()
                    latest_mtime = max(latest_mtime, stat.st_mtime)
                    suffix = child.suffix.lower() or "[none]"
                    extension_counts[suffix] = min(extension_counts.get(suffix, 0) + 1, 9999)
                    total_count += 1
                except OSError:
                    continue
            pipeline_seen = total_count > 0
            pipeline_stale = not latest_mtime or (now_ts - latest_mtime) > STALE_AFTER_SECONDS
            review_count = min(extension_counts.get(".review", 0) + extension_counts.get(".todo", 0), 9999)
            return {
                "runtime_exists": True,
                "reachable": True,
                "pipeline_seen": pipeline_seen,
                "pipeline_stale": pipeline_stale,
                "report_fresh": pipeline_seen and not pipeline_stale,
                "review_count": review_count,
                "file_count": min(total_count, 9999),
                "extension_count": min(len(extension_counts), 50),
                "last_metadata_mtime": latest_mtime or None,
                "last_success_at": now_ts,
            }
        except Exception as exc:  # noqa: BLE001 - converted to redacted degraded snapshot
            return {"runtime_exists": False, "reachable": False, "pipeline_stale": True, "review_count": 0, "error": redact_error_message(str(exc))}
