"""
Bộ Đo Lường Sát Thương Thực Chiến (Realized Combat DPS Accumulator)
Tính toán DPS thực tế dựa trên delta HP của quái vật trong cửa sổ trượt (Sliding Window).
"""

import time
from collections import deque
from typing import Dict, List


class DPSAccumulator:
    """
    Bộ đo và tích lũy DPS thực chiến (Realized Combat DPS).
    Đo lường lượng máu quái vật thực sự bị trừ qua các tick thay vì dùng DPS lý thuyết trên tooltip.
    """

    def __init__(self, window_seconds: float = 3.0):
        self.window_seconds = window_seconds
        self.damage_events: deque = deque()  # (timestamp, damage_dealt)
        self.monster_hp_cache: Dict[int, int] = {}  # entity_id -> last_known_hp
        self.total_damage_dealt: int = 0
        self.combat_start_time: float = 0.0
        self.last_damage_time: float = 0.0
        self.peak_dps: float = 0.0

    def reset(self) -> None:
        """Đặt lại bộ đo DPS (khi đổi map hoặc bắt đầu phiên mới)."""
        self.damage_events.clear()
        self.monster_hp_cache.clear()
        self.total_damage_dealt = 0
        self.combat_start_time = 0.0
        self.last_damage_time = 0.0
        self.peak_dps = 0.0

    def update_monsters_snapshot(self, monsters: List[Dict[str, int]], current_time: float = 0.0) -> float:
        """
        Cập nhật danh sách quái vật từ snapshot hiện tại.
        monsters: List of dicts, e.g. [{"id": 101, "hp": 450, "max_hp": 1000}, ...]
        Trả về DPS tức thời trong cửa sổ trượt.
        """
        now = current_time or time.time()
        current_frame_ids = set()

        for m in monsters:
            m_id = m.get("id", 0)
            cur_hp = m.get("hp", 0)
            current_frame_ids.add(m_id)

            if m_id in self.monster_hp_cache:
                prev_hp = self.monster_hp_cache[m_id]
                if cur_hp < prev_hp:
                    damage = prev_hp - cur_hp
                    self._record_damage(now, damage)
            self.monster_hp_cache[m_id] = cur_hp

        # Dọn dẹp quái vật đã biến mất khỏi tầm nhìn hoặc đã chết
        cached_ids = list(self.monster_hp_cache.keys())
        for c_id in cached_ids:
            if c_id not in current_frame_ids:
                last_hp = self.monster_hp_cache[c_id]
                if 0 < last_hp < 2000:
                    self._record_damage(now, last_hp)
                del self.monster_hp_cache[c_id]

        return self.get_current_dps(now)

    def _record_damage(self, timestamp: float, damage: int) -> None:
        if damage <= 0:
            return
        if self.combat_start_time == 0.0:
            self.combat_start_time = timestamp
        self.last_damage_time = timestamp
        self.total_damage_dealt += damage
        self.damage_events.append((timestamp, damage))

    def get_current_dps(self, current_time: float = 0.0) -> float:
        """Tính DPS trong cửa sổ trượt (window_seconds)."""
        now = current_time or time.time()
        cutoff = now - self.window_seconds

        while self.damage_events and self.damage_events[0][0] < cutoff:
            self.damage_events.popleft()

        if not self.damage_events:
            return 0.0

        window_damage = sum(dmg for _, dmg in self.damage_events)
        elapsed = max(0.5, now - self.damage_events[0][0])
        current_dps = window_damage / min(elapsed, self.window_seconds)

        if current_dps > self.peak_dps:
            self.peak_dps = current_dps

        return float(round(current_dps, 1))

    def get_average_combat_dps(self) -> float:
        """Tính DPS trung bình từ đầu trận giao chiến tới giờ."""
        if self.combat_start_time == 0.0 or self.total_damage_dealt == 0:
            return 0.0
        duration = max(1.0, self.last_damage_time - self.combat_start_time)
        return float(round(self.total_damage_dealt / duration, 1))
