"""
风控哨兵 — 实时监控操作频率，行为随机化，触发阈值告警

功能:
  1. 操作频率控制 (消息/好友/群发 分开计数)
  2. 行为随机抖动 (间隔 +-15%~30%)
  3. 多级告警 (warn → throttle → pause)
  4. 冷却与恢复
"""

import logging
import random
import time
from collections import defaultdict
from typing import Dict, Optional, Tuple

logger = logging.getLogger(__name__)


class RiskSentinel:
    """风控哨兵 — 守护操作频率边界"""

    # 默认阈值: (时间窗口秒, 最大次数)
    DEFAULT_LIMITS: Dict[str, Tuple[int, int]] = {
        "send_message":   (3600, 60),
        "add_friend":     (3600, 20),
        "group_send":     (3600, 10),
        "moment_post":    (3600, 5),
        "moment_like":    (3600, 30),
        "profile_view":   (3600, 40),
        # ── 奥创协议补全写类频控（真机铁律：写类必须受频控约束）──
        "add_friend_in_room":        (3600, 20),
        "add_friend_from_phonebook": (3600, 20),
        "add_friend_with_scene":     (3600, 20),
        "add_friend_by_card":        (3600, 20),
        "send_friend_verify":        (3600, 20),
        "join_group_by_qr":          (3600, 10),
        "send_jielong":              (3600, 10),
        "send_multi_image":          (3600, 30),
        "mass_send":                 (3600, 10),
        "batch_send":                (3600, 10),
        "reply_moment_comment":      (3600, 30),
        "delete_moment_comment":     (3600, 30),
        "stop_moments_praise":       (3600, 20),
        "start_nurture":             (3600, 5),
        "detect_zombie_fans":        (3600, 5),
        "phone_action":              (3600, 10),
    }

    JITTER_RANGE = (0.15, 0.30)

    def __init__(self, custom_limits: Optional[Dict[str, Tuple[int, int]]] = None):
        self.limits = {**self.DEFAULT_LIMITS, **(custom_limits or {})}
        self._counters: Dict[str, list] = defaultdict(list)
        self._paused_until: Dict[str, float] = {}
        self._total_ops = 0

    def check(self, action: str) -> dict:
        """
        检查某操作是否允许执行。

        Returns:
            {"allowed": bool, "wait_sec": float, "level": "ok"|"warn"|"throttle"|"pause"}
        """
        now = time.time()

        if action in self._paused_until and now < self._paused_until[action]:
            remaining = self._paused_until[action] - now
            return {"allowed": False, "wait_sec": remaining, "level": "pause",
                    "reason": f"{action} 处于冷却期，剩余 {remaining:.0f}s"}

        window, max_count = self.limits.get(action, (3600, 100))
        timestamps = self._counters[action]
        cutoff = now - window
        timestamps[:] = [t for t in timestamps if t > cutoff]
        current = len(timestamps)

        if current >= max_count:
            pause_sec = random.uniform(300, 600)
            self._paused_until[action] = now + pause_sec
            logger.warning(f"🚨 {action} 达到上限 {max_count}/{window}s，暂停 {pause_sec:.0f}s")
            return {"allowed": False, "wait_sec": pause_sec, "level": "pause",
                    "reason": f"{action} 触发上限 ({current}/{max_count})"}

        ratio = current / max_count
        if ratio > 0.8:
            logger.warning(f"⚠️ {action} 接近上限 ({current}/{max_count})")
            return {"allowed": True, "wait_sec": 0, "level": "warn",
                    "reason": f"接近上限 {current}/{max_count}"}

        if ratio > 0.6:
            return {"allowed": True, "wait_sec": 0, "level": "throttle",
                    "reason": f"频率偏高 {current}/{max_count}"}

        return {"allowed": True, "wait_sec": 0, "level": "ok", "reason": ""}

    def can_operate(self, action: str = "send_message") -> bool:
        """兼容旧技能调用：保留风控判断，返回是否允许执行。"""
        result = self.check(action)
        return bool(result.get("allowed"))

    def record(self, action: str):
        """记录一次操作"""
        self._counters[action].append(time.time())
        self._total_ops += 1

    def add_jitter(self, base_delay: float) -> float:
        """给基准延迟加随机抖动"""
        jitter_pct = random.uniform(*self.JITTER_RANGE)
        direction = random.choice([-1, 1])
        return max(0.5, base_delay * (1 + direction * jitter_pct))

    def get_recommended_delay(self, action: str) -> float:
        """根据当前频率推荐操作间隔 (秒)"""
        window, max_count = self.limits.get(action, (3600, 100))
        now = time.time()
        cutoff = now - window
        recent = [t for t in self._counters[action] if t > cutoff]
        current = len(recent)

        ratio = current / max_count if max_count > 0 else 0

        if ratio > 0.8:
            base = random.uniform(30, 60)
        elif ratio > 0.5:
            base = random.uniform(10, 25)
        else:
            base = random.uniform(3, 8)

        return self.add_jitter(base)

    def reset(self, action: Optional[str] = None):
        """重置计数器"""
        if action:
            self._counters[action].clear()
            self._paused_until.pop(action, None)
        else:
            self._counters.clear()
            self._paused_until.clear()

    def get_stats(self) -> dict:
        """获取当前风控统计"""
        now = time.time()
        stats = {}
        for action, (window, max_count) in self.limits.items():
            cutoff = now - window
            recent = [t for t in self._counters[action] if t > cutoff]
            paused = action in self._paused_until and now < self._paused_until[action]
            stats[action] = {
                "count": len(recent),
                "limit": max_count,
                "window_sec": window,
                "paused": paused,
                "usage_pct": round(len(recent) / max_count * 100, 1) if max_count > 0 else 0,
            }
        stats["total_ops"] = self._total_ops
        return stats
