package com.saas.voip.controller;

import com.saas.admin.entity.PhoneNumber;
import com.saas.admin.entity.Tenant;
import com.saas.admin.repository.TenantRepository;
import com.saas.shared.core.TenantContext;
import com.saas.shared.enums.Provider;
import com.saas.shared.service.PhoneNumberBoundaryService;
import com.saas.tenant.entity.InboundCallData;
import com.saas.tenant.entity.InboundCallRequest;
import com.saas.tenant.service.InboundCallService;
import com.saas.voip.service.TwilioCostService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/voip/twilio")
@RequiredArgsConstructor
@Slf4j
public class TwilioStatusCallbackController {
    
    private final TwilioCostService twilioCostService;
    private final InboundCallService inboundCallService;
    private final PhoneNumberBoundaryService phoneNumberBoundaryService;
    private final TenantRepository tenantRepository;
    private final ObjectMapper objectMapper;
    
    @PostMapping(value = "/status-callback", produces = MediaType.APPLICATION_XML_VALUE)
    public String handleStatusCallback(HttpServletRequest request) {
        log.info("╔═══════════════════════════════════════════════════════╗");
        log.info("║     TWILIO STATUS CALLBACK WEBHOOK RECEIVED          ║");
        log.info("╚═══════════════════════════════════════════════════════╝");
        
        log.info("🌐 [TwilioStatusCallback] Request URL: {}", request.getRequestURL());
        log.info("🌐 [TwilioStatusCallback] Request Method: {}", request.getMethod());
        log.info("🌐 [TwilioStatusCallback] Content-Type: {}", request.getContentType());
        
        MultiValueMap<String, String> formData = extractFormData(request);
        log.info("📦 [TwilioStatusCallback] Extracted {} form parameters", formData.size());
        
        String callSid = formData.getFirst("CallSid");
        String callStatus = formData.getFirst("CallStatus");
        String callDuration = formData.getFirst("CallDuration");
        String priceString = formData.getFirst("Price");
        String priceUnit = formData.getFirst("PriceUnit");
        String from = formData.getFirst("From");
        String to = formData.getFirst("To");
        String timestamp = formData.getFirst("Timestamp");
        String direction = formData.getFirst("Direction");
        
        log.info("🎯 [TwilioStatusCallback] Parsed webhook data:");
        log.info("   ├─ CallSid: {}", callSid);
        log.info("   ├─ CallStatus: {}", callStatus);
        log.info("   ├─ Duration: {}s", callDuration);
        log.info("   ├─ Price: {} {}", priceString, priceUnit);
        log.info("   ├─ From: {}", from);
        log.info("   ├─ To: {}", to);
        log.info("   ├─ Direction: {}", direction);
        log.info("   └─ Timestamp: {}", timestamp);
        
        // Log all parameters in DEBUG mode
        if (log.isDebugEnabled()) {
            log.debug("📋 [TwilioStatusCallback] All webhook parameters:");
            formData.forEach((key, values) -> 
                log.debug("   ├─ {}: {}", key, values.get(0))
            );
        }
        
        // Fetch call costs from Twilio API when call is completed
        if ("completed".equals(callStatus)) {
            log.info("✅ [TwilioStatusCallback] Call status is 'completed' - processing call cost...");
            
            // Resolve tenant ID for cost attribution using boundary service
            String tenantIdForCost = phoneNumberBoundaryService.resolveTenantIdFromPhoneNumber(to);
            if (tenantIdForCost != null) {
                log.info("📊 [TwilioStatusCallback] Tenant identified for cost tracking: {}", tenantIdForCost);
            } else {
                log.warn("⚠️ [TwilioStatusCallback] Could not determine tenant for cost - will save without tenant_id");
            }
            
            // ===== RÉCUPÉRER ET STOCKER LA CONVERSATION =====
            try {
                log.info("📥 [TwilioStatusCallback] Retrieving conversation for CallSid: {}", callSid);
                
                // Find tenant and phone via boundary service
                Optional<PhoneNumber> phoneOpt = phoneNumberBoundaryService.findByPhoneNumber(to);
                if (phoneOpt.isPresent() && phoneOpt.get().getProvider() == Provider.TWILIO) {
                    String tenantId = phoneOpt.get().getTenantId();
                    Optional<Tenant> tenantOpt = tenantRepository.findByTenantId(tenantId);
                    
                    if (tenantOpt.isPresent()) {
                        String schemaName = tenantOpt.get().getSchemaName();
                        
                        // Set tenant context to read from tenant DB
                        TenantContext.setTenantId(schemaName);
                        
                        try {
                            // Get conversation from InboundCallRequest (already saved by OpenAIRealtimeService)
                            // NOTE: OpenAIRealtimeService also extracts patient/RDV data DURING the call via confirm_appointment function
                            Optional<InboundCallRequest> requestOpt = inboundCallService.getPatientRequestByCallSid(callSid);
                            
                            if (requestOpt.isPresent()) {
                                InboundCallRequest patientRequest = requestOpt.get();
                                
                                // Log patient data extraction status
                                if (patientRequest.getNom() != null || patientRequest.getTelephone() != null) {
                                    log.info("✅ [TwilioStatusCallback] Patient data found: Name={}, Phone={}, Doctor={}, Appointment={}", 
                                             patientRequest.getNom(), patientRequest.getTelephone(), 
                                             patientRequest.getDoctorName(), patientRequest.getAppointmentDateTime());
                                } else {
                                    log.info("ℹ️ [TwilioStatusCallback] No patient data extracted (patient may not have provided info)");
                                }
                                
                                // Copy conversation to InboundCallData
                                String conversationJson = patientRequest.getConversationTranscript();
                            
                            if (conversationJson != null) {
                                
                                    // Parse JSON to List<Object>
                                    List<Object> conversation = objectMapper.readValue(conversationJson, new TypeReference<List<Object>>() {});
                                    
                                    // Get InboundCallData and update with conversation
                                    Optional<InboundCallData> callDataOpt = inboundCallService.getCallByCallSid(callSid);
                                    
                                    if (callDataOpt.isPresent()) {
                                        InboundCallData callData = callDataOpt.get();
                                        callData.setConversation(conversation);
                                        callData.setDuration(callDuration != null ? Integer.parseInt(callDuration) : null);
                                        
                                        // Save to BOTH admin and tenant databases
                                        inboundCallService.saveInBothDatabases(callData, tenantId, schemaName);
                                        log.info("✅ [TwilioStatusCallback] Conversation stored in InboundCallData ({} messages)", conversation.size());
                                    } else {
                                        log.warn("⚠️ [TwilioStatusCallback] No InboundCallData found for CallSid: {}", callSid);
                                    }
                                } else {
                                    log.info("⏭️ [TwilioStatusCallback] No conversation transcript available yet for CallSid: {}", callSid);
                                }
                            } else {
                                log.info("⏭️ [TwilioStatusCallback] No InboundCallRequest found for CallSid: {}", callSid);
                            }
                        } finally {
                            TenantContext.clear();
                        }
                    }
                }
            } catch (Exception convEx) {
                log.error("❌ [TwilioStatusCallback] Failed to store conversation", convEx);
            }
            
            // Process call cost
            try {
                log.info("🚀 [TwilioStatusCallback] Attempting to fetch cost from Twilio API...");
                log.info("🚀 [TwilioStatusCallback] Calling TwilioCostService.fetchAndSaveCallCost()...");
                
                twilioCostService.fetchAndSaveCallCost(callSid, from, to, tenantIdForCost);
                
                log.info("✅ [TwilioStatusCallback] Twilio API call cost retrieval initiated successfully!");
                log.info("   ├─ CallSid: {}", callSid);
                log.info("   ├─ TenantId: {}", tenantIdForCost);
                log.info("   └─ Method: Twilio API (preferred)");
                
            } catch (Exception e) {
                log.error("╔═══════════════════════════════════════════════════════╗");
                log.error("║     TWILIO API COST FETCH FAILED                     ║");
                log.error("╚═══════════════════════════════════════════════════════╝");
                log.error("❌ [TwilioStatusCallback] EXCEPTION while fetching Twilio call cost!", e);
                log.error("❌ [TwilioStatusCallback] Exception type: {}", e.getClass().getName());
                log.error("❌ [TwilioStatusCallback] Exception message: {}", e.getMessage());
                
                // Fallback: Use webhook data if API fails (e.g., Test Account)
                if (priceString != null && !priceString.isEmpty()) {
                    log.warn("⚠️ [TwilioStatusCallback] ════════════════════════════════");
                    log.warn("⚠️ [TwilioStatusCallback] API FAILED - USING WEBHOOK FALLBACK");
                    log.warn("⚠️ [TwilioStatusCallback] ════════════════════════════════");
                    log.info("📥 [TwilioStatusCallback] Webhook price data available:");
                    log.info("   ├─ Price: {}", priceString);
                    log.info("   ├─ Currency: {}", priceUnit);
                    log.info("   └─ Duration: {}s", callDuration);
                    
                    try {
                        log.info("💾 [TwilioStatusCallback] Saving cost from webhook data...");
                        twilioCostService.saveCallCostFromWebhook(callSid, priceString, priceUnit, 
                            callDuration, timestamp, from, to, tenantIdForCost);
                        log.info("✅ [TwilioStatusCallback] Call cost saved from webhook data successfully!");
                        log.info("   ├─ CallSid: {}", callSid);
                        log.info("   ├─ TenantId: {}", tenantIdForCost);
                        log.info("   ├─ Method: Webhook fallback");
                        log.info("   └─ Reason: Twilio API unavailable (likely Test Account)");
                    } catch (Exception webhookEx) {
                        log.error("❌ [TwilioStatusCallback] WEBHOOK FALLBACK ALSO FAILED!", webhookEx);
                        log.error("❌ [TwilioStatusCallback] Exception type: {}", webhookEx.getClass().getName());
                        log.error("❌ [TwilioStatusCallback] Exception message: {}", webhookEx.getMessage());
                    }
                } else {
                    log.error("❌ [TwilioStatusCallback] No webhook price data available!");
                    log.error("❌ [TwilioStatusCallback] Cannot save call cost - both API and webhook failed");
                    log.error("❌ [TwilioStatusCallback] Recommendations:");
                    log.error("   ├─ Verify Twilio account status (Test vs Live)");
                    log.error("   ├─ Check that webhook includes Price and PriceUnit");
                    log.error("   └─ Review Twilio webhook configuration");
                }
            }
        } else {
            log.info("⏭️ [TwilioStatusCallback] Call status is '{}' (not 'completed') - skipping cost fetch", callStatus);
            log.info("⏭️ [TwilioStatusCallback] Cost tracking only occurs for completed calls");
        }
        
        log.info("✅ [TwilioStatusCallback] Webhook processing complete - returning TwiML response");
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response></Response>";
    }
    
