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.autoshorts_sanitizer import sanitize_autoshorts_status
from jarvis_gateway.contracts import ModuleId, ModuleSnapshot, SourceType, utcnow

MAX_SCAN_ENTRIES = 300
STALE_AFTER_SECONDS = 86400 * 3
MEDIA_SUFFIXES = {".mp4", ".mov", ".wav", ".mp3", ".png", ".jpg", ".jpeg", ".webp", ".srt", ".ass"}


class AutoShortsLocalProbeAdapter(BaseAdapter):
    module_id = ModuleId.AUTOSHORTS
    title = "AutoShorts"

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

    def get_snapshot(self) -> ModuleSnapshot:
        now = utcnow()
        raw = self._probe(now.timestamp())
        return sanitize_autoshorts_status(raw, source_type=SourceType.LOCAL_PROBE, last_attempt_at=now, error=raw.get("error"))

    def _probe(self, now_ts: float) -> dict[str, Any]:
        if not self.runtime_base:
            return {"reachable": False, "pipeline_freshness": "unknown", "last_pipeline_status": "unknown", "error": "AUTOSHORTS_RUNTIME_BASE is required for local_probe"}
        base = Path(self.runtime_base).expanduser()
        try:
            if not base.exists() or not base.is_dir():
                return {"reachable": False, "pipeline_freshness": "unknown", "last_pipeline_status": "unknown", "error": "AutoShorts runtime base missing"}
            total = 0
            latest = 0.0
            status_markers = 0
            for child in base.rglob("*"):
                if total >= MAX_SCAN_ENTRIES:
                    break
                try:
                    if not child.is_file():
                        continue
                    suffix = child.suffix.lower()
                    # Metadata only. Never open/read media, captions, manifests or scripts.
                    stat = child.stat()
                    latest = max(latest, stat.st_mtime)
                    if suffix in {".status", ".ready", ".review", ".failed"}:
                        status_markers += 1
                    total += 1
                except OSError:
                    continue
            stale = not latest or (now_ts - latest) > STALE_AFTER_SECONDS
            return {
                "reachable": True,
                "candidate_count": min(total, 999),
                "pending_review_count": min(status_markers, 99),
                "ready_preview_count": 0,
                "failed_pipeline_count": 0,
                "last_pipeline_status": "stale" if stale else "review" if status_markers else "idle",
                "pipeline_freshness": "stale" if stale else "fresh",
                "publishing_status_category": "review_needed" if status_markers else "not_ready",
                "last_success_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(latest or now_ts)),
            }
        except Exception:
            return {"reachable": False, "pipeline_freshness": "unknown", "last_pipeline_status": "unknown", "error": "autoshorts_local_probe_failed"}
