package com.saas.voip.controller;

import com.saas.admin.entity.PhoneNumber;
import com.saas.admin.entity.Tenant;
import com.saas.admin.repository.PhoneNumberRepository;
import com.saas.admin.repository.TenantRepository;
import com.saas.shared.core.TenantContext;
import com.saas.shared.enums.Provider;
import com.saas.tenant.entity.InboundCallRequest;
import com.saas.tenant.entity.CallCostRecord;
import com.saas.tenant.service.InboundCallService;
import com.saas.tenant.service.CallCostTrackingService;
import com.saas.voip.service.SmsService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

/**
 * Controller for handling Telnyx Voice AI events and function calls
 * Similar to OpenAI Realtime API but using Telnyx native AI
 */
@RestController
@RequestMapping("/api/voip/telnyx/ai")
@Slf4j
@RequiredArgsConstructor
public class TelnyxEventController {

    private final InboundCallService inboundCallService;
    private final SmsService smsService;
    private final PhoneNumberRepository phoneNumberRepository;
    private final TenantRepository tenantRepository;
    private final CallCostTrackingService callCostTrackingService;

    /**
     * Webhook for Telnyx AI conversation events
     * Receives structured data from "Gather using AI" feature
     * Telnyx sends form-urlencoded data, not JSON
     */
    @PostMapping("/conversation-event")
    public ResponseEntity<Map<String, Object>> handleConversationEvent(
            HttpServletRequest request,
            @RequestParam Map<String, String> allParams) {
        
        try {
            log.info("=== TELNYX AI CONVERSATION EVENT ===");
            log.info("📥 Content-Type: {}", request.getContentType());
            log.info("📥 All parameters: {}", allParams);
            
            // Telnyx sends form-urlencoded webhooks
            // Log and acknowledge receipt
            if (allParams.isEmpty()) {
                log.warn("⚠️ No parameters received");
                return ResponseEntity.ok(Map.of("status", "no_params"));
            }
            
            // Check if this is a call-cost-events webhook
            String callbackSource = allParams.get("CallbackSource");
            if ("call-cost-events".equals(callbackSource)) {
                handleCallCostEvent(allParams);
            }
            
            // For now, just log the event and return success
            // Telnyx AI events might not need processing here
            return ResponseEntity.ok(Map.of("status", "received", "params_count", allParams.size()));
        } catch (Exception e) {
            log.error("❌ Error processing Telnyx event", e);
            return ResponseEntity.ok(Map.of("status", "error"));
        }
    }
    
