"""
Integration tests for NeuralDLP Python SDK

Tests all core functionality against a running NeuralDLP API.
"""
import pytest
from neuraldlp import NeuralDLP
from neuraldlp.exceptions import (
    AuthenticationError,
    ValidationError,
    NotFoundError,
    NeuralDLPError
)


class TestHealthCheck:
    """Test API health check"""
    
    def test_health_check(self, client):
        """Health endpoint should return successfully"""
        # This would need a health_check method in the client
        # For now, we'll test by making a simple request
        result = client.inspect_input("test")
        assert result is not None


class TestInspectInput:
    """Test input inspection functionality"""
    
    def test_safe_content(self, client):
        """Safe content should have low risk score"""
        result = client.inspect_input("Hello, how are you today?")
        
        assert result.risk_score < 30
        assert result.recommended_action == "ALLOW"
        assert result.request_id
        assert isinstance(result.entities, list)
    
    def test_content_with_pii(self, client):
        """Content with PII should be detected"""
        text = "My email is john@example.com and phone is (555) 123-4567"
        result = client.inspect_input(text)
        
        assert result.risk_score > 30
        assert len(result.entities) > 0
        
        # Check for email entity
        email_found = any(e.type == "EMAIL" for e in result.entities)
        assert email_found, "Should detect email address"
        
        email_entity = next(e for e in result.entities if e.type == "EMAIL")
        assert email_entity.value == "john@example.com"
        assert email_entity.confidence > 0.5
    
    def test_high_risk_content(self, client):
        """High-risk content should be flagged"""
        text = "My SSN is 123-45-6789 and credit card is 4532-1234-5678-9010"
        result = client.inspect_input(text)
        
        assert result.risk_score > 70
        assert result.recommended_action in ["TOKENIZE", "BLOCK"]
        assert len(result.entities) >= 1
    
    def test_with_options(self, client):
        """Inspection with options should work"""
        result = client.inspect_input(
            "Test message",
            tenant_id="test-tenant",
            user_id="test-user",
            application="test-app",
            metadata={"session_id": "abc123"}
        )
        
        assert result is not None
        assert result.request_id


class TestTokenization:
    """Test tokenization and rehydration"""
    
    def test_tokenize_and_rehydrate(self, client):
        """Should tokenize sensitive data and restore it"""
        # Step 1: Inspect to get entities
        original_text = "My email is john@example.com and phone is (555) 123-4567"
        inspection = client.inspect_input(original_text)
        
        if len(inspection.entities) == 0:
            pytest.skip("No entities detected in test data")
        
        # Step 2: Tokenize
        tokenization = client.tokenize(
            text=original_text,
            entities=inspection.entities,
            tenant_id="default",
            ttl_seconds=3600
        )
        
        assert tokenization.transformed_text != original_text
        assert tokenization.mapping_id
        assert tokenization.request_id
        assert len(tokenization.tokens) > 0
        assert tokenization.expires_at
        
        # Step 3: Rehydrate
        rehydration = client.rehydrate(
            text=tokenization.transformed_text,
            mapping_id=tokenization.mapping_id
        )
        
        assert rehydration.rehydrated_text
        assert rehydration.confidence > 0.8
        assert len(rehydration.restored_entities) > 0
        assert "john@example.com" in rehydration.rehydrated_text
    
    def test_tokenization_options(self, client):
        """Tokenization with various options should work"""
        text = "Email: test@example.com"
        inspection = client.inspect_input(text)
        
        if len(inspection.entities) == 0:
            pytest.skip("No entities detected")
        
        tokenization = client.tokenize(
            text=text,
            entities=inspection.entities,
            tenant_id="test-tenant",
            ttl_seconds=7200,
            preserve_context=True,
            deterministic=False
        )
        
        assert tokenization is not None
        assert tokenization.mapping_id


class TestInspectAndTokenize:
    """Test combined inspect and tokenize convenience method"""
    
    def test_inspect_and_tokenize_with_pii(self, client):
        """Should inspect and tokenize in one call"""
        text = "Contact me at alice@company.com"
        
        inspection, tokenization = client.inspect_and_tokenize(text)
        
        assert inspection is not None
        assert inspection.risk_score > 0
        
        # If action is TOKENIZE and entities exist, tokenization should occur
        if inspection.recommended_action == "TOKENIZE" and len(inspection.entities) > 0:
            assert tokenization is not None
            assert tokenization.mapping_id
    
    def test_inspect_and_tokenize_safe_content(self, client):
        """Safe content should not be tokenized"""
        text = "Hello, this is a safe message"
        
        inspection, tokenization = client.inspect_and_tokenize(text)
        
        assert inspection is not None
        assert tokenization is None  # Safe content shouldn't be tokenized


