"""
Testes de edge cases extremos
"""
import pytest
from atomic_agents.core import TDLNCompressor, PolicyEngine, AuditLedger
from atomic_agents.integrations.langchain import AtomicOptimizer
from uuid import uuid4
from langchain_core.outputs import LLMResult, Generation
from langchain_core.agents import AgentAction


class TestEdgeCasesTDLN:
    def test_empty_string(self):
        compressor = TDLNCompressor()
        assert compressor.compress("") == ""
        
    def test_whitespace_only(self):
        compressor = TDLNCompressor()
        result = compressor.compress("   \n\t   ")
        assert result.strip() == ""
        
    def test_only_special_chars(self):
        compressor = TDLNCompressor()
        text = "!@#$%^&*()_+-=[]{}|;:,.<>?"
        result = compressor.compress(text)
        assert isinstance(result, str)
        
    def test_very_long_single_word(self):
        compressor = TDLNCompressor()
        text = "a" * 10000
        result = compressor.compress(text)
        assert len(result) == len(text)  # Não deve comprimir palavras únicas
        
    def test_none_input(self):
        compressor = TDLNCompressor()
        with pytest.raises((AttributeError, TypeError)):
            compressor.compress(None)
            
    def test_non_string_input(self):
        compressor = TDLNCompressor()
        with pytest.raises((AttributeError, TypeError)):
            compressor.compress(123)
        with pytest.raises((AttributeError, TypeError)):
            compressor.compress([])
        with pytest.raises((AttributeError, TypeError)):
            compressor.compress({})
            
    def test_unicode_emojis(self):
        compressor = TDLNCompressor()
        text = "Please help me 🚀🎉💯. The goal is to process ✅."
        result = compressor.compress(text)
        assert "🚀" in result or "⊢" in result
        
    def test_newlines_and_tabs(self):
        compressor = TDLNCompressor()
        text = "Please help me.\nThe goal is to\nprocess\ttabs."
        result = compressor.compress(text)
        assert isinstance(result, str)
        
    def test_repeated_patterns(self):
        compressor = TDLNCompressor()
        text = "Please help me. " * 100
        result = compressor.compress(text)
        # Deve remover duplicatas
        assert result.count("Please help me") < 100


class TestEdgeCasesPolicyEngine:
    def test_empty_policies(self):
        engine = PolicyEngine({})
        result = engine.pre_check("any", {})
        assert result.allowed is True
        
    def test_none_context(self):
        engine = PolicyEngine({"maxCost": 100.0})
        with pytest.raises((KeyError, AttributeError, TypeError)):
            engine.pre_check("any", None)
            
    def test_negative_costs(self):
        engine = PolicyEngine({"maxCost": 100.0})
        result = engine.pre_check("any", {"current_cost": -50.0})
        assert result.allowed is True  # Negativo deve passar
        
    def test_zero_limits(self):
        engine = PolicyEngine({"maxCost": 0.0, "maxTokens": 0})
        result = engine.pre_check("any", {"current_cost": 0.0, "current_tokens": 0})
        # Com custo 0 e limite 0, deve bloquear (>=)
        result2 = engine.pre_check("any", {"current_cost": 0.1})
        assert result2.allowed is False
        
    def test_very_large_numbers(self):
        engine = PolicyEngine({"maxCost": 1e10, "maxTokens": 1e10})
        result = engine.pre_check("any", {"current_cost": 1e9, "current_tokens": 1e9})
        assert result.allowed is True
        
    def test_empty_tool_name(self):
        engine = PolicyEngine({"blockedTools": ["tool1"]})
        result = engine.check_tool("")
        assert result.allowed is True  # String vazia não está bloqueada
        
    def test_none_tool_name(self):
        engine = PolicyEngine({"blockedTools": ["tool1"]})
        with pytest.raises((TypeError, AttributeError)):
            engine.check_tool(None)
            
    def test_unicode_tool_names(self):
        engine = PolicyEngine({"blockedTools": ["🚀tool", "中文工具"]})
        result = engine.check_tool("🚀tool")
        assert result.allowed is False
        result = engine.check_tool("中文工具")
        assert result.allowed is False


