"""
Watchdog Alert System
======================
Routes alerts via Telegram and email with cooldown/escalation.

Escalation levels:
  1 — Log only, auto-restart attempted
  2 — Telegram alert to owner (3+ consecutive failures)
  3 — Telegram + email to admin (5+ failures or code errors)
  4 — Emergency: all services down, repeat alert every 60s
"""

from __future__ import annotations

import logging
import os
import smtplib
import time
from datetime import datetime, timezone
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pathlib import Path
from typing import Optional

import requests

logger = logging.getLogger("watchdog.alerting")

_PROJECT_ROOT = Path(__file__).resolve().parent.parent

try:
    from dotenv import load_dotenv
    load_dotenv(_PROJECT_ROOT / ".env")
except ImportError:
    pass

# Alert configuration
ALERT_COOLDOWN = int(os.getenv("ALERT_COOLDOWN_SECONDS", "300"))  # 5 min

# Telegram config (alert bot, same as webhook_sync)
ALERT_BOT_TOKEN = os.getenv("ALERT_BOT_TOKEN", "")
ADMIN_CHAT_ID = os.getenv("ADMIN_TELEGRAM_CHAT_ID", "") or os.getenv("OWNER_TELEGRAM_ID", "")

# Email config
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASS = os.getenv("SMTP_PASSWORD", "")
ADMIN_EMAIL = os.getenv("OWNER_EMAIL", "") or os.getenv("ADMIN_EMAIL", "")
FROM_EMAIL = os.getenv("AGENT_EMAIL", "") or SMTP_USER

# Support email — all error alerts are CC'd here for support tracking
SUPPORT_EMAIL = os.getenv("SUPPORT_EMAIL", "biznodesupport@1bz.biz")

# Node identity for alert context
NODE_NAME = os.getenv("BOT_NAME", "BizNode")
NODE_TIER = os.getenv("NODE_TIER", "basic")

# Cooldown tracking: {service: last_alert_timestamp}
_last_alert: dict[str, float] = {}
_last_recovery_alert: dict[str, float] = {}


def _in_cooldown(service: str, cooldown: int = ALERT_COOLDOWN) -> bool:
    """Check if we're still in cooldown for this service."""
    last = _last_alert.get(service, 0)
    return (time.time() - last) < cooldown


def _mark_alerted(service: str):
    _last_alert[service] = time.time()


# ---------------------------------------------------------------------------
# Telegram alerts
# ---------------------------------------------------------------------------

def send_telegram(message: str, parse_mode: str = "HTML") -> bool:
    """Send alert to admin Telegram chat + group (if configured)."""
    token = ALERT_BOT_TOKEN
    if not token:
        token = os.getenv("BOT_TOKEN", "") or os.getenv("TELEGRAM_BOT_TOKEN", "")
    if not token:
        logger.warning("No Telegram bot token for alerts")
        return False

    # Send to all configured destinations: owner DM + group
    destinations = set()
    if ADMIN_CHAT_ID:
        destinations.add(ADMIN_CHAT_ID)
    group_id = os.getenv("TELEGRAM_GROUP_ID", "")
    if group_id:
        destinations.add(group_id)
    if not destinations:
        logger.warning("No Telegram alert destinations configured")
        return False

    success = False
    for chat_id in destinations:
        try:
            r = requests.post(
                f"https://api.telegram.org/bot{token}/sendMessage",
                json={"chat_id": chat_id, "text": message, "parse_mode": parse_mode},
                timeout=10,
            )
            if r.status_code == 200 and r.json().get("ok"):
                success = True
            else:
                logger.error("Telegram alert to %s failed: %s", chat_id, r.text[:200])
        except Exception as exc:
            logger.error("Telegram alert to %s error: %s", chat_id, exc)
    return success


# ---------------------------------------------------------------------------
# Email alerts
# ---------------------------------------------------------------------------

def send_email_alert(subject: str, body: str) -> bool:
    """Send alert email to admin + support (biznodesupport@1bz.biz)."""
    if not SMTP_USER or not SMTP_PASS:
        logger.warning("SMTP not configured for alerts")
        return False

    recipients = []
    if ADMIN_EMAIL:
        recipients.append(ADMIN_EMAIL)
    if SUPPORT_EMAIL and SUPPORT_EMAIL not in recipients:
        recipients.append(SUPPORT_EMAIL)
    if not recipients:
        logger.warning("No alert email recipients configured")
        return False

    try:
        msg = MIMEMultipart()
        msg["From"] = FROM_EMAIL
        msg["To"] = ADMIN_EMAIL or SUPPORT_EMAIL
        if SUPPORT_EMAIL and ADMIN_EMAIL and SUPPORT_EMAIL != ADMIN_EMAIL:
            msg["Cc"] = SUPPORT_EMAIL
        msg["Subject"] = f"[BizNode Alert] {subject}"
        msg.attach(MIMEText(body, "plain"))

        server = smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=15)
        server.starttls()
        server.login(SMTP_USER, SMTP_PASS)
        server.sendmail(FROM_EMAIL, recipients, msg.as_string())
        server.quit()
        return True
    except Exception as exc:
        logger.error("Email alert error: %s", exc)
        return False


