"""
Notification Engine
Ported & adapted từ BrightBean Studio apps/notifications/engine.py
Chuyển từ Django → pure Python + JSON file storage (thay DB)
Single entry point: notify(event_type, title, body, data)
"""

import hashlib
import hmac
import json
import logging
import os
import urllib.request
from datetime import datetime, timezone

from .events import DEFAULT_CHANNELS, NON_CRITICAL, Channel, EventType

logger = logging.getLogger(__name__)

# ─── Config ────────────────────────────────────────────────────────────────
NOTIFICATIONS_FILE = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "..", "..", "data", "notifications.json"
)
MAX_NOTIFICATIONS = 500
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "canifa_webhook_secret")
SMTP_HOST    = os.getenv("SMTP_HOST", "")
SMTP_PORT    = int(os.getenv("SMTP_PORT", "587"))
SMTP_USER    = os.getenv("SMTP_USER", "")
SMTP_PASS    = os.getenv("SMTP_PASS", "")
ALERT_EMAIL  = os.getenv("ALERT_EMAIL", "")   # email nhận alerts
SLACK_WEBHOOK_URL = os.getenv("SLACK_WEBHOOK_URL", "")   # optional Slack hook

# Negative rate threshold để trigger NEGATIVE_SPIKE alert
NEGATIVE_SPIKE_THRESHOLD = float(os.getenv("NEGATIVE_SPIKE_THRESHOLD", "30.0"))

# ─── Storage ────────────────────────────────────────────────────────────────

def _ensure_dir():
    os.makedirs(os.path.dirname(NOTIFICATIONS_FILE), exist_ok=True)


