"""Stress tests for large transcript handling (ED-04).

Tests system behavior with 10k+ segments to verify:
- Memory usage remains bounded
- get_meeting latency is acceptable
- Full transcript generation completes in reasonable time
"""

from __future__ import annotations

import time
from typing import TYPE_CHECKING, ClassVar

import pytest

from noteflow.domain.entities.meeting import Meeting
from noteflow.domain.entities.segment import Segment

if TYPE_CHECKING:
    from pathlib import Path

    from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

# Mark all tests in this module as stress tests
pytestmark = [pytest.mark.stress]


class TestLargeSegmentVolume:
    """Test meeting behavior with many segments."""

    SEGMENT_COUNTS: ClassVar[list[int]] = [1000, 5000, 10000]
    PERFORMANCE_THRESHOLD_SECONDS: ClassVar[float] = 5.0

    @pytest.mark.parametrize("segment_count", SEGMENT_COUNTS)
    def test_meeting_accumulates_many_segments(self, segment_count: int) -> None:
        """Test meeting can accumulate thousands of segments.

        Verifies that the in-memory Meeting entity handles
        large segment counts without errors.
        """
        meeting = Meeting.create(title="Giant Transcript Test")

        for i in range(segment_count):
            segment = Segment(
                segment_id=i,
                text=f"Segment number {i} with some representative text content.",
                start_time=float(i),
                end_time=float(i + 1),
                speaker_id="speaker_a" if i % 2 == 0 else "speaker_b",
            )
            meeting.add_segment(segment)

        assert meeting.segment_count == segment_count
        assert meeting.next_segment_id == segment_count

    @pytest.mark.parametrize("segment_count", SEGMENT_COUNTS)
    def test_full_transcript_performance(self, segment_count: int) -> None:
        """Test full_transcript generation completes within threshold.

        The full_transcript property concatenates all segment text.
        This must remain performant even with 10k+ segments.
        """
        meeting = Meeting.create(title="Transcript Performance Test")

        for i in range(segment_count):
            segment = Segment(
                segment_id=i,
                text=f"Segment {i}: Lorem ipsum dolor sit amet, consectetur adipiscing.",
                start_time=float(i),
                end_time=float(i + 1),
            )
            meeting.add_segment(segment)

        start = time.perf_counter()
        transcript = meeting.full_transcript
        elapsed = time.perf_counter() - start

        assert len(transcript) > 0
        assert elapsed < self.PERFORMANCE_THRESHOLD_SECONDS, (
            f"full_transcript took {elapsed:.2f}s with {segment_count} segments "
            f"(threshold: {self.PERFORMANCE_THRESHOLD_SECONDS}s)"
        )

    def test_segment_iteration_performance(self) -> None:
        """Test iterating over 10k segments is fast."""
        meeting = Meeting.create(title="Iteration Test")
        segment_count = 10000

        for i in range(segment_count):
            segment = Segment(
                segment_id=i,
                text=f"Text {i}",
                start_time=float(i),
                end_time=float(i + 1),
            )
            meeting.add_segment(segment)

        start = time.perf_counter()
        total_duration = sum(
            s.end_time - s.start_time for s in meeting.segments
        )
        elapsed = time.perf_counter() - start

        assert total_duration == segment_count
        assert elapsed < 1.0, f"Segment iteration took {elapsed:.2f}s"


class TestLargeSegmentPersistence:
    """Test database persistence with large segment counts."""

    @pytest.mark.integration
    async def test_meeting_with_many_segments_persists(
        self,
        postgressession_factory: async_sessionmaker[AsyncSession],
        meetings_dir: Path,
    ) -> None:
        """Test meeting with 1000 segments can be persisted and retrieved.

        Uses a smaller count (1000) for integration tests to keep
        test execution time reasonable while still validating the
        persistence layer handles bulk segment data.
        """
        from noteflow.infrastructure.persistence.unit_of_work import SqlAlchemyUnitOfWork

        segment_count = 1000

        # Create meeting with many segments
        meeting = Meeting.create(title="Persistence Volume Test")
        meeting.start_recording()
        meeting.begin_stopping()
        meeting.stop_recording()

        for i in range(segment_count):
            segment = Segment(
                segment_id=i,
                text=f"Persisted segment {i} with content.",
                start_time=float(i * 0.1),
                end_time=float((i + 1) * 0.1),
            )
            meeting.add_segment(segment)

        # Persist
        async with SqlAlchemyUnitOfWork(postgressession_factory, meetings_dir) as uow:
            await uow.meetings.create(meeting)
            for segment in meeting.segments:
                await uow.segments.add(meeting.id, segment)
            await uow.commit()

        # Retrieve and verify
        start = time.perf_counter()
        async with SqlAlchemyUnitOfWork(postgressession_factory, meetings_dir) as uow:
            retrieved = await uow.meetings.get(meeting.id)
            segments = await uow.segments.get_by_meeting(meeting.id)
        elapsed = time.perf_counter() - start

        assert retrieved is not None
        assert len(segments) == segment_count
        assert elapsed < 5.0, (
            f"Retrieving meeting with {segment_count} segments took {elapsed:.2f}s"
        )


class TestMemoryBoundedness:
    """Test that memory usage remains bounded with large datasets."""

    def test_segment_creation_memory_stable(self) -> None:
        """Test creating many segments doesn't cause memory explosion.

        This test creates and discards segments in batches to verify
        memory is properly garbage collected.
        """
        import gc
        import sys

        meeting = Meeting.create(title="Memory Test")
        batch_size = 1000
        num_batches = 10

        # Get baseline memory
        gc.collect()
        initial_size = sys.getsizeof(meeting.segments)

        for batch in range(num_batches):
            for i in range(batch_size):
                segment_id = batch * batch_size + i
                segment = Segment(
                    segment_id=segment_id,
                    text=f"Batch {batch} segment {i}",
                    start_time=float(segment_id),
                    end_time=float(segment_id + 1),
                )
                meeting.add_segment(segment)

        total_segments = batch_size * num_batches
        assert meeting.segment_count == total_segments

        # Verify list size grew (sanity check)
        final_size = sys.getsizeof(meeting.segments)
        assert final_size > initial_size