    /**
     * Handle call cost webhook from Telnyx
     * Example params:
     * - CallCost[v3:_CZ1yMfu...]=0.0055
     * - BilledDurationSeconds[v3:_CZ1yMfu...]=60
     * - CallSid=v3:_CZ1yMfu...
     * - From=+212661979197
     * - To=+18066983980
     */
    private void handleCallCostEvent(Map<String, String> params) {
        try {
            log.info("💰 Processing call-cost-events webhook");
            
            String callSid = params.get("CallSid");
            String from = params.get("From");
            String to = params.get("To");
            
            if (callSid == null || from == null || to == null) {
                log.warn("⚠️ Missing required params (CallSid, From, or To) in call-cost webhook");
                return;
            }
            
            // Find call cost in params - format: CallCost[callSid]=value
            String callCostKey = "CallCost[" + callSid + "]";
            String billedDurationKey = "BilledDurationSeconds[" + callSid + "]";
            
            String costStr = params.get(callCostKey);
            String durationStr = params.get(billedDurationKey);
            
            if (costStr == null) {
                log.warn("⚠️ No CallCost found for CallSid: {}", callSid);
                return;
            }
            
            // Parse values
            double totalCost = Double.parseDouble(costStr);
            int billedSeconds = durationStr != null ? Integer.parseInt(durationStr) : 0;
            
            log.info("💵 Call cost received - CallSid: {}, Cost: ${}, Duration: {}s", 
                     callSid, totalCost, billedSeconds);
            
            // Identify tenant via phone number
            Optional<PhoneNumber> phoneOpt = phoneNumberRepository.findByPhoneNumber(to);
            
            if (phoneOpt.isEmpty() || phoneOpt.get().getProvider() != Provider.TELNYX) {
                log.warn("⚠️ No Telnyx phone number found for: {}", to);
                return;
            }
            
            PhoneNumber phoneNumber = phoneOpt.get();
            String tenantId = phoneNumber.getTenantId();
            
            Optional<Tenant> tenantOpt = tenantRepository.findByTenantId(tenantId);
            if (tenantOpt.isEmpty()) {
                log.error("❌ Tenant not found for phone: {}", to);
                return;
            }
            
            Tenant tenant = tenantOpt.get();
            String schemaName = tenant.getSchemaName();
            
            // Build CallCostRecord entity
            CallCostRecord costRecord = new CallCostRecord();
            costRecord.setCallSid(callSid);
            costRecord.setProvider("TELNYX");
            costRecord.setCallDurationSeconds(billedSeconds);
            costRecord.setCost(BigDecimal.valueOf(totalCost));
            costRecord.setCurrency("USD");
            costRecord.setFromNumber(from);
            costRecord.setToNumber(to);
            costRecord.setCallEndTime(LocalDateTime.now());
            
            // Add simple cost details
            Map<String, Object> costDetails = new HashMap<>();
            costDetails.put("billed_duration_seconds", billedSeconds);
            costDetails.put("total_cost_usd", totalCost);
            costDetails.put("tenant_schema", schemaName);
            costDetails.put("webhook_source", "call-cost-events");
            costRecord.setCostDetails(costDetails);
            
            // Save to admin database (saas_db) - TenantContext is already "saas_db"
            callCostTrackingService.saveCallCost(costRecord);
            
            log.info("✅ Call cost saved to admin database (saas_db) - CallSid: {}, Tenant: {}, Cost: ${}", 
                     callSid, tenantId, totalCost);
            
        } catch (Exception e) {
            log.error("❌ Error processing call cost event", e);
        }
    }

