import time as _time
from unittest.mock import MagicMock, patch

from awe.mq.mq_producer import MQProducer, MQProducerOptions
from awe.types.common import Payload, ResponseMessage

def _build_payload(task_id: str):
    return Payload(
        taskId=task_id,
        userId='user-1',
        clientId='client-1',
        workflowCode='wf-1',
        messageTag='APP',
    )

def _build_progress_message(message_type='progress', debounced=False):
    return ResponseMessage({
        'type': 'PROGRESS',
        'debounced': debounced,
        'response': {
            'progress': 10,
            'progressData': {
                'type': message_type,
            },
        },
    })

def _make_producer(monkeypatch, throttle_seconds=1, global_max_qps=0):
    """创建一个 mock 了底层 MQ 客户端的 MQProducer"""
    options = MQProducerOptions(
        progress_throttle_seconds=throttle_seconds,
        global_max_qps=global_max_qps,
    )
    monkeypatch.setattr(MQProducer, 'get_producer', lambda self, opts: MagicMock())
    producer = MQProducer(options)
    producer.publish_message = MagicMock()
    return producer


def test_send_progress_message_throttles_same_task(monkeypatch):
    """同一 task 的进度消息在节流窗口内应被抑制"""
    producer = _make_producer(monkeypatch, throttle_seconds=1)

    timestamps = iter([0.0, 0.2, 1.2])
    monkeypatch.setattr('awe.mq.mq_producer.time.monotonic', lambda: next(timestamps))

    payload = _build_payload('task-1')
    message = _build_progress_message()

    producer.send_progress_message(message, payload)
    producer.send_progress_message(message, payload)
    producer.send_progress_message(message, payload)

    assert producer.publish_message.call_count == 2


def test_send_progress_message_does_not_cross_throttle_tasks(monkeypatch):
    """不同 task 的进度消息互不影响节流"""
    producer = _make_producer(monkeypatch, throttle_seconds=1)

    timestamps = iter([0.0, 0.1])
    monkeypatch.setattr('awe.mq.mq_producer.time.monotonic', lambda: next(timestamps))

    producer.send_progress_message(_build_progress_message(), _build_payload('task-1'))
    producer.send_progress_message(_build_progress_message(), _build_payload('task-2'))

    assert producer.publish_message.call_count == 2


def test_send_complete_message_clears_progress_state(monkeypatch):
    """complete 消息应清除该 task 的节流状态，使后续进度消息可以立即发送"""
    producer = _make_producer(monkeypatch, throttle_seconds=1)

    timestamps = iter([0.0, 0.1])
    monkeypatch.setattr('awe.mq.mq_producer.time.monotonic', lambda: next(timestamps))

    payload = _build_payload('task-1')
    producer.send_progress_message(_build_progress_message(), payload)
    producer.send_complete_message(ResponseMessage({'type': 'COMPLETED', 'response': {'data': {}}}), payload)
    producer.send_progress_message(_build_progress_message(), payload)

    assert producer.publish_message.call_count == 3


def test_non_debounced_progress_also_throttled(monkeypatch):
    """非 debounced 的进度消息也应走节流，不再直接发送"""
    producer = _make_producer(monkeypatch, throttle_seconds=1)

    timestamps = iter([0.0, 0.2])
    monkeypatch.setattr('awe.mq.mq_producer.time.monotonic', lambda: next(timestamps))

    payload = _build_payload('task-1')
    message = _build_progress_message(debounced=False)

    producer.send_progress_message(message, payload)
    producer.send_progress_message(message, payload)

    assert producer.publish_message.call_count == 1


def test_trailing_message_fires_after_throttle_window(monkeypatch):
    """被节流的最后一条消息应在窗口结束后自动补发（trailing）"""
    producer = _make_producer(monkeypatch, throttle_seconds=0.1)

    call_count_before = producer.publish_message.call_count
    payload = _build_payload('task-1')

    producer.send_progress_message(_build_progress_message(), payload)
    assert producer.publish_message.call_count == call_count_before + 1

    producer.send_progress_message(_build_progress_message(), payload)
    # 第二条被节流，不会立即发送
    assert producer.publish_message.call_count == call_count_before + 1

    # 等待 trailing timer 触发
    _time.sleep(0.3)
    assert producer.publish_message.call_count == call_count_before + 2


def test_global_qps_limit(monkeypatch):
    """全局 QPS 限制应在超出时丢弃消息"""
    # global_max_qps=2 意味着最小间隔 0.5s
    options = MQProducerOptions(progress_throttle_seconds=0, global_max_qps=2)
    monkeypatch.setattr(MQProducer, 'get_producer', lambda self, opts: MagicMock())
    producer = MQProducer(options)
    underlying_publish = MagicMock()
    producer.producer.publish_message = underlying_publish

    # throttle_seconds=0 时 _throttled_progress_publish 不调用 time.monotonic，
    # 每条消息只在 _check_global_rate 中调用一次 time.monotonic
    # msg1: global=0.0(通过, 首次)
    # msg2: global=0.1(拒绝, 0.1-0.0<0.5)
    # msg3: global=0.6(通过, 0.6-0.0>=0.5)
    timestamps = iter([0.0, 0.1, 0.6])
    monkeypatch.setattr('awe.mq.mq_producer.time.monotonic', lambda: next(timestamps))

    payload = _build_payload('task-1')
    message = _build_progress_message()

    producer.send_progress_message(message, payload)
    producer.send_progress_message(message, payload)
    producer.send_progress_message(message, payload)

    # 第 1 条通过，第 2 条被全局限流丢弃，第 3 条通过
    assert underlying_publish.call_count == 2