# /home/Autopartes/pos/services/push_service.py
"""Web Push notification service for Nexus POS.

Uses the pywebpush library to send push notifications via the Web Push API.
VAPID keys are generated once and stored in the tenant config table.

Usage:
    from services.push_service import notify_owner

    # Non-blocking: fire-and-forget push to owner(s)
    notify_owner(conn, 'Venta Cancelada', f'Venta #{sale_id} cancelada por {reason}', '/pos')
"""

import json
import logging
import os
import traceback

logger = logging.getLogger(__name__)

# Try to import pywebpush — graceful degradation if not installed
try:
    from pywebpush import webpush, WebPushException
    HAS_WEBPUSH = True
except ImportError:
    HAS_WEBPUSH = False
    logger.warning("pywebpush not installed — push notifications disabled. Install with: pip install pywebpush")

try:
    from py_vapid import Vapid
    HAS_VAPID = True
except ImportError:
    HAS_VAPID = False


def get_or_create_vapid_keys(conn):
    """Get VAPID keys from config, or generate new ones.

    Returns: (private_key_pem, public_key_b64url) or (None, None) if unavailable.
    """
    if not HAS_VAPID and not HAS_WEBPUSH:
        return None, None

    cur = conn.cursor()

    # Check if keys already exist in config
    try:
        cur.execute("SELECT value FROM config WHERE key = 'vapid_private_key'")
        row = cur.fetchone()
        if row:
            private_key = row[0]
            cur.execute("SELECT value FROM config WHERE key = 'vapid_public_key'")
            pub_row = cur.fetchone()
            public_key = pub_row[0] if pub_row else None
            cur.close()
            if public_key:
                return private_key, public_key
    except Exception:
        pass  # config table might not exist yet

    # Generate new VAPID keys
    try:
        vapid = Vapid()
        vapid.generate_keys()
        private_pem = vapid.private_pem().decode('utf-8')
        public_b64 = vapid.public_key_urlsafe_base64()

        # Store in config table
        try:
            cur.execute("""
                INSERT INTO config (key, value) VALUES ('vapid_private_key', %s)
                ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value
            """, (private_pem,))
            cur.execute("""
                INSERT INTO config (key, value) VALUES ('vapid_public_key', %s)
                ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value
            """, (public_b64,))
            conn.commit()
        except Exception:
            conn.rollback()

        cur.close()
        return private_pem, public_b64
    except Exception as e:
        logger.error(f"Failed to generate VAPID keys: {e}")
        cur.close()
        return None, None


def save_subscription(conn, employee_id, subscription_json):
    """Save a push subscription for an employee.

    Args:
        conn: psycopg2 connection
        employee_id: int
        subscription_json: dict (the PushSubscription object from the browser)
    """
    cur = conn.cursor()
    sub_str = json.dumps(subscription_json) if isinstance(subscription_json, dict) else subscription_json

    # Upsert: one subscription per employee
    cur.execute("""
        INSERT INTO push_subscriptions (employee_id, subscription_data, created_at)
        VALUES (%s, %s, NOW())
        ON CONFLICT (employee_id) DO UPDATE
        SET subscription_data = EXCLUDED.subscription_data, created_at = NOW()
    """, (employee_id, sub_str))
    conn.commit()
    cur.close()


def send_push(conn, employee_id, title, body, url=None):
    """Send a push notification to a specific employee.

    Returns True on success, False on failure.
    """
    if not HAS_WEBPUSH:
        logger.debug("pywebpush not available, skipping push")
        return False

    cur = conn.cursor()

    # Get subscription
    cur.execute("SELECT subscription_data FROM push_subscriptions WHERE employee_id = %s", (employee_id,))
    row = cur.fetchone()
    if not row:
        cur.close()
        return False

    subscription = json.loads(row[0]) if isinstance(row[0], str) else row[0]

    # Get VAPID keys
    private_key, public_key = get_or_create_vapid_keys(conn)
    if not private_key:
        cur.close()
        return False

    cur.close()

    payload = json.dumps({
        'title': title,
        'body': body,
        'url': url or '/pos',
        'icon': '/pos/static/icons/icon-192.png',
        'badge': '/pos/static/icons/badge-72.png',
    })

    try:
        webpush(
            subscription_info=subscription,
            data=payload,
            vapid_private_key=private_key,
            vapid_claims={'sub': 'mailto:notificaciones@nexusautoparts.mx'},
        )
        return True
    except WebPushException as e:
        logger.warning(f"Push failed for employee {employee_id}: {e}")
        # If subscription expired, remove it
        if e.response and e.response.status_code in (404, 410):
            cur2 = conn.cursor()
            cur2.execute("DELETE FROM push_subscriptions WHERE employee_id = %s", (employee_id,))
            conn.commit()
            cur2.close()
        return False
    except Exception as e:
        logger.warning(f"Push error: {e}")
        return False


def notify_owner(conn, title, body, url=None):
    """Send push notification to all owner/admin employees (non-blocking, best-effort).

    This is the main integration point — call from sale cancellation, stock alerts, etc.
    """
    if not HAS_WEBPUSH:
        return

    cur = conn.cursor()
    try:
        cur.execute("""
            SELECT ps.employee_id
            FROM push_subscriptions ps
            JOIN employees e ON e.id = ps.employee_id
            WHERE e.role IN ('owner', 'admin') AND e.is_active = true
        """)
        employee_ids = [r[0] for r in cur.fetchall()]
        cur.close()
    except Exception:
        cur.close()
        return

    for eid in employee_ids:
        try:
            send_push(conn, eid, title, body, url)
        except Exception:
            pass  # Never let push failures affect business logic


def ensure_push_table(conn):
    """Create push_subscriptions table if it doesn't exist."""
    cur = conn.cursor()
    try:
        cur.execute("""
            CREATE TABLE IF NOT EXISTS push_subscriptions (
                id SERIAL PRIMARY KEY,
                employee_id INTEGER NOT NULL UNIQUE,
                subscription_data TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT NOW()
            )
        """)
        conn.commit()
    except Exception:
        conn.rollback()
    finally:
        cur.close()
