"""Integration tests for events-only pipeline in evolution.

These tests verify that the events table correctly surfaces sessions
through the evolution pipeline when logs table is empty.

Per R8 (Real Wiring): Integration tests use real implementations at database boundary.
LLM calls are mocked for unit testing the pipeline logic.
"""

import tempfile
from pathlib import Path
from unittest.mock import patch

import pytest
from returns.result import Success

from lattice.core.types.evidence import CompilerOutput
from lattice.shell.evolution import (
    EvolutionConfig,
    query_pending_sessions,
    run_evolution,
)
from lattice.shell.schema import create_store
from lattice.shell.store import insert_event, set_metadata


@pytest.fixture
def store_conn():
    """Create a temporary store.db for testing."""
    with tempfile.TemporaryDirectory() as tmpdir:
        result = create_store(Path(tmpdir) / "store.db")
        assert isinstance(result, Success), f"Failed to create store: {result}"
        conn = result.unwrap()
        yield conn
        conn.close()


class TestEventsOnlyPipeline:
    """Test evolution pipeline with events-only data (no logs table entries)."""

    def test_evolution_pipeline_events_only(self, store_conn):
        """Real store.db with only events data should process sessions through evolution."""
        # Create real store.db with only events data (no logs)
        insert_event(store_conn, "events-session-1", "user", "User question about auth")
        insert_event(
            store_conn, "events-session-1", "assistant", "Assistant response about auth"
        )
        insert_event(
            store_conn,
            "events-session-2",
            "user",
            "User question about database connections",
        )
        insert_event(
            store_conn,
            "events-session-2",
            "assistant",
            "Assistant response about database",
        )
        insert_event(store_conn, "events-session-2", "tool", "read")

        # Verify events are queryable via query_pending_sessions
        pending_result = query_pending_sessions(store_conn)
        assert isinstance(pending_result, Success)
        pending = pending_result.unwrap()
        assert len(pending.sessions) == 2, (
            "Should have 2 sessions from events-only data"
        )
        session_ids = {s.session_id for s in pending.sessions}
        assert "events-session-1" in session_ids
        assert "events-session-2" in session_ids

        # Run evolution pipeline with mocked LLM
        with patch("lattice.shell.llm.llm_complete") as mock_llm:
            mock_llm.return_value = Success(
                "<triage>Processed 2 sessions from events</triage>"
                "<cross_ref>Found patterns</cross_ref>"
                "<synthesis>Generated proposals</synthesis>"
                "<review>Validated rules</review>"
            )

            # Create a minimal LLM config mock
            from dataclasses import dataclass

            @dataclass
            class MockConfig:
                model: str = "test-model"
                api_key_env: str = "TEST_API_KEY"
                base_url: str | None = None

            result = run_evolution(
                store_conn,
                prompt_template="<triage><cross_ref><synthesis><review>",
                current_rules=[],
                current_token_count=0,
                alert_tokens=3000,
                llm_config=MockConfig(),
            )

        # Assert evolution processed sessions
        assert isinstance(result, Success)
        output = result.unwrap()
        assert isinstance(output, CompilerOutput)
        assert output.sessions_processed > 0, (
            "Evolution should process events-only sessions"
        )
        assert output.sessions_processed == 2, "Should process both event sessions"

    def test_evolution_pipeline_incremental_events(self, store_conn):
        """Incremental evolution should respect last_evolved_at for events."""
        # Set up previous evolution timestamp
        set_metadata(store_conn, "last_evolved_at", "2026-02-17T10:00:00Z")

        # Insert old event (before last_evolved_at)
        # Using direct SQL to control timestamp
        store_conn.execute(
            "INSERT INTO events (external_id, session_id, timestamp, type, content) "
            "VALUES (?, ?, ?, ?, ?)",
            (
                "old-event-id",
                "old-events-session",
                "2026-02-17T09:00:00Z",
                "user",
                "Old event",
            ),
        )
        store_conn.commit()

        # Insert new events (after last_evolved_at) via insert_event
        insert_event(store_conn, "new-events-session", "user", "New question")

        # Query pending sessions
        pending_result = query_pending_sessions(store_conn)
        assert isinstance(pending_result, Success)
        pending = pending_result.unwrap()

        # Should only have the new session
        assert len(pending.sessions) == 1
        assert pending.sessions[0].session_id == "new-events-session"

    def test_evolution_pipeline_events_with_tool_metadata(self, store_conn):
        """Events with tool status are included in session logs (tool status stored but not yet queried)."""
        # Insert events with tool metadata
        insert_event(
            store_conn,
            "tool-events-session",
            "user",
            "User asked about file structure",
        )
        insert_event(
            store_conn,
            "tool-events-session",
            "tool",
            "read",
            tool_input="src/auth.py",
            tool_status="success",
        )
        insert_event(
            store_conn,
            "tool-events-session",
            "tool",
            "edit",
            tool_input="src/auth.py",
            tool_status="error",
            tool_error="Permission denied",
        )
        insert_event(
            store_conn,
            "tool-events-session",
            "assistant",
            "Assistant explained the error",
        )

        # Query pending sessions
        pending_result = query_pending_sessions(store_conn)
        assert isinstance(pending_result, Success)
        pending = pending_result.unwrap()

        # Should have 1 session with 4 entries
        assert len(pending.sessions) == 1
        session = pending.sessions[0]
        assert session.session_id == "tool-events-session"
        # 4 entries: user + tool(success) + tool(error) + assistant
        assert len(session.logs) == 4

    def test_evolution_pipeline_mixed_logs_and_events_incremental(self, store_conn):
        """Incremental evolution should merge logs and events based on timestamp."""
        from lattice.core.types.enums import Role
        from lattice.shell.store import insert_log

        # Set up previous evolution timestamp
        set_metadata(store_conn, "last_evolved_at", "2026-02-17T12:00:00Z")

        # Insert old log (before last_evolved_at)
        store_conn.execute(
            "INSERT INTO logs (external_id, session_id, timestamp, role, content, metadata) "
            "VALUES (?, ?, ?, ?, ?, ?)",
            (
                "old-log-id",
                "old-session",
                "2026-02-17T10:00:00Z",
                "user",
                "Old log message",
                "{}",
            ),
        )
        store_conn.commit()

        # Insert new log and event (after last_evolved_at)
        insert_log(store_conn, "new-mixed-session", Role.USER, "New log message")
        insert_event(store_conn, "new-mixed-session", "assistant", "New event message")

        # Query pending sessions
        pending_result = query_pending_sessions(store_conn)
        assert isinstance(pending_result, Success)
        pending = pending_result.unwrap()

        # Should have only the new session (old one excluded by timestamp)
        assert len(pending.sessions) == 1
        assert pending.sessions[0].session_id == "new-mixed-session"
        # Should have 2 entries: 1 from logs + 1 from events
        assert len(pending.sessions[0].logs) == 2
