package com.saas.shared.service.ai;

import com.saas.tenant.entity.AiApiCostRecord;
import com.saas.tenant.repository.AiApiCostRecordRepository;
import com.saas.shared.core.TenantContext;
import com.saas.shared.dto.AiCostCalculationResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * AI Cost Tracking Service
 * 
 * Clean Architecture:
 * - Single Responsibility: Persist AI costs to database
 * - Repository pattern: Uses AiApiCostRecordRepository for data access
 * - Always saves to admin database (saas_db) for centralized cost tracking
 * 
 * Usage Flow:
 * 1. AI handler calls AiCostCalculator.calculateCost()
 * 2. Get AiCostCalculationResult
 * 3. Call saveAiCost() to persist
 * 
 * Cost Consolidation:
 * - Links to call via call_sid
 * - Multiple AI providers per call supported (OpenAI + ElevenLabs)
 * - Join with CallCostRecord for total call cost (VoIP + AI)
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AiCostTrackingService {
    
    private final AiApiCostRecordRepository aiCostRepository;
    
    /**
     * Save AI cost record to admin database (saas_db)
     * 
     * @param callSid Call identifier
     * @param tenantId Tenant identifier
     * @param costResult AI cost calculation result
     * @param callStartTime Call start timestamp
     * @param callEndTime Call end timestamp
     * @param fromNumber Caller phone number
     * @param toNumber Called phone number
     * @return Saved AiApiCostRecord
     */
    @Transactional
    public AiApiCostRecord saveAiCost(
            String callSid,
            String tenantId,
            AiCostCalculationResult costResult,
            LocalDateTime callStartTime,
            LocalDateTime callEndTime,
            String fromNumber,
            String toNumber
    ) {
        log.info("💰 [AiCostTracking] Saving AI cost - CallSid: {}, Provider: {}, Cost: {} {}", 
                callSid, costResult.getAiProvider(), costResult.getCost(), costResult.getCurrency());
        
        // Ensure we're writing to admin database
        String previousTenant = TenantContext.getTenantId();
        try {
            TenantContext.setTenantId("saas_db");
            
            // Calculate duration
            Integer durationSeconds = null;
            if (callStartTime != null && callEndTime != null) {
                durationSeconds = (int) java.time.Duration.between(callStartTime, callEndTime).getSeconds();
            }
            
            AiApiCostRecord record = AiApiCostRecord.builder()
                    .callSid(callSid)
                    .aiProvider(costResult.getAiProvider())
                    .costType(costResult.getCostType())
                    .cost(costResult.getCost())
                    .currency(costResult.getCurrency())
                    .usageDetails(costResult.getUsageDetails())
                    .metadata(costResult.getMetadata())
                    .callStartTime(callStartTime)
                    .callEndTime(callEndTime)
                    .callDurationSeconds(durationSeconds)
                    .fromNumber(fromNumber)
                    .toNumber(toNumber)
                    .build();
            
            AiApiCostRecord saved = aiCostRepository.save(record);
            log.info("✅ [AiCostTracking] AI cost saved - ID: {}, CallSid: {}, Provider: {}", 
                    saved.getId(), saved.getCallSid(), saved.getAiProvider());
            
            return saved;
            
        } finally {
            // Restore previous tenant context
            if (previousTenant != null) {
                TenantContext.setTenantId(previousTenant);
            } else {
                TenantContext.clear();
            }
        }
    }
    
    /**
     * Get all AI costs for a specific call
     * 
     * @param callSid Call identifier
     * @return List of AI cost records
     */
    @Transactional(readOnly = true)
    public List<AiApiCostRecord> getAiCostsByCall(String callSid) {
        return aiCostRepository.findByCallSid(callSid);
    }
    
    /**
     * Calculate total AI cost for a call
     * 
     * @param callSid Call identifier
     * @return Total AI cost (sum of all AI providers)
     */
    @Transactional(readOnly = true)
    public BigDecimal getTotalAiCostForCall(String callSid) {
        List<AiApiCostRecord> costs = getAiCostsByCall(callSid);
        return costs.stream()
                .map(AiApiCostRecord::getCost)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    /**
     * Get AI cost summary for a tenant within date range
     * 
     * @param tenantId Tenant identifier
     * @param start Start date
     * @param end End date
     * @return Cost summary map
     */
    @Transactional(readOnly = true)
    public Map<String, Object> getAiCostSummary(String tenantId, LocalDateTime start, LocalDateTime end) {
        BigDecimal totalCost = aiCostRepository.getTotalCostBetween(start, end);
        List<Object[]> costByProvider = aiCostRepository.getCostByProviderBetween(start, end);
        List<Object[]> costByType = aiCostRepository.getCostByTypeBetween(start, end);
        Long callCount = aiCostRepository.getCallCountBetween(start, end);
        
        Map<String, Object> summary = new HashMap<>();
        summary.put("total_cost", totalCost != null ? totalCost : BigDecimal.ZERO);
        summary.put("currency", "USD");
        summary.put("call_count", callCount != null ? callCount : 0L);
        summary.put("period", Map.of("start", start, "end", end));
        
        // Cost by provider
        Map<String, BigDecimal> providerBreakdown = new HashMap<>();
        for (Object[] row : costByProvider) {
            providerBreakdown.put((String) row[0], (BigDecimal) row[1]);
        }
        summary.put("cost_by_provider", providerBreakdown);
        
        // Cost by type
        Map<String, BigDecimal> typeBreakdown = new HashMap<>();
        for (Object[] row : costByType) {
            typeBreakdown.put((String) row[0], (BigDecimal) row[1]);
        }
        summary.put("cost_by_type", typeBreakdown);
        
        return summary;
    }
    
    /**
     * Check if AI costs already exist for a call
     * Prevents duplicate cost recording
     * 
     * @param callSid Call identifier
     * @return true if costs exist
     */
    @Transactional(readOnly = true)
    public boolean aiCostsExistForCall(String callSid) {
        return aiCostRepository.existsByCallSid(callSid);
    }
}
