"""
Tests for framework integrations — LangChain, CrewAI, AutoGen, OpenAI Agents.

All tests use the SQLite backend (injected via backend= param) to verify
data actually persists, NOT the in-memory mock.
"""

import os
import pytest


class TestLangChainIntegration:

    def test_default_backend_is_not_mock(self, tmp_dir, monkeypatch):
        monkeypatch.setenv("SYNRIX_BACKEND", "sqlite")
        monkeypatch.setenv("SYNRIX_DATA_DIR", tmp_dir)
        from synrix_runtime.integrations.langchain_memory import SynrixMemory
        mem = SynrixMemory(agent_id="lc_test")
        assert mem.backend.backend_type != "mock"

    def test_save_and_load_context(self, agent_backend):
        from synrix_runtime.integrations.langchain_memory import SynrixMemory
        mem = SynrixMemory(agent_id="lc_test", backend=agent_backend)

        mem.save_context({"input": "Hello"}, {"output": "Hi there!"})
        mem.save_context({"input": "How are you?"}, {"output": "I'm good!"})

        variables = mem.load_memory_variables({})
        history = variables["history"]
        assert "Hello" in history
        assert "Hi there!" in history

    def test_get_full_history(self, agent_backend):
        from synrix_runtime.integrations.langchain_memory import SynrixMemory
        mem = SynrixMemory(agent_id="lc_hist", backend=agent_backend)

        mem.save_context({"input": "A"}, {"output": "B"})
        mem.save_context({"input": "C"}, {"output": "D"})

        history = mem.get_full_history()
        assert len(history) == 2

    def test_restore_from_crash(self, agent_backend):
        from synrix_runtime.integrations.langchain_memory import SynrixMemory
        mem1 = SynrixMemory(agent_id="lc_crash", backend=agent_backend)
        mem1.save_context({"input": "X"}, {"output": "Y"})
        mem1.save_context({"input": "W"}, {"output": "Z"})

        # Simulate crash: new instance, same backend
        mem2 = SynrixMemory(agent_id="lc_crash", backend=agent_backend)
        count = mem2.restore_from_crash()
        assert count == 2

    def test_entity_storage(self, agent_backend):
        from synrix_runtime.integrations.langchain_memory import SynrixMemory
        mem = SynrixMemory(agent_id="lc_entity", backend=agent_backend)

        mem.store_entity("John", {"role": "CEO", "company": "Acme"})
        entity = mem.get_entity("John")
        assert entity is not None
        assert entity.get("role") == "CEO"


class TestCrewAIIntegration:

    def test_default_backend_is_not_mock(self, tmp_dir, monkeypatch):
        monkeypatch.setenv("SYNRIX_BACKEND", "sqlite")
        monkeypatch.setenv("SYNRIX_DATA_DIR", tmp_dir)
        from synrix_runtime.integrations.crewai_memory import SynrixCrewMemory
        mem = SynrixCrewMemory(crew_id="crew_test")
        assert mem.backend.backend_type != "mock"

    def test_store_and_get_finding(self, agent_backend):
        from synrix_runtime.integrations.crewai_memory import SynrixCrewMemory
        mem = SynrixCrewMemory(crew_id="crew_1", backend=agent_backend)

        mem.store_finding("researcher", "market_size", {"value": "$4.2B"})
        finding = mem.get_finding("market_size")
        assert finding is not None
        assert finding.get("value") == "$4.2B"

    def test_crew_snapshot_and_restore(self, agent_backend):
        from synrix_runtime.integrations.crewai_memory import SynrixCrewMemory
        mem = SynrixCrewMemory(crew_id="crew_snap", backend=agent_backend)

        mem.store_finding("analyst", "growth_rate", {"value": "15%"})
        snap = mem.crew_snapshot("test_snap")
        assert snap["items"] >= 1

        result = mem.crew_restore("test_snap")
        assert result["restored"]


class TestAutoGenIntegration:

    def test_default_backend_is_not_mock(self, tmp_dir, monkeypatch):
        monkeypatch.setenv("SYNRIX_BACKEND", "sqlite")
        monkeypatch.setenv("SYNRIX_DATA_DIR", tmp_dir)
        from synrix_runtime.integrations.autogen_memory import SynrixAutoGenMemory
        mem = SynrixAutoGenMemory(group_id="ag_test")
        assert mem.backend.backend_type != "mock"

    def test_store_and_retrieve_messages(self, agent_backend):
        from synrix_runtime.integrations.autogen_memory import SynrixAutoGenMemory
        mem = SynrixAutoGenMemory(group_id="ag_1", backend=agent_backend)

        mem.store_message("alice", "bob", "Hello Bob")
        mem.store_message("bob", "alice", "Hi Alice")

        history = mem.get_conversation_history()
        assert len(history) == 2

    def test_search_conversations(self, agent_backend):
        from synrix_runtime.integrations.autogen_memory import SynrixAutoGenMemory
        mem = SynrixAutoGenMemory(group_id="ag_search", backend=agent_backend)

        mem.store_message("alice", "bob", "The quarterly revenue is $10M")
        mem.store_message("bob", "alice", "Thanks for the update")

        matches = mem.search_conversations("revenue")
        assert len(matches) == 1

    def test_get_stats(self, agent_backend):
        from synrix_runtime.integrations.autogen_memory import SynrixAutoGenMemory
        mem = SynrixAutoGenMemory(group_id="ag_stats", backend=agent_backend)

        mem.store_message("a1", "a2", "msg1")
        mem.store_message("a2", "a1", "msg2")

        stats = mem.get_stats()
        assert stats["total_messages"] == 2
        assert stats["unique_agents"] == 2


class TestOpenAIAgentsIntegration:

    def test_default_backend_is_not_mock(self, tmp_dir, monkeypatch):
        monkeypatch.setenv("SYNRIX_BACKEND", "sqlite")
        monkeypatch.setenv("SYNRIX_DATA_DIR", tmp_dir)
        from synrix_runtime.integrations.openai_agents import SynrixOpenAIMemory
        mem = SynrixOpenAIMemory()
        assert mem.backend.backend_type != "mock"

    def test_store_and_restore_thread(self, agent_backend):
        from synrix_runtime.integrations.openai_agents import SynrixOpenAIMemory
        mem = SynrixOpenAIMemory(backend=agent_backend)

        mem.store_thread_state("thread_123", {"messages": [{"role": "user", "content": "Hi"}]})
        result = mem.restore_thread("thread_123")
        assert result["found"]
        assert "messages" in result["state"]

    def test_store_run_result(self, agent_backend):
        from synrix_runtime.integrations.openai_agents import SynrixOpenAIMemory
        mem = SynrixOpenAIMemory(backend=agent_backend)

        mem.store_run_result("run_abc", {"output": "Analysis complete", "agent_name": "analyst"})
        runs = mem.get_all_runs()
        assert len(runs) >= 1

    def test_get_agent_history(self, agent_backend):
        from synrix_runtime.integrations.openai_agents import SynrixOpenAIMemory
        mem = SynrixOpenAIMemory(backend=agent_backend)

        mem.store_run_result("run_1", {"agent_name": "writer", "output": "Draft"})
        mem.store_run_result("run_2", {"agent_name": "editor", "output": "Edited"})
        mem.store_run_result("run_3", {"agent_name": "writer", "output": "Final"})

        writer_history = mem.get_agent_history("writer")
        assert len(writer_history) == 2
