"""درجة الإنذار الحقيقي + محرك التصعيد المؤكَّد (SOP) — موجة 2026 (Hakimo/Actuate).

- `true_alarm_score`: احتمال 0..1 أن الإنذار حقيقي = ثقة الكشف + حكم VLM + تاريخ المراجعة.
  الإنذارات ضعيفة الاحتمال لا تُصعَّد → خفض الإنذارات الكاذبة ومضاعفة سعة المراقبة.
- **تصعيد متدرّج**: كشف → Tier‑1 واتساب فوري → إن لم يُقَرّ خلال N دقيقة → Tier‑2 → إرسال
  بأدلة. خيط خلفي يفحص كل دقيقة. الإقرار (`/ack`) أو الرفض يوقف التصعيد.
"""
import threading
import time
from datetime import timedelta

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.orm import Session

from . import notifier
from .db import SessionLocal, get_db
from .models import Branch, Event, utcnow

router = APIRouter(prefix="/api/insights", tags=["escalation"])

# SOP افتراضي متدرّج (قابل للتخصيص لكل فرع لاحقاً)
DEFAULT_SOP = [
    {"tier": 1, "after_s": 0,    "action": "notify",   "label": "تنبيه"},
    {"tier": 2, "after_s": 300,  "action": "escalate", "label": "تصعيد (بلا إقرار 5د)"},
    {"tier": 3, "after_s": 900,  "action": "dispatch", "label": "للإرسال بأدلة (15د)"},
]
MIN_SCORE_TO_ESCALATE = 0.4     # دون هذا: إنذار ضعيف، لا يُصعَّد


def true_alarm_score(base_score: float, vlm_flagged: bool, confirm_rate: float) -> float:
    """0..1 — احتمال أن الإنذار حقيقي (base_score و confirm_rate مطبَّعان 0..1)."""
    clamp = lambda x: max(0.0, min(1.0, x))
    return round(0.45 * clamp(base_score) + 0.30 * (1.0 if vlm_flagged else 0.0)
                 + 0.25 * clamp(confirm_rate), 3)


def confirm_rate(db: Session, feature_key: str, camera_id) -> float:
    """نسبة التأكيد التاريخية للمراجَعة (confirmed/(confirmed+rejected)). محايد إن لا تاريخ."""
    q = select(Event).where(Event.event_type == feature_key, Event.reviewed.is_(True))
    if camera_id:
        q = q.where(Event.camera_id == camera_id)
    revd = [(e.meta or {}).get("review") for e in db.scalars(q.limit(100)).all()]
    revd = [r for r in revd if r in ("confirmed", "rejected")]
    if not revd:
        return 0.5
    return revd.count("confirmed") / len(revd)


def _recent_audio(db: Session, camera_id, window_s: int = 30) -> bool:
    """هل وقع حدث صوتي على نفس الكاميرا مؤخراً؟ (دليل تصعيد صوت+صورة)."""
    if not camera_id:
        return False
    since = utcnow() - timedelta(seconds=window_s)
    return db.scalar(select(Event).where(
        Event.event_type == "audio_event", Event.camera_id == camera_id,
        Event.detected_at >= since)) is not None


def score_event(db: Session, event: Event, vlm_flagged: bool) -> float:
    """يحسب درجة الإنذار الحقيقي ويخزّنها في meta.true_alarm_score.
    دليل صوتي متزامن (صوت+صورة) يرفع الثقة — نمط 2026 verified alarm."""
    base = float((event.meta or {}).get("score", 0) or 0)
    base = base / 10.0 if base > 1 else base          # قد تكون 0..10 أو 0..1
    s = true_alarm_score(base, vlm_flagged,
                         confirm_rate(db, event.event_type, event.camera_id))
    if event.event_type != "audio_event" and _recent_audio(db, event.camera_id):
        s = round(min(1.0, s + 0.15), 3)              # تعزيز بالدليل الصوتي
    event.meta = {**(event.meta or {}), "true_alarm_score": s}
    return s


def branch_sop(branch: Branch) -> dict:
    """SOP الفرع (tiers + min_score) أو الافتراضي."""
    cfg = (branch.sop or {}) if branch else {}
    return {"tiers": cfg.get("tiers") or DEFAULT_SOP,
            "min_score": float(cfg.get("min_score", MIN_SCORE_TO_ESCALATE))}


def due_action(age_s: float, last_tier: int, acked: bool, sop=DEFAULT_SOP):
    """أعلى خطوة SOP حان وقتها ولم تُنفَّذ بعد (None إن أُقِرّ أو لا شيء مستحق)."""
    if acked:
        return None
    due = None
    for step in sop:
        if step["after_s"] <= age_s and step["tier"] > last_tier:
            due = step
    return due


# ---------- الخيط الخلفي ----------

def _fire(db, event, branch, step):
    tier, label = step["tier"], step["label"]
    note = (event.meta or {}).get("ai_note") or event.event_type
    msg = (f"🚨 [{label}] {event.event_type} — {note}\n"
           f"احتمال إنذار حقيقي: {int(float((event.meta or {}).get('true_alarm_score',0))*100)}%")
    if step["action"] == "dispatch":
        msg += "\n➡️ يُنصح بالإرسال/الاتصال بالجهة المختصة (أدلة مرفقة)."
    path = None
    from .device import _snapshot_path            # داخل الدالة لتفادي دورة الاستيراد
    if event.snapshot_url:
        p = _snapshot_path(event.id)
        import os
        path = p if os.path.exists(p) else None
    if branch and branch.alert_numbers:
        notifier.send_alert(branch.alert_numbers, msg, path)
    event.meta = {**(event.meta or {}), "escalation_tier": tier}
    db.commit()