class TestEdgeCasesLedger:
    def test_empty_data(self):
        ledger = AuditLedger()
        entry = ledger.record({})
        assert "hash" in entry
        assert "timestamp" in entry
        
    def test_none_data(self):
        ledger = AuditLedger()
        with pytest.raises((TypeError, AttributeError)):
            ledger.record(None)
            
    def test_non_dict_data(self):
        ledger = AuditLedger()
        with pytest.raises((TypeError, AttributeError)):
            ledger.record("not a dict")
        with pytest.raises((TypeError, AttributeError)):
            ledger.record(123)
        with pytest.raises((TypeError, AttributeError)):
            ledger.record([])
            
    def test_circular_reference_data(self):
        ledger = AuditLedger()
        data = {"key": "value"}
        data["self"] = data  # Referência circular
        # JSON serialization deve falhar ou lidar com isso
        try:
            entry = ledger.record(data)
            # Se passar, verificar que hash foi gerado
            assert "hash" in entry
        except (TypeError, ValueError):
            # Comportamento esperado para referências circulares
            pass
            
    def test_very_deep_nested_dict(self):
        ledger = AuditLedger()
        data = {}
        current = data
        for i in range(100):
            current["level"] = {}
            current = current["level"]
        current["value"] = "deep"
        
        entry = ledger.record(data)
        assert "hash" in entry
        
    def test_special_chars_in_data(self):
        ledger = AuditLedger()
        data = {
            "event": "test",
            "special": "\x00\x01\x02\n\t\r\"'\\",
            "unicode": "🚀🎉💯中文"
        }
        entry = ledger.record(data)
        assert "hash" in entry


class TestEdgeCasesOptimizer:
    def test_empty_prompts_list(self):
        optimizer = AtomicOptimizer(compression=True, ledger=False)
        prompts = []
        optimizer.on_llm_start({}, prompts, run_id=uuid4())
        assert len(prompts) == 0
        
    def test_none_prompts(self):
        optimizer = AtomicOptimizer(compression=False, ledger=False)
        with pytest.raises((TypeError, AttributeError)):
            optimizer.on_llm_start({}, None, run_id=uuid4())
            
    def test_very_long_prompt(self):
        optimizer = AtomicOptimizer(compression=True, ledger=False)
        long_prompt = "Please help me. " * 10000
        prompts = [long_prompt]
        optimizer.on_llm_start({}, prompts, run_id=uuid4())
        assert len(prompts[0]) > 0
        
    def test_multiple_prompts(self):
        optimizer = AtomicOptimizer(compression=True, ledger=False)
        prompts = [
            "Please help me. The goal is to process 1.",
            "Please help me. The goal is to process 2.",
            "Please help me. The goal is to process 3.",
        ]
        optimizer.on_llm_start({}, prompts, run_id=uuid4())
        assert len(prompts) == 3
        for p in prompts:
            assert len(p) > 0
            
    def test_policy_violation_with_ledger(self):
        optimizer = AtomicOptimizer(
            compression=False,
            policies={"maxCost": 10.0},
            ledger=True
        )
        optimizer.current_cost = 15.0
        
        try:
            optimizer.on_llm_start({}, ["test"], run_id=uuid4())
        except Exception:
            pass  # Esperado falhar
        
        # Ledger deve ter registrado a tentativa se possível
        # (depende da implementação)
        
    def test_empty_llm_result(self):
        optimizer = AtomicOptimizer(ledger=True)
        llm_result = LLMResult(generations=[])
        optimizer.on_llm_end(llm_result, run_id=uuid4())
        # Não deve quebrar
        
    def test_none_agent_action(self):
        optimizer = AtomicOptimizer(ledger=False)
        with pytest.raises((TypeError, AttributeError)):
            optimizer.on_agent_action(None, run_id=uuid4())
            
    def test_agent_action_no_tool(self):
        optimizer = AtomicOptimizer(
            policies={"blockedTools": ["tool1"]},
            ledger=False
        )
        action = AgentAction(tool="", tool_input="", log="")
        # String vazia não está bloqueada
        optimizer.on_agent_action(action, run_id=uuid4())  # Não deve quebrar


