import asyncio
import time

import pytest

from core.history_bridge import SessionHistoryBridge


class _FakeHistoryWriter:
    def __init__(self, *, add_delay_s: float = 0.0, add_result: bool = True):
        self.add_delay_s = add_delay_s
        self.add_result = add_result
        self.created_call_ids = []
        self.transcripts = []
        self.finalize_calls = 0
        self.finalize_statuses = []
        self.finalize_at = None
        self.last_transcript_at = None

    async def create_call_record(self, *, user_id: int, assistant_id: str | None, source: str = "debug"):
        _ = (user_id, assistant_id, source)
        call_id = "call_test_1"
        self.created_call_ids.append(call_id)
        return call_id

    async def add_transcript(
        self,
        *,
        call_id: str,
        turn_index: int,
        speaker: str,
        content: str,
        start_ms: int,
        end_ms: int,
        confidence: float | None = None,
        duration_ms: int | None = None,
    ) -> bool:
        _ = confidence
        if self.add_delay_s > 0:
            await asyncio.sleep(self.add_delay_s)
        self.transcripts.append(
            {
                "call_id": call_id,
                "turn_index": turn_index,
                "speaker": speaker,
                "content": content,
                "start_ms": start_ms,
                "end_ms": end_ms,
                "duration_ms": duration_ms,
            }
        )
        self.last_transcript_at = time.monotonic()
        return self.add_result

    async def finalize_call_record(self, *, call_id: str, status: str, duration_seconds: int) -> bool:
        _ = (call_id, duration_seconds)
        self.finalize_calls += 1
        self.finalize_statuses.append(status)
        self.finalize_at = time.monotonic()
        return True

@pytest.mark.asyncio
async def test_slow_backend_does_not_block_enqueue():
    writer = _FakeHistoryWriter(add_delay_s=0.15, add_result=True)
    bridge = SessionHistoryBridge(
        history_writer=writer,
        enabled=True,
        queue_max_size=32,
        retry_max_attempts=0,
        retry_backoff_sec=0.01,
        finalize_drain_timeout_sec=1.0,
    )

    try:
        call_id = await bridge.start_call(user_id=1, assistant_id="assistant_1", source="debug")
        assert call_id == "call_test_1"

        t0 = time.perf_counter()
        queued = bridge.enqueue_turn(role="user", text="hello world")
        elapsed_s = time.perf_counter() - t0

        assert queued is True
        assert elapsed_s < 0.02

        await bridge.finalize(status="connected")
        assert len(writer.transcripts) == 1
        assert writer.finalize_calls == 1
    finally:
        await bridge.shutdown()


@pytest.mark.asyncio
async def test_failing_backend_retries_but_enqueue_remains_non_blocking():
    writer = _FakeHistoryWriter(add_delay_s=0.01, add_result=False)
    bridge = SessionHistoryBridge(
        history_writer=writer,
        enabled=True,
        queue_max_size=32,
        retry_max_attempts=2,
        retry_backoff_sec=0.01,
        finalize_drain_timeout_sec=0.5,
    )

    try:
        await bridge.start_call(user_id=1, assistant_id="assistant_1", source="debug")
        t0 = time.perf_counter()
        assert bridge.enqueue_turn(role="assistant", text="retry me")
        elapsed_s = time.perf_counter() - t0
        assert elapsed_s < 0.02

        await bridge.finalize(status="connected")

        # Initial try + 2 retries
        assert len(writer.transcripts) == 3
        assert writer.finalize_calls == 1
    finally:
        await bridge.shutdown()


@pytest.mark.asyncio
async def test_finalize_is_idempotent_and_waits_for_queue_drain():
    writer = _FakeHistoryWriter(add_delay_s=0.05, add_result=True)
    bridge = SessionHistoryBridge(
        history_writer=writer,
        enabled=True,
        queue_max_size=32,
        retry_max_attempts=0,
        retry_backoff_sec=0.01,
        finalize_drain_timeout_sec=1.0,
    )

    try:
        await bridge.start_call(user_id=1, assistant_id="assistant_1", source="debug")
        assert bridge.enqueue_turn(role="user", text="first")

        ok_1 = await bridge.finalize(status="connected")
        ok_2 = await bridge.finalize(status="connected")

        assert ok_1 is True
        assert ok_2 is True
        assert len(writer.transcripts) == 1
        assert writer.finalize_calls == 1
        assert writer.last_transcript_at is not None
        assert writer.finalize_at is not None
        assert writer.finalize_at >= writer.last_transcript_at
    finally:
        await bridge.shutdown()
