"""
Webhook通知插件
通过HTTP Webhook发送通知
"""

import asyncio
import hashlib
import hmac
import json
import time
from datetime import datetime
from typing import Any, Dict, List, Optional

try:
    import aiohttp

    AIOHTTP_AVAILABLE = True
except ImportError:
    AIOHTTP_AVAILABLE = False
    aiohttp = None

from src.core.logger import logger

from .base import Notification, NotificationLevel, NotificationPlugin


class WebhookNotificationPlugin(NotificationPlugin):
    """
    Webhook通知插件
    支持多种Webhook格式（Slack, Discord, 通用）
    """

    def __init__(self, name: str = "webhook", config: Dict[str, Any] = None):
        super().__init__(name, config)

        if not AIOHTTP_AVAILABLE:
            self.enabled = False
            logger.warning("aiohttp not installed, webhook plugin disabled")
            return

        # Webhook配置
        self.webhook_url = config.get("webhook_url") if config else None
        self.webhook_type = (
            config.get("webhook_type", "generic") if config else "generic"
        )  # generic, slack, discord, teams
        self.secret = config.get("secret") if config else None  # 用于签名
        self.timeout = config.get("timeout", 30) if config else 30
        self.headers = config.get("headers", {}) if config else {}

        # 缓冲配置
        self._buffer: List[Notification] = []
        self._lock = asyncio.Lock()
        self._session: Optional[aiohttp.ClientSession] = None
        self._flush_task = None

        if not self.webhook_url:
            self.enabled = False
            logger.warning("No webhook URL configured")
            return

        # 启动刷新任务
        self._start_flush_task()

    def _start_flush_task(self):
        """启动定时刷新任务"""

        async def flush_loop():
            while self.enabled:
                await asyncio.sleep(self.flush_interval)
                await self.flush()

        self._flush_task = asyncio.create_task(flush_loop())

    async def _get_session(self) -> aiohttp.ClientSession:
        """获取HTTP会话"""
        if not self._session:
            self._session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=self.timeout))
        return self._session

    def _generate_signature(self, payload: str) -> str:
        """生成请求签名"""
        if not self.secret:
            return ""

        # 使用HMAC-SHA256生成签名
        signature = hmac.new(
            self.secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256
        ).hexdigest()

        return signature

    def _format_for_slack(self, notification: Notification) -> Dict[str, Any]:
        """格式化为Slack消息"""
        # Slack颜色映射
        color_map = {
            NotificationLevel.INFO: "#36a64f",
            NotificationLevel.WARNING: "warning",
            NotificationLevel.ERROR: "danger",
            NotificationLevel.CRITICAL: "#ff0000",
        }

        return {
            "text": notification.title,
            "attachments": [
                {
                    "color": color_map.get(notification.level, "#808080"),
                    "title": notification.title,
                    "text": notification.message,
                    "fields": (
                        [
                            {"title": k, "value": str(v), "short": True}
                            for k, v in notification.metadata.items()
                        ]
                        if notification.metadata
                        else []
                    ),
                    "footer": notification.source,
                    "ts": int(notification.timestamp.timestamp()),
                }
            ],
        }

    def _format_for_discord(self, notification: Notification) -> Dict[str, Any]:
        """格式化为Discord消息"""
        # Discord颜色映射
        color_map = {
            NotificationLevel.INFO: 0x00FF00,
            NotificationLevel.WARNING: 0xFFA500,
            NotificationLevel.ERROR: 0xFF0000,
            NotificationLevel.CRITICAL: 0x8B0000,
        }

        embeds = [
            {
                "title": notification.title,
                "description": notification.message,
                "color": color_map.get(notification.level, 0x808080),
                "fields": (
                    [
                        {"name": k, "value": str(v), "inline": True}
                        for k, v in notification.metadata.items()
                    ]
                    if notification.metadata
                    else []
                ),
                "footer": {"text": notification.source},
                "timestamp": notification.timestamp.isoformat(),
            }
        ]

        return {"embeds": embeds}

    def _format_for_teams(self, notification: Notification) -> Dict[str, Any]:
        """格式化为Microsoft Teams消息"""
        # Teams颜色映射
        color_map = {
            NotificationLevel.INFO: "00ff00",
            NotificationLevel.WARNING: "ffa500",
            NotificationLevel.ERROR: "ff0000",
            NotificationLevel.CRITICAL: "8b0000",
        }

        facts = (
            [{"name": k, "value": str(v)} for k, v in notification.metadata.items()]
            if notification.metadata
            else []
        )

        return {
            "@type": "MessageCard",
            "@context": "https://schema.org/extensions",
            "themeColor": color_map.get(notification.level, "808080"),
            "title": notification.title,
            "text": notification.message,
            "sections": [{"facts": facts}] if facts else [],
            "summary": notification.title,
        }

    def _format_payload(self, notification: Notification) -> Dict[str, Any]:
        """根据Webhook类型格式化负载"""
        if self.webhook_type == "slack":
            return self._format_for_slack(notification)
        elif self.webhook_type == "discord":
            return self._format_for_discord(notification)
        elif self.webhook_type == "teams":
            return self._format_for_teams(notification)
        else:
            # 通用格式
            return notification.to_dict()

    async def _do_send(self, notification: Notification) -> bool:
        """
        实际发送单个通知

        Note: 对于 CRITICAL 级别通知，直接发送；其他级别加入缓冲区
        """
        # 添加到缓冲区
        async with self._lock:
            self._buffer.append(notification)

            # 如果是严重通知，立即发送
            if notification.level == NotificationLevel.CRITICAL:
                return await self._flush_buffer()

            # 如果缓冲区满，自动刷新
            if len(self._buffer) >= self.batch_size:
                return await self._flush_buffer()

        return True

    async def _do_send_batch(self, notifications: List[Notification]) -> Dict[str, Any]:
        """实际批量发送通知"""
        success_count = 0
        failed_count = 0
        errors = []

        if not notifications:
            return {"total": 0, "sent": 0, "failed": 0}

        # 批量发送
        for notification in notifications:
            try:
                payload = self._format_payload(notification)
                payload_str = json.dumps(payload)

                headers = dict(self.headers)
                headers["Content-Type"] = "application/json"

                # 添加签名
                if self.secret:
                    signature = self._generate_signature(payload_str)
                    headers["X-Signature"] = signature
                    headers["X-Timestamp"] = str(int(time.time()))

                # 发送请求
                session = await self._get_session()
                async with session.post(
                    self.webhook_url, data=payload_str, headers=headers
                ) as response:
                    if response.status < 300:
                        success_count += 1
                    else:
                        failed_count += 1
                        error_text = await response.text()
                        errors.append(f"HTTP {response.status}: {error_text}")

            except Exception as e:
                failed_count += 1
                errors.append(str(e))

        return {
            "total": len(notifications),
            "sent": success_count,
            "failed": failed_count,
            "errors": errors,
        }

    async def _flush_buffer(self) -> bool:
        """刷新缓冲的通知（内部方法，不带锁）"""
        if not self._buffer:
            return True

        notifications = self._buffer[:]
        self._buffer.clear()

        # 批量发送（直接调用 _do_send_batch 避免重复统计）
        result = await self._do_send_batch(notifications)
        return result["failed"] == 0

    async def flush(self) -> bool:
        """刷新缓冲的通知"""
        async with self._lock:
            return await self._flush_buffer()

    async def _get_extra_stats(self) -> Dict[str, Any]:
        """获取 Webhook 特定的统计信息"""
        return {
            "type": "webhook",
            "webhook_type": self.webhook_type,
            "webhook_url": (
                self.webhook_url.split("?")[0] if self.webhook_url else None
            ),  # 隐藏查询参数
            "buffer_size": len(self._buffer),
            "has_secret": bool(self.secret),
        }

    async def _do_shutdown(self):
        """清理资源"""
        await self.close()

    async def close(self):
        """关闭插件"""
        # 刷新缓冲
        await self.flush()

        # 取消刷新任务
        if self._flush_task:
            self._flush_task.cancel()

        # 关闭HTTP会话
        if self._session:
            await self._session.close()

    def __del__(self):
        """清理资源"""
        try:
            asyncio.create_task(self.close())
        except:
            pass