def _load_notifications() -> list[dict]:
    _ensure_dir()
    if not os.path.exists(NOTIFICATIONS_FILE):
        return []
    try:
        with open(NOTIFICATIONS_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return []


def _save_notifications(data: list[dict]):
    _ensure_dir()
    with open(NOTIFICATIONS_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2, default=str)


def _append_notification(notif: dict) -> None:
    existing = _load_notifications()
    existing.insert(0, notif)
    _save_notifications(existing[:MAX_NOTIFICATIONS])


# ─── Core ────────────────────────────────────────────────────────────────────

def notify(
    event_type: str,
    title: str,
    body: str = "",
    data: dict | None = None,
    channels_override: list[str] | None = None,
) -> dict:
    """
    Tạo notification và dispatch tới các channels.
    Đây là single entry point — mọi module đều gọi hàm này.

    Args:
        event_type: Một trong EventType.*
        title: Tiêu đề ngắn gọn
        body: Nội dung chi tiết
        data: Metadata tuỳ ý (post_id, platform, ...)
        channels_override: Ghi đè default channels nếu cần
    """
    if event_type not in EventType.ALL:
        logger.warning("Unknown event_type: %s", event_type)

    notif = {
        "id": _gen_id(),
        "event_type": event_type,
        "title": title,
        "body": body,
        "data": data or {},
        "is_read": False,
        "created_at": datetime.now(timezone.utc).isoformat(),
    }

    # Persist in-app notification
    _append_notification(notif)

    # Determine channels
    channels = channels_override or _resolve_channels(event_type)

    # Dispatch
    for channel in channels:
        try:
            if channel == Channel.EMAIL and SMTP_USER and ALERT_EMAIL:
                _dispatch_email(notif)
            elif channel == Channel.WEBHOOK:
                _dispatch_webhook(notif)
        except Exception as e:
            logger.error("Notification dispatch failed [%s] %s: %s", channel, event_type, e)

    # Slack nếu có config và là critical event
    if SLACK_WEBHOOK_URL and event_type not in NON_CRITICAL:
        try:
            _dispatch_slack(notif)
        except Exception as e:
            logger.error("Slack dispatch failed: %s", e)

    logger.info("✅ Notification [%s]: %s", event_type, title)
    return notif


def _resolve_channels(event_type: str) -> list[str]:
    defaults = DEFAULT_CHANNELS.get(event_type, {})
    return [ch for ch, enabled in defaults.items() if enabled]


# ─── Dispatchers ─────────────────────────────────────────────────────────────

def _dispatch_email(notif: dict) -> None:
    """Gửi email alert qua SMTP."""
    import smtplib
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText

    msg = MIMEMultipart("alternative")
    msg["Subject"] = f"[Canifa AI] {notif['title']}"
    msg["From"]    = SMTP_USER
    msg["To"]      = ALERT_EMAIL

    text_part = MIMEText(
        f"{notif['title']}\n\n{notif['body']}\n\nEvent: {notif['event_type']}\nTime: {notif['created_at']}",
        "plain", "utf-8"
    )
    html_part = MIMEText(f"""
    <div style="font-family:Inter,sans-serif;max-width:600px;margin:0 auto;padding:24px">
      <div style="background:#4f46e5;color:white;padding:16px 20px;border-radius:8px 8px 0 0">
        <strong>Canifa AI Notification</strong>
      </div>
      <div style="border:1px solid #e5e7eb;border-top:none;padding:20px;border-radius:0 0 8px 8px">
        <h2 style="color:#1a1a2e;margin:0 0 8px">{notif['title']}</h2>
        <p style="color:#555;margin:0 0 16px">{notif.get('body','')}</p>
        <div style="background:#f9fafb;padding:12px;border-radius:6px;font-size:13px;color:#888">
          Event: <code>{notif['event_type']}</code><br>
          Time: {notif['created_at']}
        </div>
      </div>
    </div>
    """, "html", "utf-8")

    msg.attach(text_part)
    msg.attach(html_part)

    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
        server.starttls()
        server.login(SMTP_USER, SMTP_PASS)
        server.sendmail(SMTP_USER, ALERT_EMAIL, msg.as_string())

    logger.info("📧 Email sent to %s: %s", ALERT_EMAIL, notif["title"])


def _dispatch_webhook(notif: dict) -> None:
    """Gửi notification qua HMAC-signed HTTP POST webhook."""
    webhook_url = notif.get("data", {}).get("webhook_url") or os.getenv("NOTIFICATION_WEBHOOK_URL", "")
    if not webhook_url:
        return

    payload = json.dumps({
        "event_type": notif["event_type"],
        "title": notif["title"],
        "body": notif["body"],
        "data": notif["data"],
        "created_at": notif["created_at"],
    }, default=str).encode("utf-8")

    signature = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(), payload, hashlib.sha256
    ).hexdigest()

    req = urllib.request.Request(
        webhook_url,
        data=payload,
        headers={
            "Content-Type": "application/json",
            "X-Canifa-Signature": signature,
            "X-Event-Type": notif["event_type"],
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        if resp.status >= 400:
            raise RuntimeError(f"Webhook returned HTTP {resp.status}")

    logger.info("🔗 Webhook dispatched: %s", notif["event_type"])


def _dispatch_slack(notif: dict) -> None:
    """Gửi notification tới Slack channel qua Incoming Webhook."""
    emoji = {
        "negative_spike": "🚨",
        "content_failed": "❌",
        "content_published": "✅",
        "api_token_expired": "⚠️",
        "content_submitted": "📋",
        "ai_rules_updated": "🧠",
    }.get(notif["event_type"], "📢")

    payload = json.dumps({
        "text": f"{emoji} *{notif['title']}*\n{notif.get('body', '')}",
        "username": "Canifa AI",
        "icon_emoji": ":robot_face:",
    }).encode("utf-8")

    req = urllib.request.Request(
        SLACK_WEBHOOK_URL,
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=5):
        pass

    logger.info("💬 Slack notified: %s", notif["event_type"])


# ─── Helper ──────────────────────────────────────────────────────────────────

def _gen_id() -> str:
    import secrets
    return secrets.token_hex(8)


# ─── API helpers (dùng trong route) ──────────────────────────────────────────

def get_notifications(limit: int = 50, unread_only: bool = False) -> list[dict]:
    notifications = _load_notifications()
    if unread_only:
        notifications = [n for n in notifications if not n.get("is_read")]
    return notifications[:limit]


def mark_read(notification_id: str) -> bool:
    notifications = _load_notifications()
    for n in notifications:
        if n.get("id") == notification_id:
            n["is_read"] = True
            _save_notifications(notifications)
            return True
    return False


def mark_all_read() -> int:
    notifications = _load_notifications()
    count = sum(1 for n in notifications if not n.get("is_read"))
    for n in notifications:
        n["is_read"] = True
    _save_notifications(notifications)
    return count


def get_unread_count() -> int:
    return sum(1 for n in _load_notifications() if not n.get("is_read"))


# ─── Auto-check negative spike ───────────────────────────────────────────────

def check_negative_spike() -> bool:
    """
    Kiểm tra nếu tỷ lệ negative messages vượt threshold → gửi alert.
    Gọi từ social_inbox_route hoặc background task.
    Returns True nếu có spike.
    """
    try:
        from api.social_inbox.social_inbox_route import _load_messages as load_msgs
        messages = load_msgs()
    except Exception:
        return False

    if len(messages) < 10:  # Không đủ data để đánh giá
        return False

    recent = messages[:50]
    neg = sum(1 for m in recent if m.get("sentiment") == "negative")
    rate = neg / len(recent) * 100

    if rate >= NEGATIVE_SPIKE_THRESHOLD:
        notify(
            event_type=EventType.NEGATIVE_SPIKE,
            title=f"🚨 Negative spike: {rate:.1f}% ({neg}/{len(recent)} messages)",
            body=f"Tỷ lệ negative comments trên social media đang ở mức {rate:.1f}% "
                 f"(ngưỡng cảnh báo: {NEGATIVE_SPIKE_THRESHOLD}%). "
                 "AI learning loop đã được kích hoạt.",
            data={"negative_rate": rate, "sample_size": len(recent)},
        )
        return True
    return False
