"""
Unit Tests for BaseService
Tests for base service functionality with mocks.
"""

import pytest
from unittest.mock import MagicMock, patch
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from app.services.base import BaseService, ServiceConfig
from app.services.mocks import MockCacheService


class MockRepo:
    """Mock repository for testing."""
    def __init__(self):
        self.data = {}


class MockService(BaseService):
    """Concrete implementation for testing."""
    
    cache_patterns = ["test*", "mock*"]
    
    def __init__(self, config=None, **kwargs):
        super().__init__(config=config, **kwargs)
        self.last_operation = None
    
    def get_data(self, key: str):
        """Sample method using base service features."""
        self.last_operation = f"get_{key}"
        return {"key": key, "value": "test"}
    
    def update_data(self, key: str, value: str):
        """Sample method with cache invalidation."""
        self.last_operation = f"update_{key}"
        self.invalidate_cache_for("test", key)
        return {"key": key, "value": value}
    
    def require_auth_test(self, token: str):
        """Test auth requirement."""
        return self.require_auth(token)


class TestServiceConfig:
    """Tests for ServiceConfig."""
    
    def test_default_config(self):
        """Test default configuration."""
        config = ServiceConfig()
        
        assert config.player_repo is None
        assert config.team_repo is None
        assert config.cache is None
    
    def test_config_with_repos(self):
        """Test configuration with repositories."""
        player_repo = MockRepo()
        team_repo = MockRepo()
        cache = MockCacheService()
        
        config = ServiceConfig(
            player_repo=player_repo,
            team_repo=team_repo,
            cache=cache
        )
        
        assert config.player_repo is player_repo
        assert config.team_repo is team_repo
        assert config.cache is cache


class TestBaseServiceInit:
    """Tests for BaseService initialization."""
    
    def test_init        """Test initialization_with_config(self):
 with config object."""
        config = ServiceConfig(cache=MockCacheService())
        service = MockService(config=config)
        
        assert service._cache is not None
    
    def test_init_with_kwargs(self):
        """Test initialization with keyword arguments."""
        cache = MockCacheService()
        service = MockService(cache=cache)
        
        assert service._cache is cache
    
    def test_config_overrides_kwargs(self):
        """Test that config takes precedence over kwargs."""
        cache1 = MockCacheService()
        cache2 = MockCacheService()
        
        config = ServiceConfig(cache=cache1)
        service = MockService(config=config, cache=cache2)
        
        # Config should take precedence
        assert service._cache is cache1


class TestBaseServiceCacheInvalidation:
    """Tests for cache invalidation methods."""
    
    def test_invalidate_cache_for_entity(self):
        """Test invalidating cache for a specific entity."""
        cache = MockCacheService()
        cache.set("test:123:data", '{"test": "value"}', 300)
        
        config = ServiceConfig(cache=cache)
        service = MockService(config=config)
        
        # Should not throw
        service.invalidate_cache_for("test", entity_id=123)
    
    def test_invalidate_related_cache(self):
        """Test invalidating multiple cache patterns."""
        cache = MockCacheService()
        
        # Set some cache entries
        cache.set("test1:data", '{"1": "data"}', 300)
        cache.set("mock2:data", '{"2": "data"}', 300)
        cache.set("other:data", '{"3": "data"}', 300)
        
        config = ServiceConfig(cache=cache)
        service = MockService(config=config)
        
        # Invalidate patterns
        service.invalidate_related_cache(["test*", "mock*"])
        
        # test* and mock* should be cleared
        assert not cache.exists("test1:data")
        assert not cache.exists("mock2:data")
        # other should remain
        assert cache.exists("other:data")


class TestBaseServiceErrorHandling:
    """Tests for error handling methods."""
    
    def test_handle_error_no_rethrow(self):
        """Test error handling without rethrowing."""
        service = MockService()
        
        result = service.handle_error("Test operation", ValueError("test error"), rethrow=False)
        
        assert "error" in result
        assert "Test operation" in result["error"]
    
    def test_handle_error_with_rethrow(self):
        """Test error handling that rethrows."""
        service = MockService()
        
        with pytest.raises(Exception) as exc_info:
            service.handle_error("Test operation", ValueError("test error"), rethrow=True)
        
        assert "Test operation" in str(exc_info.value)


class TestBaseServiceAuth:
    """Tests for authentication methods."""
    
    def test_require_auth_valid_token(self):
        """Test valid token authentication."""
        service = MockService()
        
        with patch('app.services.base.valid_token', return_value=True):
            result = service.require_auth_test("valid_token")
            assert result is True
    
    def test_require_auth_invalid_token(self):
        """Test invalid token authentication."""
        service = MockService()
        
        with patch('app.services.base.valid_token', return_value=False):
            with pytest.raises(Exception) as exc_info:
                service.require_auth_test("invalid_token")
            
            assert exc_info.value.status_code == 401


class TestBaseServiceQueryParams:
    """Tests for query parameter parsing."""
    
    def test_parse_query_params_remove_none(self):
        """Test removing None values."""
        service = MockService()
        
        result = service.parse_query_params({
            "name": "test",
            "age": None,
            "active": True,
            "empty": []
        })
        
        assert "name" in result
        assert "age" not in result
        assert "active" in result
        assert "empty" not in result  # Empty list removed
    
    def test_parse_query_params_keep_none(self):
        """Test keeping None values when specified."""
        service = MockService()
        
        result = service.parse_query_params({
            "name": "test",
            "age": None
        }, remove_none=False)
        
        assert "name" in result
        assert "age" in result
        assert result["age"] is None


class TestBaseServiceCsvFormatting:
    """Tests for CSV formatting."""
    
    def test_format_csv_response(self):
        """Test CSV formatting."""
        service = MockService()
        
        headers = ["Name", "Age", "City"]
        rows = [
            ["John", "30", "NYC"],
            ["Jane", "25", "LA"]
        ]
        
        csv = service.format_csv_response(headers, rows)
        
        assert "Name" in csv
        assert "John" in csv
        assert "Jane" in csv


# Run tests if executed directly
if __name__ == "__main__":
    pytest.main([__file__, "-v"])