# ---------------------------------------------------------------------------
# Alert routing with escalation
# ---------------------------------------------------------------------------

def alert_service_down(
    service: str,
    error_type: str,
    error_detail: str,
    consecutive: int,
    recovery_action: str,
    recovery_success: bool,
):
    """
    Route an alert based on escalation level.

    Level 1: consecutive < 3  → Log only
    Level 2: consecutive 3-4  → Telegram
    Level 3: consecutive 5+   → Telegram + email
    Level 4: code errors      → Immediate Telegram + email
    """
    now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")

    # Level 1: Log only
    if consecutive < 3 and error_type not in ("name_error", "import_error", "syntax_error", "disk_full"):
        logger.info("[L1] %s down (%s) — auto-recovering (%s)", service, error_type, recovery_action)
        return

    # Check cooldown
    if _in_cooldown(service):
        logger.debug("Alert cooldown active for %s", service)
        return

    # Build alert message
    status_icon = "✅" if recovery_success else "❌"
    recovery_status = "Auto-recovered" if recovery_success else "Recovery FAILED"

    tg_msg = (
        f"🔴 <b>Service Down: {service.upper()}</b>\n\n"
        f"<b>Node:</b> {NODE_NAME} ({NODE_TIER})\n"
        f"<b>Error:</b> {error_type}\n"
        f"<b>Detail:</b> <code>{error_detail[:200]}</code>\n"
        f"<b>Consecutive failures:</b> {consecutive}\n"
        f"<b>Recovery:</b> {recovery_action} {status_icon}\n"
        f"<b>Status:</b> {recovery_status}\n"
        f"<b>Time:</b> {now}"
    )

    email_body = (
        f"BizNode Service Alert\n"
        f"{'=' * 40}\n\n"
        f"Node: {NODE_NAME} ({NODE_TIER})\n"
        f"Service: {service}\n"
        f"Error type: {error_type}\n"
        f"Detail: {error_detail[:500]}\n"
        f"Consecutive failures: {consecutive}\n"
        f"Recovery attempted: {recovery_action}\n"
        f"Recovery result: {recovery_status}\n"
        f"Time: {now}\n\n"
        f"--- Watchdog Daemon ---"
    )

    # Code errors or disk full → immediate escalation (Level 4)
    if error_type in ("name_error", "import_error", "syntax_error", "attribute_error", "disk_full"):
        logger.warning("[L4] %s — code/system error, escalating immediately", service)
        send_telegram(tg_msg)
        send_email_alert(f"{service.upper()} — {error_type} (NEEDS MANUAL FIX)", email_body)
        _mark_alerted(service)
        return

    # Level 2: Telegram only
    if consecutive < 5:
        logger.warning("[L2] %s down x%d — sending Telegram alert", service, consecutive)
        send_telegram(tg_msg)
        _mark_alerted(service)
        return

    # Level 3: Telegram + email
    logger.warning("[L3] %s down x%d — sending Telegram + email", service, consecutive)
    send_telegram(tg_msg)
    send_email_alert(f"{service.upper()} DOWN x{consecutive}", email_body)
    _mark_alerted(service)


def alert_all_down(services_down: list[str]):
    """Emergency: all critical services are down."""
    if _in_cooldown("__all_down__", cooldown=60):
        return

    tg_msg = (
        f"🚨🚨 <b>EMERGENCY: ALL SERVICES DOWN</b> 🚨🚨\n\n"
        f"<b>Node:</b> {NODE_NAME} ({NODE_TIER})\n"
        f"<b>Services:</b> {', '.join(services_down)}\n"
        f"<b>Time:</b> {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}\n\n"
        f"Manual intervention required!"
    )

    send_telegram(tg_msg)
    send_email_alert("EMERGENCY — ALL SERVICES DOWN",
                     f"All services are down: {', '.join(services_down)}\n"
                     f"Node: {NODE_NAME}\nManual intervention required.")
    _last_alert["__all_down__"] = time.time()


def alert_recovered(service: str, downtime_seconds: int):
    """Send recovery notification after a service comes back up."""
    if _in_cooldown(f"__recovered_{service}", cooldown=300):
        return

    minutes = downtime_seconds // 60
    seconds = downtime_seconds % 60
    duration = f"{minutes}m {seconds}s" if minutes else f"{seconds}s"

    tg_msg = (
        f"🟢 <b>Service Recovered: {service.upper()}</b>\n\n"
        f"<b>Node:</b> {NODE_NAME}\n"
        f"<b>Downtime:</b> {duration}\n"
        f"<b>Time:</b> {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}"
    )

    send_telegram(tg_msg)
    _last_alert[f"__recovered_{service}"] = time.time()