    private MultiValueMap<String, String> extractFormData(HttpServletRequest request) {
        log.debug("📤 [TwilioStatusCallback] Extracting form data from request");
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        
        try {
            // Read request body
            BufferedReader reader = request.getReader();
            StringBuilder body = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                body.append(line);
            }
            
            log.debug("📄 [TwilioStatusCallback] Raw request body: {}", body.toString());
            
            // Parse URL-encoded form data
            String[] pairs = body.toString().split("&");
            log.debug("📋 [TwilioStatusCallback] Parsing {} key-value pairs", pairs.length);
            
            for (String pair : pairs) {
                String[] keyValue = pair.split("=", 2);
                if (keyValue.length == 2) {
                    String key = java.net.URLDecoder.decode(keyValue[0], "UTF-8");
                    String value = java.net.URLDecoder.decode(keyValue[1], "UTF-8");
                    formData.add(key, value);
                    log.debug("   ├─ {} = {}", key, value);
                }
            }
        } catch (Exception e) {
            log.error("❌ [TwilioStatusCallback] Error reading request body", e);
            log.error("❌ [TwilioStatusCallback] Exception type: {}", e.getClass().getName());
            log.error("❌ [TwilioStatusCallback] Exception message: {}", e.getMessage());
        }
        
        // Also check URL parameters (fallback)
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String[] paramValues = request.getParameterValues(paramName);
            for (String paramValue : paramValues) {
                if (!formData.containsKey(paramName)) {
                    formData.add(paramName, paramValue);
                    log.debug("   ├─ {} = {} (from URL params)", paramName, paramValue);
                }
            }
        }
        
        log.debug("✅ [TwilioStatusCallback] Form data extraction complete - {} parameters", formData.size());
        return formData;
    }
}
