"""Tests for streaming transcript support."""

import json
import tempfile
from pathlib import Path

import pytest

try:
    import zstandard as zstd
    HAS_ZSTD = True
except ImportError:
    HAS_ZSTD = False

from autodebug_replay.transcripts import (
    ReplayDivergence,
    StreamingTranscriptIndex,
    TranscriptIndex,
    _estimate_transcript_size,
    _stream_jsonl_file,
    load_transcripts,
)


@pytest.fixture
def transcript_dir():
    """Create a temporary directory with test transcripts."""
    with tempfile.TemporaryDirectory() as tmpdir:
        path = Path(tmpdir)

        # Create test HTTP records
        http_records = [
            {"seq": 1, "type": "HTTP", "method": "GET", "url": "https://api.example.com/users"},
            {"seq": 3, "type": "HTTP", "method": "POST", "url": "https://api.example.com/users"},
        ]
        with open(path / "deps_http.jsonl", "w") as f:
            for r in http_records:
                f.write(json.dumps(r) + "\n")

        # Create test SQL records
        sql_records = [
            {"seq": 2, "type": "SQL", "query": "SELECT * FROM users"},
            {"seq": 4, "type": "SQL", "query": "INSERT INTO users (name) VALUES ('test')"},
        ]
        with open(path / "deps_sql.jsonl", "w") as f:
            for r in sql_records:
                f.write(json.dumps(r) + "\n")

        # Create empty events file
        (path / "events.jsonl").touch()

        yield path


@pytest.fixture
def compressed_transcript_dir():
    """Create a temporary directory with zstd compressed transcripts."""
    if not HAS_ZSTD:
        pytest.skip("zstandard not available")

    with tempfile.TemporaryDirectory() as tmpdir:
        path = Path(tmpdir)

        # Create test HTTP records (compressed)
        http_records = [
            {"seq": 1, "type": "HTTP", "method": "GET", "url": "https://api.example.com/users"},
            {"seq": 3, "type": "HTTP", "method": "POST", "url": "https://api.example.com/users"},
        ]
        cctx = zstd.ZstdCompressor()
        content = "\n".join(json.dumps(r) for r in http_records).encode()
        with open(path / "deps_http.jsonl.zst", "wb") as f:
            f.write(cctx.compress(content))

        yield path


class TestStreamJsonlFile:
    """Test streaming JSONL file reading."""

    def test_stream_plain_jsonl(self, transcript_dir):
        records = list(_stream_jsonl_file(transcript_dir / "deps_http.jsonl"))
        assert len(records) == 2
        assert records[0]["method"] == "GET"
        assert records[1]["method"] == "POST"

    def test_stream_nonexistent_file(self, transcript_dir):
        records = list(_stream_jsonl_file(transcript_dir / "nonexistent.jsonl"))
        assert records == []

    @pytest.mark.skipif(not HAS_ZSTD, reason="zstandard not available")
    def test_stream_compressed_jsonl(self, compressed_transcript_dir):
        records = list(_stream_jsonl_file(compressed_transcript_dir / "deps_http.jsonl"))
        assert len(records) == 2
        assert records[0]["method"] == "GET"


class TestTranscriptIndex:
    """Test standard TranscriptIndex."""

    def test_expect_returns_records_in_order(self, transcript_dir):
        index = load_transcripts(transcript_dir, use_streaming=False)

        http1 = index.expect("HTTP")
        assert http1["method"] == "GET"

        http2 = index.expect("HTTP")
        assert http2["method"] == "POST"

    def test_expect_raises_on_exhausted(self, transcript_dir):
        index = load_transcripts(transcript_dir, use_streaming=False)

        index.expect("HTTP")
        index.expect("HTTP")

        with pytest.raises(ReplayDivergence) as exc_info:
            index.expect("HTTP")

        assert exc_info.value.code == "MISSING_DEP"

    def test_has_more(self, transcript_dir):
        index = load_transcripts(transcript_dir, use_streaming=False)

        assert index.has_more("HTTP")
        index.expect("HTTP")
        assert index.has_more("HTTP")
        index.expect("HTTP")
        assert not index.has_more("HTTP")


class TestStreamingTranscriptIndex:
    """Test StreamingTranscriptIndex for large transcripts."""

    def test_expect_returns_records_in_order(self, transcript_dir):
        index = StreamingTranscriptIndex(transcript_dir)

        http1 = index.expect("HTTP")
        assert http1["method"] == "GET"

        http2 = index.expect("HTTP")
        assert http2["method"] == "POST"

    def test_expect_raises_on_exhausted(self, transcript_dir):
        index = StreamingTranscriptIndex(transcript_dir)

        index.expect("HTTP")
        index.expect("HTTP")

        with pytest.raises(ReplayDivergence) as exc_info:
            index.expect("HTTP")

        assert exc_info.value.code == "MISSING_DEP"

    def test_has_more(self, transcript_dir):
        index = StreamingTranscriptIndex(transcript_dir)

        assert index.has_more("HTTP")
        index.expect("HTTP")
        assert index.has_more("HTTP")
        index.expect("HTTP")
        assert not index.has_more("HTTP")

    def test_lazy_loading(self, transcript_dir):
        """Verify types are loaded lazily."""
        index = StreamingTranscriptIndex(transcript_dir)

        # Initially nothing is loaded
        assert len(index._loaded_types) == 0

        # Request HTTP - only HTTP should be loaded
        index.expect("HTTP")
        assert "HTTP" in index._loaded_types
        assert "SQL" not in index._loaded_types

        # Request SQL - now SQL should be loaded
        index.expect("SQL")
        assert "SQL" in index._loaded_types


class TestLoadTranscripts:
    """Test load_transcripts function."""

    def test_default_mode_for_small_transcripts(self, transcript_dir):
        index = load_transcripts(transcript_dir)
        # Small transcripts should use standard TranscriptIndex
        assert isinstance(index, TranscriptIndex)

    def test_force_streaming_mode(self, transcript_dir):
        index = load_transcripts(transcript_dir, use_streaming=True)
        assert isinstance(index, StreamingTranscriptIndex)

    def test_force_standard_mode(self, transcript_dir):
        index = load_transcripts(transcript_dir, use_streaming=False)
        assert isinstance(index, TranscriptIndex)


class TestEstimateTranscriptSize:
    """Test transcript size estimation."""

    def test_estimate_plain_files(self, transcript_dir):
        size = _estimate_transcript_size(transcript_dir)
        assert size > 0

    def test_estimate_empty_dir(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            size = _estimate_transcript_size(Path(tmpdir))
            assert size == 0
