"""الكواشف المدمجة — نفس منطق الوكيل الحالي مُعاد كموديولات تحت واجهة VDE.
تُختار حسب kind الميزة؛ config يقودها. تغطّي rule/edge_model/vlm والوضعين (event/stream).
"""
import time

from .api import Detection, Detector


class ZonePresence(Detector):
    """قاعدة وجود مكاني: وصل الحدث لأن الجسم دخل منطقة هذه الميزة (اسم المنطقة=المفتاح)."""
    kind = "rule"

    def on_event(self, ev, ctx):
        return Detection(flagged=True, label="zone",
                         note=ev.get("note", "دخول منطقة مراقَبة"),
                         snapshot=ctx.snapshot(ev.get("camera")))


class OnnxObject(Detector):
    """كشف أشياء بنموذج ONNX محلي (أسماك/PPE/جوال…). الأصناف من ONNX metadata."""
    kind = "edge_model"

    def setup(self, ctx):
        self.det = ctx.onnx() if ctx.model_path else None
        self.labels = ctx.config.get("detect_labels") or []
        self.min_conf = float(ctx.config.get("min_conf", 0.35))
        self.min_count = int(ctx.config.get("min_count", 1))

    def _analyze(self, frame, ctx):
        if not self.det or not frame:
            return None
        hits = self.det.detect(frame, conf=self.min_conf)
        if self.labels:
            hits = [h for h in hits if h[0] in self.labels]
        if len(hits) >= self.min_count:
            labs = sorted({h[0] for h in hits})
            return Detection(flagged=True, score=max(h[1] for h in hits),
                             note="كشف: " + "، ".join(labs), label=",".join(labs),
                             snapshot=frame)
        return None

    def on_frame(self, frame, ctx):                 # الوضع البثّي
        return self._analyze(frame, ctx)

    def on_event(self, ev, ctx):                    # الوضع الحدثي
        return self._analyze(ctx.snapshot(ev.get("camera")), ctx)


class CloudVLM(Detector):
    """كشف سلوكي عبر VLM سحابي (عراك/تنمّر/سقوط…). event=لقطات متتالية، stream=إطار مفرد."""
    kind = "vlm"

    def setup(self, ctx):
        self.n = int(ctx.config.get("frames", 4))
        self.gap = float(ctx.config.get("frame_gap_s", 0.4))

    def _emit(self, verdict, snapshot):
        if verdict.get("flagged"):
            return Detection(flagged=True, score=float(verdict.get("score", 0)),
                             note=verdict.get("note", ""), snapshot=snapshot)
        return None

    def on_event(self, ev, ctx):                    # مجدول/نشاط → لقطات متتالية (سياق زمني)
        frames = ctx.burst(ev.get("camera"), self.n, self.gap)
        if not frames:
            return None
        return self._emit(ctx.vlm(self.key, frames), frames[-1])

    def on_frame(self, frame, ctx):                 # بثّ → إطار مفرد
        return self._emit(ctx.vlm(self.key, [frame]), frame)


class Inactivity(Detector):
    """كشف الخمول (مزايا 2026): غياب أشخاص في الكاميرا لـ inactive_s ثانية → تنبيه.
    الأحداث تسجّل النشاط، والجدولة تفحص المدة. حارس ترك موقعه / كاش مهجور / خط توقّف."""
    kind = "rule"
    always_run = True                               # يجب أن يسجّل كل نشاط بلا تهدئة

    def setup(self, ctx):
        self.threshold = int(ctx.config.get("inactive_s", 300))
        self.interval_s = int(ctx.config.get("check_s", 30))   # كل كم يفحص
        self.last_activity = time.time()
        self.alerted = False

    def on_event(self, ev, ctx):
        if ev.get("trigger") == "schedule":
            idle = time.time() - self.last_activity
            if idle >= self.threshold and not self.alerted:
                self.alerted = True
                return Detection(flagged=True, score=round(idle), label="inactivity",
                                 note=f"لا نشاط في {ev.get('camera')} منذ "
                                      f"{int(idle // 60)} دقيقة",
                                 snapshot=ctx.snapshot(ev.get("camera")))
            return None
        self.last_activity = time.time()            # حدث نشاط → أعد الضبط
        self.alerted = False
        return None


class VehicleCrossing(Detector):
    """عبور/دخول مركبة لمنطقة (مزايا 2026): مركبة (car/truck) داخل منطقة الميزة → تنبيه.
    درايف‑ثرو، رصيف تحميل، مواقف، اقتحام مركبة للموقع."""
    kind = "rule"

    def setup(self, ctx):
        self.zone = ctx.config.get("zone") or self.key    # اسم منطقة العبور (=المفتاح)

    def on_event(self, ev, ctx):
        if self.zone in (ev.get("zones") or []):
            return Detection(flagged=True, label="vehicle",
                             note=f"مركبة داخل {self.zone}",
                             snapshot=ctx.snapshot(ev.get("camera")))
        return None


class Compound(Detector):
    """تنبيه مركّب (مزايا 2026 — Verkada compound): يُطلق فقط عند تحقق **كل** الشروط ضمن
    نافذة زمنية على نفس الكاميرا. مثال: weapon_detection + crowd_density، أو منطقة + بلا PPE.
    يُغذّى بإطلاقات الكواشف الأخرى عبر المحرك (لا trigger مباشر)."""
    kind = "rule"
    always_run = True

    def setup(self, ctx):
        self.conditions = list(ctx.config.get("conditions") or [])
        self.window = float(ctx.config.get("window_s", 30))
        self.seen = {}

    def on_event(self, ev, ctx):
        if ev.get("trigger") != "condition":
            return None
        now = time.time()
        self.seen[ev["fired_key"]] = now
        self.seen = {k: t for k, t in self.seen.items() if now - t <= self.window}
        if self.conditions and all(c in self.seen for c in self.conditions):
            self.seen.clear()
            return Detection(flagged=True, label="compound",
                             note="تنبيه مركّب: " + " + ".join(self.conditions),
                             snapshot=ctx.snapshot(ev.get("camera")))
        return None


class AudioAlert(Detector):
    """حدث صوتي (صراخ/زجاج/طلق ناري/إنذار) من كشف صوت المحرك → حدث أمني أول-درجة.
    يغذّي التنبيه المركّب (صوت+صورة) ويرفع درجة الإنذار الحقيقي (دليل تصعيد 2026)."""
    kind = "rule"

    def setup(self, ctx):
        self.labels = ctx.config.get("alert_labels") or [
            "scream", "yell", "glass_break", "gunshot", "fire_alarm"]

    def on_event(self, ev, ctx):
        label = ev.get("label", "")
        if label in self.labels:
            return Detection(flagged=True, label=label, score=1.0,
                             note=f"حدث صوتي: {label}",
                             snapshot=ctx.snapshot(ev.get("camera")))
        return None


# kind → صنف الكاشف المدمج
BUILTINS = {"rule": ZonePresence, "edge_model": OnnxObject, "vlm": CloudVLM}

# مفتاح ميزة → (صنف، دالة triggers) لمزايا VDE الخاصة (تتجاوز التصنيف بـ kind)
FEATURE_DETECTORS = {
    "inactivity": (Inactivity, lambda cfg: ["object:person", "motion", "schedule"]),
    "vehicle_crossing": (VehicleCrossing, lambda cfg: ["object:car", "object:truck"]),
    "compound_alert": (Compound, lambda cfg: ["schedule"]),   # يُغذّى بالشروط
    "audio_event": (AudioAlert, lambda cfg: ["audio"]),       # صوت من المحرك
}
