"""
养号调度器 — 新号冷启动期自动限制操作量，渐进提升

功能:
  1. 新号冷启动期 (前 7 天) 自动限流
  2. 每日操作量递增曲线
  3. 模拟日常行为 (刷朋友圈、看文章、回消息)
  4. 活跃时段分布 (早 8-10, 午 12-14, 晚 19-22)
  5. 与 RiskSentinel 联动调整阈值
"""

import json
import logging
import os
import random
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple

logger = logging.getLogger(__name__)


class NurtureScheduler:
    """养号调度器 — 安全地养熟一个账号"""

    COLD_START_DAYS = 7

    DAILY_LIMITS_CURVE = {
        1: {"send_message": 5,  "add_friend": 2,  "moment_like": 5,  "moment_post": 0},
        2: {"send_message": 10, "add_friend": 3,  "moment_like": 8,  "moment_post": 1},
        3: {"send_message": 18, "add_friend": 5,  "moment_like": 12, "moment_post": 1},
        4: {"send_message": 25, "add_friend": 8,  "moment_like": 18, "moment_post": 2},
        5: {"send_message": 35, "add_friend": 12, "moment_like": 22, "moment_post": 2},
        6: {"send_message": 45, "add_friend": 15, "moment_like": 28, "moment_post": 3},
        7: {"send_message": 50, "add_friend": 18, "moment_like": 30, "moment_post": 3},
    }

    MATURE_LIMITS = {
        "send_message": 60, "add_friend": 20, "moment_like": 30,
        "moment_post": 5, "group_send": 10, "profile_view": 40,
    }

    ACTIVE_HOURS: List[Tuple[int, int, float]] = [
        (7, 9, 0.6),
        (9, 12, 0.8),
        (12, 14, 1.0),
        (14, 17, 0.7),
        (17, 19, 0.5),
        (19, 22, 1.0),
        (22, 24, 0.4),
    ]

    DAILY_BEHAVIORS = [
        {"action": "browse_moments", "weight": 3, "duration_min": (2, 8)},
        {"action": "read_article", "weight": 2, "duration_min": (1, 5)},
        {"action": "check_messages", "weight": 4, "duration_min": (1, 3)},
        {"action": "browse_discover", "weight": 1, "duration_min": (2, 6)},
    ]

    def __init__(self, state_file: str = "nurture_state.json"):
        self.state_file = state_file
        self._state = self._load_state()

    def register_account(self, account_id: str):
        """注册一个新的养号账号"""
        if account_id not in self._state:
            self._state[account_id] = {
                "start_date": datetime.now().isoformat(),
                "day_counters": {},
                "total_ops": {},
            }
            self._save_state()
            logger.info(f"注册养号: {account_id}")

    def get_account_day(self, account_id: str) -> int:
        """获取账号处于第几天"""
        info = self._state.get(account_id)
        if not info:
            return 999  # 未注册视为成熟号
        start = datetime.fromisoformat(info["start_date"])
        delta = (datetime.now() - start).days + 1
        return delta

    def is_cold_start(self, account_id: str) -> bool:
        """是否在冷启动期"""
        return self.get_account_day(account_id) <= self.COLD_START_DAYS

    def get_daily_limit(self, account_id: str, action: str) -> int:
        """获取该账号今日某操作的上限"""
        day = self.get_account_day(account_id)
        if day > self.COLD_START_DAYS:
            return self.MATURE_LIMITS.get(action, 100)
        day_limits = self.DAILY_LIMITS_CURVE.get(day, self.DAILY_LIMITS_CURVE[7])
        return day_limits.get(action, self.MATURE_LIMITS.get(action, 100))

    def can_operate(self, account_id: str, action: str) -> dict:
        """
        检查账号当前是否允许执行某操作

        Returns:
            {"allowed": bool, "reason": str, "day": int, "limit": int, "used": int}
        """
        day = self.get_account_day(account_id)
        limit = self.get_daily_limit(account_id, action)

        today_key = datetime.now().strftime("%Y-%m-%d")
        info = self._state.get(account_id, {})
        counters = info.get("day_counters", {}).get(today_key, {})
        used = counters.get(action, 0)

        if not self._is_active_hour():
            return {
                "allowed": False,
                "reason": "当前不在活跃时段",
                "day": day,
                "limit": limit,
                "used": used,
            }

        if used >= limit:
            return {
                "allowed": False,
                "reason": f"今日已达上限 ({used}/{limit})",
                "day": day,
                "limit": limit,
                "used": used,
            }

        return {
            "allowed": True,
            "reason": "",
            "day": day,
            "limit": limit,
            "used": used,
        }

    def record_operation(self, account_id: str, action: str):
        """记录一次操作"""
        if account_id not in self._state:
            self.register_account(account_id)

        today_key = datetime.now().strftime("%Y-%m-%d")
        info = self._state[account_id]

        if "day_counters" not in info:
            info["day_counters"] = {}
        if today_key not in info["day_counters"]:
            info["day_counters"][today_key] = {}

        counters = info["day_counters"][today_key]
        counters[action] = counters.get(action, 0) + 1

        if "total_ops" not in info:
            info["total_ops"] = {}
        info["total_ops"][action] = info["total_ops"].get(action, 0) + 1

        self._save_state()

    def get_nurture_plan(self, account_id: str) -> List[dict]:
        """
        生成今日养号行为计划 — 穿插在正式操作之间执行
        """
        plan = []
        hour = datetime.now().hour
        activity_weight = self._get_hour_weight(hour)

        behavior_count = max(1, int(random.uniform(2, 5) * activity_weight))
        selected = random.choices(
            self.DAILY_BEHAVIORS,
            weights=[b["weight"] for b in self.DAILY_BEHAVIORS],
            k=behavior_count,
        )

        for behavior in selected:
            dur_min = random.uniform(*behavior["duration_min"])
            delay_min = random.uniform(5, 30)
            plan.append({
                "action": behavior["action"],
                "duration_sec": int(dur_min * 60),
                "delay_before_sec": int(delay_min * 60),
                "hour_weight": activity_weight,
            })

        return plan

    def get_risk_sentinel_overrides(self, account_id: str) -> Dict[str, Tuple[int, int]]:
        """
        返回适合该账号当前阶段的 RiskSentinel 阈值覆盖

        用法: sentinel = RiskSentinel(custom_limits=scheduler.get_risk_sentinel_overrides(acct))
        """
        overrides = {}
        for action in self.MATURE_LIMITS:
            limit = self.get_daily_limit(account_id, action)
            overrides[action] = (3600, limit)
        return overrides

    def get_stats(self, account_id: str) -> dict:
        """获取养号统计"""
        info = self._state.get(account_id)
        if not info:
            return {"registered": False}

        day = self.get_account_day(account_id)
        today_key = datetime.now().strftime("%Y-%m-%d")
        counters = info.get("day_counters", {}).get(today_key, {})

        return {
            "registered": True,
            "account_id": account_id,
            "day": day,
            "cold_start": day <= self.COLD_START_DAYS,
            "today_counters": counters,
            "total_ops": info.get("total_ops", {}),
            "start_date": info["start_date"],
        }

    # ---- 内部方法 ----

    def _is_active_hour(self) -> bool:
        """当前是否在活跃时段"""
        hour = datetime.now().hour
        for start, end, weight in self.ACTIVE_HOURS:
            if start <= hour < end and weight >= 0.3:
                return True
        return False

    @staticmethod
    def _get_hour_weight(hour: int) -> float:
        """获取当前小时的活跃权重"""
        for start, end, weight in NurtureScheduler.ACTIVE_HOURS:
            if start <= hour < end:
                return weight
        return 0.1

    def _load_state(self) -> dict:
        if os.path.exists(self.state_file):
            try:
                with open(self.state_file, "r") as f:
                    return json.load(f)
            except Exception as e:
                logger.warning(f"加载养号状态失败: {e}")
        return {}

    def _save_state(self):
        try:
            with open(self.state_file, "w") as f:
                json.dump(self._state, f, ensure_ascii=False, indent=2)
        except Exception as e:
            logger.warning(f"保存养号状态失败: {e}")