def _escalate_once():
    db = SessionLocal()
    try:
        since = utcnow() - timedelta(hours=2)
        evs = db.scalars(select(Event).where(
            Event.severity == "high", Event.detected_at >= since)).all()
        now = utcnow()
        branches, sops = {}, {}
        for e in evs:
            m = e.meta or {}
            if m.get("ack") or m.get("review") == "rejected":
                continue
            bid = m.get("branch_id")
            if bid and bid not in branches:
                branches[bid] = db.get(Branch, bid)
                sops[bid] = branch_sop(branches[bid])
            sop = sops.get(bid, {"tiers": DEFAULT_SOP, "min_score": MIN_SCORE_TO_ESCALATE})
            if float(m.get("true_alarm_score", 1.0)) < sop["min_score"]:
                continue                          # إنذار ضعيف → لا تصعيد
            age = (now - e.detected_at).total_seconds() if e.detected_at else 0
            step = due_action(age, int(m.get("escalation_tier", 0)), False, sop["tiers"])
            if step:
                _fire(db, e, branches.get(bid), step)
    finally:
        db.close()


def start_escalator():
    def loop():
        while True:
            try:
                _escalate_once()
            except Exception:
                pass
            time.sleep(60)
    threading.Thread(target=loop, daemon=True, name="veedon-escalator").start()


# ---------- حزمة الإنذار المؤكَّد (AVS-01) ----------

AVS_LABELS = {0: "لا معلومات", 1: "مصدر واحد", 2: "نشاط مؤكَّد متعدد",
              3: "تهديد مُتحقَّق", 4: "مؤكَّد بشرياً / جارٍ"}


def avs01_level(score: float, audio: bool, multi: bool, human: bool) -> int:
    """تصنيف AVS-01 (0..4) من الأدلة — معيار التحقّق للإرسال (TMA AVS-01)."""
    if human:
        return 4
    if score >= 0.7 and (audio or multi):
        return 3
    if score >= 0.5 or audio or multi:
        return 2
    if score >= 0.3:
        return 1
    return 0


def verified_package(db: Session, e: Event) -> dict:
    """حزمة إنذار مؤكَّد مُهيكلة (أدلة + خط زمني + مستوى AVS-01) — جاهزة للإرسال."""
    m = e.meta or {}
    score = float(m.get("true_alarm_score", 0))
    audio = _recent_audio(db, e.camera_id, 120)
    human = bool(m.get("ack") or m.get("review") == "confirmed")
    lo = (e.detected_at or utcnow()) - timedelta(minutes=2)
    hi = (e.detected_at or utcnow()) + timedelta(minutes=2)
    corr = [x for x in db.scalars(select(Event).where(
        Event.camera_id == e.camera_id,
        Event.detected_at >= lo, Event.detected_at <= hi)).all()
        if str(x.id) != str(e.id)]
    level = avs01_level(score, audio, bool(corr), human)
    return {
        "avs01_level": level, "level_label": AVS_LABELS[level],
        "event": {"id": str(e.id), "type": e.event_type, "severity": e.severity,
                  "at": e.detected_at.isoformat() if e.detected_at else None,
                  "snapshot_url": e.snapshot_url},
        "branch_id": m.get("branch_id"), "true_alarm_score": score,
        "evidence": {"audio": audio, "correlated_events": len(corr),
                     "timeline": [{"type": x.event_type,
                                   "at": x.detected_at.isoformat() if x.detected_at else None}
                                  for x in sorted(corr,
                                                  key=lambda z: z.detected_at or utcnow())]},
        "human_verified": human,
        "recommended": "dispatch" if level >= 3 else "verify" if level >= 2 else "monitor",
    }


@router.get("/verified/{event_id}")
def verified_alarm(event_id: str, db: Session = Depends(get_db)):
    """حزمة الإنذار المؤكَّد (AVS-01) لحدث — أدلة مُهيكلة للإرسال للجهة المختصة."""
    e = db.get(Event, event_id)
    if not e:
        raise HTTPException(404, "event not found")
    return verified_package(db, e)


@router.get("/sop/{branch_id}")
def get_sop(branch_id: str, db: Session = Depends(get_db)):
    """SOP التصعيد لهذا الفرع (أو الافتراضي إن لم يُخصَّص)."""
    b = db.get(Branch, branch_id)
    if not b:
        raise HTTPException(404, "branch not found")
    return branch_sop(b)


@router.put("/sop/{branch_id}")
def put_sop(branch_id: str, payload: dict, db: Session = Depends(get_db)):
    """تحديث SOP الفرع: {tiers:[{tier,after_s,action,label}], min_score:0..1}."""
    b = db.get(Branch, branch_id)
    if not b:
        raise HTTPException(404, "branch not found")
    tiers = payload.get("tiers") or DEFAULT_SOP
    b.sop = {"tiers": tiers,
             "min_score": max(0.0, min(1.0, float(payload.get("min_score", 0.4))))}
    db.commit()
    return branch_sop(b)


@router.post("/ack/{event_id}")
def ack(event_id: str, db: Session = Depends(get_db)):
    """إقرار الحدث — يوقف التصعيد (نمط verified alarm)."""
    e = db.get(Event, event_id)
    if not e:
        raise HTTPException(404, "event not found")
    e.meta = {**(e.meta or {}), "ack": True}
    e.reviewed = True
    db.commit()
    return {"acked": True, "event_id": event_id}