class TestMappingManagement:
    """Test mapping status and deletion"""
    
    def test_get_mapping_status(self, client):
        """Should retrieve mapping status"""
        # Create a mapping first
        text = "My email is status@example.com"
        inspection = client.inspect_input(text)
        
        if len(inspection.entities) == 0:
            pytest.skip("No entities detected")
        
        tokenization = client.tokenize(text, inspection.entities)
        
        # Get status
        status = client.get_mapping_status(tokenization.mapping_id)
        
        assert status.mapping_id == tokenization.mapping_id
        assert status.exists is True
        assert status.ttl_seconds > 0
        assert status.expires_at
    
    def test_delete_mapping(self, client):
        """Should delete a mapping"""
        # Create a mapping
        text = "My email is delete@example.com"
        inspection = client.inspect_input(text)
        
        if len(inspection.entities) == 0:
            pytest.skip("No entities detected")
        
        tokenization = client.tokenize(text, inspection.entities)
        mapping_id = tokenization.mapping_id
        
        # Delete it
        client.delete_mapping(mapping_id)
        
        # Verify it's gone - either NotFoundError or exists=False
        try:
            status = client.get_mapping_status(mapping_id)
            assert status.exists is False
        except NotFoundError:
            # This is also acceptable - mapping doesn't exist
            pass


class TestInspectOutput:
    """Test output inspection for data leakage"""
    
    def test_inspect_output_safe(self, client):
        """Safe output should not show leakage"""
        # Create a mapping first
        text = "My email is output@example.com"
        inspection = client.inspect_input(text)
        
        if len(inspection.entities) == 0:
            pytest.skip("No entities detected")
        
        tokenization = client.tokenize(text, inspection.entities)
        
        # Inspect a safe output
        safe_output = "Sure, I can help you with that!"
        try:
            output_inspection = client.inspect_output(
                text=safe_output,
                mapping_id=tokenization.mapping_id
            )
            
            assert output_inspection is not None
            # leakage_detected might not be set for safe content
            if hasattr(output_inspection, 'leakage_detected') and output_inspection.leakage_detected is not None:
                assert output_inspection.leakage_detected is False
        except Exception as e:
            # Output inspection might not be fully implemented
            pytest.skip(f"Output inspection not available: {e}")


class TestContextManager:
    """Test context manager functionality"""
    
    def test_context_manager(self, api_key, base_url):
        """Client should work as context manager"""
        with NeuralDLP(api_key=api_key, base_url=base_url) as client:
            result = client.inspect_input("Test message")
            assert result is not None
        
        # Client should be closed after context


class TestHelperMethods:
    """Test helper methods on result objects"""
    
    def test_inspection_result_helpers(self, client):
        """InspectionResult helper methods should work correctly"""
        # Test safe content
        safe_result = client.inspect_input("Hello world")
        assert safe_result.is_safe() is True
        assert safe_result.should_tokenize() is False
        assert safe_result.is_blocked() is False
        
        # Test high-risk content
        risky_result = client.inspect_input("SSN: 123-45-6789, CC: 4532-1234-5678-9010")
        assert risky_result.is_safe() is False
        assert risky_result.is_blocked() or risky_result.should_tokenize()


class TestErrorHandling:
    """Test error handling and exceptions"""
    
    def test_invalid_api_key(self, base_url):
        """Invalid API key should raise AuthenticationError or error"""
        client = NeuralDLP(api_key="invalid_key", base_url=base_url)
        
        # API might return 401 or 500 for invalid keys
        with pytest.raises((AuthenticationError, NeuralDLPError)):
            client.inspect_input("test")
    
    def test_missing_mapping(self, client):
        """Requesting non-existent mapping should handle gracefully"""
        # This might raise NotFoundError or return exists=False
        try:
            status = client.get_mapping_status("nonexistent_mapping_id")
            # If it doesn't raise, it should return exists=False
            assert status.exists is False
        except NotFoundError:
            # This is also acceptable behavior
            pass


class TestModels:
    """Test data model functionality"""
    
    def test_entity_serialization(self):
        """Entity model should serialize/deserialize correctly"""
        from neuraldlp.models import Entity
        
        entity_data = {
            "type": "EMAIL",
            "value": "test@example.com",
            "start": 0,
            "end": 16,
            "confidence": 0.99
        }
        
        entity = Entity.from_dict(entity_data)
        assert entity.type == "EMAIL"
        assert entity.value == "test@example.com"
        assert entity.confidence == 0.99
        
        # Test to_dict
        serialized = entity.to_dict()
        assert serialized["type"] == "EMAIL"
        assert serialized["value"] == "test@example.com"
