import json
from datetime import datetime, timezone
from sqlalchemy import select
from app.core.config import settings
from app.core.database import SessionLocal
from app.core.security import decrypt_secret
from app.models.entities import Notification, PushSubscription, PushDelivery, MobileNotificationPreference

def now(): return datetime.now(timezone.utc)

def dispatch_pending_push(limit:int=200):
    if not settings.vapid_private_key or not settings.vapid_public_key:
        return {'status':'disabled','reason':'VAPID não configurado','delivered':0}
    try:
        from pywebpush import webpush, WebPushException
    except ImportError:
        return {'status':'disabled','reason':'pywebpush não instalado','delivered':0}
    db=SessionLocal(); delivered=0; failed=0
    try:
        notifications=db.scalars(select(Notification).order_by(Notification.created_at.desc()).limit(limit)).all()
        for n in notifications:
            pref=db.get(MobileNotificationPreference,n.user_id)
            if pref and not pref.push_enabled: continue
            subs=db.scalars(select(PushSubscription).where(PushSubscription.user_id==n.user_id,PushSubscription.active.is_(True))).all()
            for sub in subs:
                existing=db.scalar(select(PushDelivery).where(PushDelivery.notification_id==n.id,PushDelivery.subscription_id==sub.id))
                if existing and existing.status=='delivered': continue
                row=existing or PushDelivery(notification_id=n.id,subscription_id=sub.id)
                row.attempts+=1
                try:
                    subscription=json.loads(decrypt_secret(sub.encrypted_subscription))
                    response=webpush(subscription_info=subscription,data=json.dumps({'title':n.title,'body':n.body,'link':n.link,'notification_id':n.id}),vapid_private_key=settings.vapid_private_key,vapid_claims={'sub':settings.vapid_subject},ttl=3600)
                    row.status='delivered'; row.response_code=getattr(response,'status_code',201); row.delivered_at=now(); row.error=''; sub.last_success_at=now(); delivered+=1
                except Exception as exc:
                    row.status='failed'; row.error=str(exc)[:1000]; row.response_code=getattr(getattr(exc,'response',None),'status_code',0) or 0; sub.failure_count+=1; sub.last_failure_at=now(); failed+=1
                    if row.response_code in {404,410}: sub.active=False
                db.add(row)
        db.commit(); return {'status':'ok','delivered':delivered,'failed':failed}
    finally: db.close()