    /**
     * Handle Gather completed - AI extracted structured data
     */
    private void handleGatherCompleted(Map<String, Object> payload, String callSessionId, String schemaName, PhoneNumber phoneNumber) {
        try {
            Map<String, Object> gatheredData = (Map<String, Object>) payload.get("gathered_data");
            
            if (gatheredData == null) {
                log.warn("⚠️ No gathered data in Gather completed event");
                return;
            }
            
            log.info("📋 Gather completed - Extracted data: {}", gatheredData);
            
            // Extract patient data similar to OpenAI function calling
            String patientName = (String) gatheredData.get("patient_name");
            String patientPhone = (String) gatheredData.get("patient_phone");
            String illness = (String) gatheredData.get("reason_for_visit");
            String doctorName = (String) gatheredData.get("doctor_name");
            String appointmentDateStr = (String) gatheredData.get("appointment_date");
            String appointmentTimeStr = (String) gatheredData.get("appointment_time");
            
            // SET TENANT CONTEXT before saving!
            TenantContext.setTenantId(schemaName);
            
            try {
                // Save to InboundCallRequest (using French field names)
                InboundCallRequest callRequest = new InboundCallRequest();
                callRequest.setCallSid(callSessionId);
                callRequest.setNom(patientName);
                callRequest.setTelephone(patientPhone);
                callRequest.setMaladie(illness);
                callRequest.setDoctorName(doctorName);
                
                // Parse appointment date/time if available
                if (appointmentDateStr != null && appointmentTimeStr != null) {
                    try {
                        String dateTimeStr = appointmentDateStr + " " + appointmentTimeStr;
                        LocalDateTime appointmentDateTime = LocalDateTime.parse(dateTimeStr, 
                            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
                        callRequest.setAppointmentDateTime(appointmentDateTime);
                    } catch (Exception e) {
                        log.warn("Failed to parse appointment date/time: {} {}", appointmentDateStr, appointmentTimeStr);
                    }
                }
                
                inboundCallService.savePatientRequest(callRequest);
                log.info("✅ Telnyx gathered data saved to tenant schema: {}", schemaName);
            } finally {
                TenantContext.clear();
            }
            
        } catch (Exception e) {
            log.error("Error handling Gather completed", e);
        }
    }

    /**
     * Handle function call from Telnyx AI
     */
    private void handleFunctionCall(Map<String, Object> payload, String callSessionId, String schemaName, PhoneNumber phoneNumber) {
        try {
            String functionName = (String) payload.get("function_name");
            Map<String, Object> arguments = (Map<String, Object>) payload.get("arguments");
            
            log.info("🔧 Function called: {} with args: {}", functionName, arguments);
            
            if ("confirm_appointment".equals(functionName)) {
                handleConfirmAppointment(callSessionId, schemaName, arguments, phoneNumber);
            }
            
        } catch (Exception e) {
            log.error("Error handling function call", e);
        }
    }

    /**
     * Handle appointment confirmation and send SMS
     */
    private void handleConfirmAppointment(String callSessionId, String schemaName, Map<String, Object> arguments, PhoneNumber phoneNumber) {
        try {
            // SET TENANT CONTEXT before database operations
            TenantContext.setTenantId(schemaName);
            
            try {
                // Get call request from database
                Optional<InboundCallRequest> requestOpt = inboundCallService.getPatientRequestByCallSid(callSessionId);
                
                if (requestOpt.isEmpty()) {
                    log.warn("⚠️ Call request not found for CallSid: {}", callSessionId);
                    return;
                }
                
                InboundCallRequest request = requestOpt.get();
                request.setAppointmentConfirmed(true);
                inboundCallService.savePatientRequest(request);
                
                // Send SMS confirmation via Telnyx using unified SmsService
                if (request.getTelephone() != null && request.getAppointmentDateTime() != null) {
                    String smsSid = smsService.sendAppointmentConfirmation(
                        SmsService.Provider.TELNYX,
                        phoneNumber.getPhoneNumber(),
                        request.getTelephone(),
                        request.getNom(),
                        request.getDoctorName(),
                        request.getAppointmentDateTime(),
                        null
                    );
                    
                    if (smsSid != null) {
                        request.setSmsSent(true);
                        request.setSmsSid(smsSid);
                        inboundCallService.savePatientRequest(request);
                        log.info("✅ SMS confirmation sent via Telnyx - SID: {}", smsSid);
                    }
                }
                
                log.info("✅ Appointment confirmed in tenant schema: {}", schemaName);
            } finally {
                TenantContext.clear();
            }
            
        } catch (Exception e) {
            log.error("Error confirming appointment", e);
        }
    }

    /**
     * Handle transcript updates
     */
    private void handleTranscriptUpdate(Map<String, Object> payload, String callSessionId, String schemaName) {
        try {
            List<Map<String, Object>> transcript = (List<Map<String, Object>>) payload.get("transcript");
            
            if (transcript != null && !transcript.isEmpty()) {
                log.info("📝 Transcript updated for call: {} - {} messages", callSessionId, transcript.size());
                
                // SET TENANT CONTEXT before database operations
                TenantContext.setTenantId(schemaName);
                
                try {
                    // Update transcript in database
                    inboundCallService.updateTranscript(callSessionId, transcript);
                    log.info("✅ Transcript updated in tenant schema: {}", schemaName);
                } finally {
                    TenantContext.clear();
                }
            }
            
        } catch (Exception e) {
            log.error("Error updating transcript", e);
        }
    }

    @GetMapping("/health")
    public String health() {
        return "Telnyx AI Event Service is running!";
    }
}
