package com.saas.voip.service.ai;

import com.saas.shared.dto.AiCostCalculationResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Unit tests for ElevenLabs Cost Calculator
 * 
 * Test Coverage:
 * - Cost calculation accuracy
 * - Different models (Turbo v2, Multilingual v2)
 * - Edge cases (zero characters, null input)
 * - Pricing correctness
 */
class ElevenLabsCostCalculatorTest {
    
    private ElevenLabsCostCalculator calculator;
    
    @BeforeEach
    void setUp() {
        calculator = new ElevenLabsCostCalculator();
    }
    
    @Test
    void testCalculateCost_TurboV2_Standard() {
        // Given: Standard usage for Turbo v2
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 1000);
        usageMetrics.put("model", "eleven_turbo_v2");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Verify calculation
        assertNotNull(result);
        assertEquals("ELEVENLABS", result.getAiProvider());
        assertEquals("TTS", result.getCostType());
        assertEquals("USD", result.getCurrency());
        
        // Cost = (1000 / 1000) * 0.30 = 0.30
        BigDecimal expectedCost = new BigDecimal("0.300000");
        assertEquals(expectedCost, result.getCost());
        
        // Verify usage details
        Map<String, Object> details = result.getUsageDetails();
        assertEquals(1000, details.get("characters"));
        assertEquals("eleven_turbo_v2", details.get("model"));
    }
    
    @Test
    void testCalculateCost_MultilingualV2() {
        // Given: Multilingual v2 model
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 2500);
        usageMetrics.put("model", "eleven_multilingual_v2");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Cost = (2500 / 1000) * 0.30 = 0.75
        BigDecimal expectedCost = new BigDecimal("0.750000");
        assertEquals(expectedCost, result.getCost());
    }
    
    @Test
    void testCalculateCost_SmallUsage() {
        // Given: Less than 1000 characters
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 250);
        usageMetrics.put("model", "eleven_turbo_v2");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Cost = (250 / 1000) * 0.30 = 0.075
        BigDecimal expectedCost = new BigDecimal("0.075000");
        assertEquals(expectedCost, result.getCost());
    }
    
    @Test
    void testCalculateCost_ZeroCharacters() {
        // Given: Zero characters
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 0);
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Cost should be zero
        assertEquals(BigDecimal.ZERO, result.getCost());
        assertTrue(result.isValid());
    }
    
    @Test
    void testCalculateCost_LargeUsage() {
        // Given: Large usage (100K characters)
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 100000);
        usageMetrics.put("model", "eleven_turbo_v2");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Cost = (100000 / 1000) * 0.30 = 30.00
        BigDecimal expectedCost = new BigDecimal("30.000000");
        assertEquals(expectedCost, result.getCost());
    }
    
    @Test
    void testCalculateCost_UnknownModel() {
        // Given: Unknown model (should use default pricing)
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 1000);
        usageMetrics.put("model", "some_unknown_model");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Should use default pricing (0.30 per 1K)
        BigDecimal expectedCost = new BigDecimal("0.300000");
        assertEquals(expectedCost, result.getCost());
    }
    
    @Test
    void testCalculateCost_NoModel() {
        // Given: No model specified
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 1000);
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Should use default pricing
        BigDecimal expectedCost = new BigDecimal("0.300000");
        assertEquals(expectedCost, result.getCost());
    }
    
    @Test
    void testCalculateCost_NullInput() {
        // When: Calculate cost with null input
        AiCostCalculationResult result = calculator.calculateCost(null);
        
        // Then: Should return zero-cost result
        assertNotNull(result);
        assertEquals(BigDecimal.ZERO, result.getCost());
        assertTrue(result.isValid());
    }
    
    @Test
    void testCalculateCost_EmptyInput() {
        // When: Calculate cost with empty map
        AiCostCalculationResult result = calculator.calculateCost(new HashMap<>());
        
        // Then: Should return zero-cost result
        assertNotNull(result);
        assertEquals(BigDecimal.ZERO, result.getCost());
    }
    
    @Test
    void testGetProviderName() {
        assertEquals("ELEVENLABS", calculator.getProviderName());
    }
    
    @Test
    void testGetSupportedCostTypes() {
        String[] types = calculator.getSupportedCostTypes();
        assertNotNull(types);
        assertEquals(1, types.length);
        assertEquals("TTS", types[0]);
    }
    
    @Test
    void testResultIsValid() {
        // Given: Valid usage
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 1250);
        usageMetrics.put("model", "eleven_turbo_v2");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Result should be valid
        assertTrue(result.isValid());
    }
    
    @Test
    void testCalculateCost_WithMetadata() {
        // Given: Usage with additional metadata
        Map<String, Object> usageMetrics = new HashMap<>();
        usageMetrics.put("characters", 1500);
        usageMetrics.put("model", "eleven_turbo_v2");
        usageMetrics.put("agent_id", "agent_123");
        usageMetrics.put("voice_id", "voice_456");
        usageMetrics.put("conversation_id", "conv_789");
        
        // When: Calculate cost
        AiCostCalculationResult result = calculator.calculateCost(usageMetrics);
        
        // Then: Metadata should be captured
        assertNotNull(result.getMetadata());
        assertEquals("agent_123", result.getMetadata().get("agent_id"));
        assertEquals("voice_456", result.getMetadata().get("voice_id"));
        assertEquals("conv_789", result.getMetadata().get("conversation_id"));
    }
}
