"""
Tests for Phase 4 credit deduction
"""
from unittest.mock import patch
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.billing.services.credit_service import CreditService
from igny8_core.business.billing.constants import CREDIT_COSTS
from igny8_core.business.billing.exceptions import InsufficientCreditsError
from igny8_core.api.tests.test_integration_base import IntegrationTestBase


class Phase4CreditTests(IntegrationTestBase):
    """Tests for Phase 4 credit deduction"""
    
    def setUp(self):
        super().setUp()
        # Set initial credits
        self.account.credits = 1000
        self.account.save()
    
    def test_linking_deducts_correct_credits(self):
        """Test that linking deducts correct credits"""
        cost = CreditService.get_credit_cost('linking')
        expected_cost = CREDIT_COSTS.get('linking', 0)
        
        self.assertEqual(cost, expected_cost)
        self.assertEqual(cost, 8)  # From constants
    
    def test_optimization_deducts_correct_credits(self):
        """Test that optimization deducts correct credits based on word count"""
        word_count = 500
        cost = CreditService.get_credit_cost('optimization', word_count)
        
        # Should be 1 credit per 200 words, so 500 words = 3 credits (max(1, 1 * 500/200) = 3)
        expected = max(1, int(CREDIT_COSTS.get('optimization', 1) * (word_count / 200)))
        self.assertEqual(cost, expected)
    
    def test_optimization_credits_per_entry_point(self):
        """Test that optimization credits are same regardless of entry point"""
        word_count = 400
        
        # All entry points should use same credit calculation
        cost = CreditService.get_credit_cost('optimization', word_count)
        
        # 400 words = 2 credits (1 * 400/200)
        self.assertEqual(cost, 2)
    
    @patch('igny8_core.business.billing.services.credit_service.CreditService.deduct_credits')
    def test_pipeline_deducts_credits_at_each_stage(self, mock_deduct):
        """Test that pipeline deducts credits at each stage"""
        from igny8_core.business.content.services.content_pipeline_service import ContentPipelineService
        from igny8_core.business.linking.services.linker_service import LinkerService
        from igny8_core.business.optimization.services.optimizer_service import OptimizerService
        
        content = Content.objects.create(
            account=self.account,
            site=self.site,
            sector=self.sector,
            title="Test",
            word_count=400,
            source='igny8'
        )
        
        # Mock the services
        with patch.object(LinkerService, 'process') as mock_link, \
             patch.object(OptimizerService, 'optimize_from_writer') as mock_optimize:
            
            mock_link.return_value = content
            mock_optimize.return_value = content
            
            service = ContentPipelineService()
            service.process_writer_content(content.id)
            
            # Should deduct credits for both linking and optimization
            self.assertGreater(mock_deduct.call_count, 0)
    
    def test_insufficient_credits_blocks_linking(self):
        """Test that insufficient credits blocks linking"""
        self.account.credits = 5  # Less than linking cost (8)
        self.account.save()
        
        with self.assertRaises(InsufficientCreditsError):
            CreditService.check_credits(self.account, 'linking')
    
    def test_insufficient_credits_blocks_optimization(self):
        """Test that insufficient credits blocks optimization"""
        self.account.credits = 1  # Less than optimization cost for 500 words
        self.account.save()
        
        with self.assertRaises(InsufficientCreditsError):
            CreditService.check_credits(self.account, 'optimization', 500)
    
    def test_credit_deduction_logged(self):
        """Test that credit deduction is logged"""
        from igny8_core.business.billing.models import CreditUsageLog
        
        initial_credits = self.account.credits
        cost = CreditService.get_credit_cost('linking')
        
        CreditService.deduct_credits_for_operation(
            account=self.account,
            operation_type='linking',
            description="Test linking"
        )
        
        self.account.refresh_from_db()
        self.assertEqual(self.account.credits, initial_credits - cost)
        
        # Check that usage log was created
        log = CreditUsageLog.objects.filter(
            account=self.account,
            operation_type='linking'
        ).first()
        self.assertIsNotNone(log)
    
    def test_batch_operations_deduct_multiple_credits(self):
        """Test that batch operations deduct multiple credits"""
        initial_credits = self.account.credits
        linking_cost = CreditService.get_credit_cost('linking')
        
        # Deduct for 3 linking operations
        for i in range(3):
            CreditService.deduct_credits_for_operation(
                account=self.account,
                operation_type='linking',
                description=f"Linking {i}"
            )
        
        self.account.refresh_from_db()
        expected_credits = initial_credits - (linking_cost * 3)
        self.assertEqual(self.account.credits, expected_credits)

