# services/sales/tests/unit/test_services.py
"""
Unit tests for Sales Service
"""

import pytest
from datetime import datetime, timezone
from decimal import Decimal
from unittest.mock import AsyncMock, patch
from uuid import uuid4

from app.services.sales_service import SalesService
from app.schemas.sales import SalesDataCreate, SalesDataUpdate, SalesDataQuery


@pytest.mark.asyncio
class TestSalesService:
    """Test Sales Service business logic"""

    @pytest.fixture
    def sales_service(self):
        """Create sales service instance"""
        return SalesService()

    async def test_create_sales_record_success(self, sales_service, sample_tenant_id, sample_sales_data):
        """Test successful sales record creation"""
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_record = AsyncMock()
            mock_record.id = uuid4()
            mock_record.inventory_product_id = sample_sales_data.inventory_product_id
            mock_repository.create_sales_record.return_value = mock_record
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.create_sales_record(
                    sample_sales_data, 
                    sample_tenant_id
                )
                
                assert result is not None
                assert result.id is not None
                mock_repository.create_sales_record.assert_called_once_with(sample_sales_data, sample_tenant_id)

    async def test_create_sales_record_validation_error(self, sales_service, sample_tenant_id):
        """Test sales record creation with validation error"""
        # Create invalid sales data (future date)
        invalid_data = SalesDataCreate(
            date=datetime(2030, 1, 1, tzinfo=timezone.utc),  # Future date
            inventory_product_id="550e8400-e29b-41d4-a716-446655440000",
            product_name="Test Product",
            quantity_sold=1,
            revenue=Decimal("5.00")
        )
        
        with pytest.raises(ValueError, match="Sales date cannot be in the future"):
            await sales_service.create_sales_record(invalid_data, sample_tenant_id)

    async def test_update_sales_record(self, sales_service, sample_tenant_id):
        """Test updating a sales record"""
        record_id = uuid4()
        update_data = SalesDataUpdate(
            inventory_product_id="550e8400-e29b-41d4-a716-446655440999",
            product_name="Updated Product",
            quantity_sold=10
        )
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            
            # Mock existing record
            mock_existing = AsyncMock()
            mock_existing.tenant_id = sample_tenant_id
            mock_repository.get_by_id.return_value = mock_existing
            
            # Mock updated record
            mock_updated = AsyncMock()
            mock_updated.inventory_product_id = "550e8400-e29b-41d4-a716-446655440999"
            mock_repository.update.return_value = mock_updated
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.update_sales_record(
                    record_id, 
                    update_data, 
                    sample_tenant_id
                )
                
                assert result.inventory_product_id == "550e8400-e29b-41d4-a716-446655440999"
                mock_repository.update.assert_called_once()

    async def test_update_nonexistent_record(self, sales_service, sample_tenant_id):
        """Test updating a non-existent record"""
        record_id = uuid4()
        update_data = SalesDataUpdate(inventory_product_id="550e8400-e29b-41d4-a716-446655440999", product_name="Updated Product")
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_repository.get_by_id.return_value = None  # Record not found
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                with pytest.raises(ValueError, match="not found for tenant"):
                    await sales_service.update_sales_record(
                        record_id, 
                        update_data, 
                        sample_tenant_id
                    )

    async def test_get_sales_records(self, sales_service, sample_tenant_id):
        """Test getting sales records for tenant"""
        query_params = SalesDataQuery(limit=10)
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_records = [AsyncMock(), AsyncMock()]
            mock_repository.get_by_tenant.return_value = mock_records
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.get_sales_records(
                    sample_tenant_id, 
                    query_params
                )
                
                assert len(result) == 2
                mock_repository.get_by_tenant.assert_called_once_with(sample_tenant_id, query_params)

    async def test_get_sales_record_success(self, sales_service, sample_tenant_id):
        """Test getting a specific sales record"""
        record_id = uuid4()
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_record = AsyncMock()
            mock_record.tenant_id = sample_tenant_id
            mock_repository.get_by_id.return_value = mock_record
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.get_sales_record(record_id, sample_tenant_id)
                
                assert result is not None
                assert result.tenant_id == sample_tenant_id

    async def test_get_sales_record_wrong_tenant(self, sales_service, sample_tenant_id):
        """Test getting a record that belongs to different tenant"""
        record_id = uuid4()
        wrong_tenant_id = uuid4()
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_record = AsyncMock()
            mock_record.tenant_id = sample_tenant_id  # Different tenant
            mock_repository.get_by_id.return_value = mock_record
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.get_sales_record(record_id, wrong_tenant_id)
                
                assert result is None

    async def test_delete_sales_record(self, sales_service, sample_tenant_id):
        """Test deleting a sales record"""
        record_id = uuid4()
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            
            # Mock existing record
            mock_existing = AsyncMock()
            mock_existing.tenant_id = sample_tenant_id
            mock_repository.get_by_id.return_value = mock_existing
            
            mock_repository.delete.return_value = True
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.delete_sales_record(record_id, sample_tenant_id)
                
                assert result is True
                mock_repository.delete.assert_called_once_with(record_id)

    async def test_get_product_sales(self, sales_service, sample_tenant_id):
        """Test getting sales for specific product"""
        inventory_product_id = "550e8400-e29b-41d4-a716-446655440000"
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_records = [AsyncMock(), AsyncMock()]
            mock_repository.get_by_product.return_value = mock_records
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.get_product_sales(sample_tenant_id, inventory_product_id)
                
                assert len(result) == 2
                mock_repository.get_by_product.assert_called_once()

    async def test_get_sales_analytics(self, sales_service, sample_tenant_id):
        """Test getting sales analytics"""
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            mock_analytics = {
                "total_revenue": Decimal("100.00"),
                "total_quantity": 50,
                "total_transactions": 10
            }
            mock_repository.get_analytics.return_value = mock_analytics
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.get_sales_analytics(sample_tenant_id)
                
                assert result["total_revenue"] == Decimal("100.00")
                assert result["total_quantity"] == 50
                assert result["total_transactions"] == 10

    async def test_validate_sales_record(self, sales_service, sample_tenant_id):
        """Test validating a sales record"""
        record_id = uuid4()
        validation_notes = "Validated by manager"
        
        with patch('app.services.sales_service.get_db_transaction') as mock_get_db:
            mock_db = AsyncMock()
            mock_get_db.return_value.__aenter__.return_value = mock_db
            
            mock_repository = AsyncMock()
            
            # Mock existing record
            mock_existing = AsyncMock()
            mock_existing.tenant_id = sample_tenant_id
            mock_repository.get_by_id.return_value = mock_existing
            
            # Mock validated record
            mock_validated = AsyncMock()
            mock_validated.is_validated = True
            mock_repository.validate_record.return_value = mock_validated
            
            with patch('app.services.sales_service.SalesRepository', return_value=mock_repository):
                result = await sales_service.validate_sales_record(
                    record_id, 
                    sample_tenant_id, 
                    validation_notes
                )
                
                assert result.is_validated is True
                mock_repository.validate_record.assert_called_once_with(record_id, validation_notes)

    async def test_validate_sales_data_business_rules(self, sales_service, sample_tenant_id):
        """Test business validation rules"""
        # Test revenue mismatch detection
        sales_data = SalesDataCreate(
            date=datetime.now(timezone.utc),
            inventory_product_id="550e8400-e29b-41d4-a716-446655440000",
            product_name="Test Product",
            quantity_sold=5,
            unit_price=Decimal("2.00"),
            revenue=Decimal("15.00"),  # Should be 10.00 (5 * 2.00)
            discount_applied=Decimal("0")
        )
        
        # This should not raise an error, just log a warning
        await sales_service._validate_sales_data(sales_data, sample_tenant_id)

    async def test_post_create_actions(self, sales_service):
        """Test post-create actions"""
        mock_record = AsyncMock()
        mock_record.id = uuid4()
        
        # Should not raise any exceptions
        await sales_service._post_create_actions(mock_record)