package com.saas.voip.service;

import com.saas.tenant.entity.VapiCall;
import com.saas.tenant.repository.VapiCallRepository;
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.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Vapi.ai Webhook Service
 * 
 * Processes webhooks from Vapi.ai:
 * - function-call: AI assistant calls a function
 * - end-of-call-report: Call finished, includes transcript and cost
 * - status-update: Call status changed (queued, ringing, in-progress, ended)
 * 
 * Architecture:
 * - Multi-tenant routing via tenantId extraction from webhook payload
 * - Updates VapiCall entity with real-time data
 * - Triggers cost tracking for completed calls
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class VapiWebhookService {
    
    private final VapiCallRepository callRepository;
    private final VapiCostTrackingService costTrackingService;
    
    /**
     * Process end-of-call-report webhook
     * This is called when a Vapi call ends and contains full transcript + cost data
     */
    @Transactional
    public void processEndOfCallReport(Map<String, Object> payload) {
        try {
            log.info("📥 Processing Vapi end-of-call-report webhook");
            
            // Extract call data
            Map<String, Object> call = (Map<String, Object>) payload.get("call");
            if (call == null) {
                log.error("❌ No call data in webhook payload");
                return;
            }
            
            String vapiCallId = (String) call.get("id");
            String status = (String) call.get("status");
            String endedReason = (String) call.get("endedReason");
            
            // Find call in database
            VapiCall existingCall = callRepository.findByVapiCallId(vapiCallId)
                .orElseGet(() -> {
                    log.warn("⚠️ Call {} not found in database, creating new record", vapiCallId);
                    return new VapiCall();
                });
            
            // Update call data
            existingCall.setVapiCallId(vapiCallId);
            existingCall.setStatus(status != null ? status : "ended");
            existingCall.setEndReason(endedReason);
            
            // Extract timestamps
            String startedAtStr = (String) call.get("startedAt");
            String endedAtStr = (String) call.get("endedAt");
            if (startedAtStr != null) {
                existingCall.setStartTime(parseIsoDateTime(startedAtStr));
            }
            if (endedAtStr != null) {
                existingCall.setEndTime(parseIsoDateTime(endedAtStr));
            }
            
            // Extract phone numbers
            Map<String, Object> customer = (Map<String, Object>) call.get("customer");
            if (customer != null) {
                String customerNumber = (String) customer.get("number");
                existingCall.setCustomerNumber(customerNumber);
            }
            
            Map<String, Object> phoneNumber = (Map<String, Object>) call.get("phoneNumber");
            if (phoneNumber != null) {
                String number = (String) phoneNumber.get("number");
                existingCall.setPhoneNumber(number);
            }
            
            // Extract assistant ID
            String assistantId = (String) call.get("assistantId");
            if (assistantId != null) {
                existingCall.setAssistantId(assistantId);
            }
            
            // Extract transcript
            List<Map<String, Object>> messages = (List<Map<String, Object>>) payload.get("messages");
            if (messages != null) {
                existingCall.setTranscript(new ArrayList<>(messages));
            }
            
            // Extract analysis
            Map<String, Object> analysis = (Map<String, Object>) payload.get("analysis");
            if (analysis != null) {
                existingCall.setAnalysis(analysis);
            }
            
            // Extract cost
            Map<String, Object> costData = (Map<String, Object>) call.get("cost");
            if (costData != null) {
                Object totalCostObj = costData.get("total");
                if (totalCostObj != null) {
                    BigDecimal totalCost = new BigDecimal(totalCostObj.toString());
                    existingCall.setCost(totalCost);
                    existingCall.setCostBreakdown(costData);
                    
                    // Trigger cost tracking
                    if (existingCall.getStartTime() != null) {
                        costTrackingService.trackCallCost(existingCall, costData);
                    }
                }
            }
            
            // Extract recording URL
            String recordingUrl = (String) call.get("recordingUrl");
            if (recordingUrl != null) {
                existingCall.setRecordingUrl(recordingUrl);
            }
            
            // Store full webhook payload for debugging
            existingCall.setWebhookPayload(payload);
            
            // Save to database
            callRepository.save(existingCall);
            log.info("✅ Vapi call {} updated successfully from end-of-call-report", vapiCallId);
            
        } catch (Exception e) {
            log.error("❌ Error processing Vapi end-of-call-report: {}", e.getMessage(), e);
        }
    }
    
    /**
     * Process status-update webhook
     * Called when call status changes (queued → ringing → in-progress → ended)
     */
    @Transactional
    public void processStatusUpdate(Map<String, Object> payload) {
        try {
            log.info("📥 Processing Vapi status-update webhook");
            
            Map<String, Object> call = (Map<String, Object>) payload.get("call");
            if (call == null) {
                log.error("❌ No call data in webhook payload");
                return;
            }
            
            String vapiCallId = (String) call.get("id");
            String status = (String) call.get("status");
            
            // Find and update call
            callRepository.findByVapiCallId(vapiCallId).ifPresentOrElse(
                existingCall -> {
                    existingCall.setStatus(status);
                    
                    // Update timestamps based on status
                    if ("in-progress".equals(status) && existingCall.getStartTime() == null) {
                        existingCall.setStartTime(LocalDateTime.now());
                    }
                    if ("ended".equals(status) && existingCall.getEndTime() == null) {
                        existingCall.setEndTime(LocalDateTime.now());
                    }
                    
                    callRepository.save(existingCall);
                    log.info("✅ Vapi call {} status updated to: {}", vapiCallId, status);
                },
                () -> log.warn("⚠️ Call {} not found in database, ignoring status update", vapiCallId)
            );
            
        } catch (Exception e) {
            log.error("❌ Error processing Vapi status-update: {}", e.getMessage(), e);
        }
    }
    
    /**
     * Process function-call webhook
     * Called when AI assistant invokes a function (e.g., bookAppointment)
     */
    public Map<String, Object> processFunctionCall(Map<String, Object> payload) {
        try {
            log.info("📥 Processing Vapi function-call webhook");
            
            Map<String, Object> functionCall = (Map<String, Object>) payload.get("functionCall");
            if (functionCall == null) {
                log.error("❌ No functionCall data in webhook payload");
                return Map.of("error", "No functionCall data");
            }
            
            String functionName = (String) functionCall.get("name");
            Map<String, Object> parameters = (Map<String, Object>) functionCall.get("parameters");
            
            log.info("🎯 Function call: {} with parameters: {}", functionName, parameters);
            
            // TODO: Implement function routing based on functionName
            // Example: bookAppointment, cancelAppointment, getAvailableSlots, etc.
            
            // For now, return a simple success response
            return Map.of(
                "result", "Function " + functionName + " executed successfully",
                "data", Map.of("success", true)
            );
            
        } catch (Exception e) {
            log.error("❌ Error processing Vapi function-call: {}", e.getMessage(), e);
            return Map.of("error", e.getMessage());
        }
    }
    
    /**
     * Parse ISO 8601 datetime string to LocalDateTime
     */
    private LocalDateTime parseIsoDateTime(String isoDateTimeStr) {
        try {
            return LocalDateTime.parse(isoDateTimeStr, DateTimeFormatter.ISO_DATE_TIME);
        } catch (Exception e) {
            log.error("❌ Error parsing datetime: {}", isoDateTimeStr, e);
            return LocalDateTime.now();
        }
    }
}
