package com.saas.voip.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
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.tenant.entity.CallCostRecord;
import com.saas.tenant.entity.InboundCallRequest;
import com.saas.tenant.service.CallCostTrackingService;
import com.saas.tenant.service.InboundCallService;
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.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

@RestController
@RequestMapping("/api/voip/elevenlabs")
@RequiredArgsConstructor
@Slf4j
public class ElevenLabsPostCallWebhookController {
    
    private final ObjectMapper objectMapper;
    private final InboundCallService inboundCallService;
    private final CallCostTrackingService callCostTrackingService;
    private final PhoneNumberRepository phoneNumberRepository;
    private final TenantRepository tenantRepository;
    
    @PostMapping("/post-call-webhook")
    public ResponseEntity<String> handlePostCallWebhook(@RequestBody Map<String, Object> payload) {
        
        try {
            log.info("=== ELEVENLABS POST-CALL WEBHOOK RECEIVED ===");
            log.info("Payload: {}", objectMapper.writeValueAsString(payload));
            
            String eventType = (String) payload.get("type");
            
            if (!"post_call_transcription".equals(eventType)) {
                log.warn("⚠️ Unsupported event type: {}", eventType);
                return ResponseEntity.ok("OK");
            }
            
            Map<String, Object> data = (Map<String, Object>) payload.get("data");
            
            if (data == null) {
                log.warn("⚠️ No data in webhook payload");
                return ResponseEntity.ok("OK");
            }
            
            String conversationId = (String) data.get("conversation_id");
            String agentId = (String) data.get("agent_id");
            
            List<Map<String, Object>> transcript = (List<Map<String, Object>>) data.get("transcript");
            Map<String, Object> metadata = (Map<String, Object>) data.get("metadata");
            Map<String, Object> analysis = (Map<String, Object>) data.get("analysis");
            
            log.info("📞 Conversation ID: {}, Agent ID: {}", conversationId, agentId);
            
            if (metadata != null) {
                Integer callDurationSecs = (Integer) metadata.get("call_duration_secs");
                Integer cost = (Integer) metadata.get("cost"); // Credits
                Long startTimeUnix = ((Number) metadata.get("start_time_unix_secs")).longValue();
                Map<String, Object> dynamicVars = (Map<String, Object>) metadata.get("dynamic_variables");
                
                log.info("⏱️ Duration: {} seconds, Cost: {} credits", callDurationSecs, cost);
                
                String phoneNumber = null;
                if (dynamicVars != null) {
                    phoneNumber = (String) dynamicVars.get("To");
                }
                
                String schemaName = identifyTenantSchema(phoneNumber);
                
                if (schemaName == null) {
                    log.warn("⚠️ Cannot identify tenant for phone: {}", phoneNumber);
                    return ResponseEntity.ok("OK");
                }
                
                // Set TenantContext for patient data (uses JPA repository)
                TenantContext.setTenantId(schemaName);
                
                try {
                    savePatientData(conversationId, transcript, analysis);
                    log.info("✅ ElevenLabs patient data saved to tenant schema: {}", schemaName);
                } finally {
                    TenantContext.clear();
                }
                
                // Save cost record to admin database ONLY - cost data is admin-only information
                saveCostRecord(conversationId, callDurationSecs, cost, startTimeUnix, phoneNumber);
                log.info("✅ ElevenLabs cost record saved to admin database (saas_db)");
            }
            
            return ResponseEntity.ok("OK");
            
        } catch (Exception e) {
            log.error("❌ Error processing ElevenLabs post-call webhook", e);
            return ResponseEntity.status(500).body("Error");
        }
    }
    
    private void savePatientData(String conversationId, List<Map<String, Object>> transcript, 
                                 Map<String, Object> analysis) {
        try {
            if (analysis == null) {
                log.warn("⚠️ No analysis data available");
                return;
            }
            
            List<Map<String, Object>> evaluationResults = 
                (List<Map<String, Object>>) analysis.get("evaluation_criteria_results");
            
            if (evaluationResults == null || evaluationResults.isEmpty()) {
                log.warn("⚠️ No evaluation criteria results");
                return;
            }
            
            InboundCallRequest callRequest = new InboundCallRequest();
            callRequest.setCallSid(conversationId);
            
            for (Map<String, Object> result : evaluationResults) {
                String criteriaName = (String) result.get("criteria");
                Object value = result.get("value");
                
                if (value == null) continue;
                
                if (criteriaName.contains("patient_name") || criteriaName.contains("nom")) {
                    callRequest.setNom(value.toString());
                } else if (criteriaName.contains("phone") || criteriaName.contains("telephone")) {
                    callRequest.setTelephone(value.toString());
                } else if (criteriaName.contains("birthdate") || criteriaName.contains("date_naissance")) {
                    callRequest.setDateNaissance(value.toString());
                } else if (criteriaName.contains("illness") || criteriaName.contains("maladie")) {
                    callRequest.setMaladie(value.toString());
                } else if (criteriaName.contains("doctor") || criteriaName.contains("medecin")) {
                    callRequest.setDoctorName(value.toString());
                } else if (criteriaName.contains("appointment_date")) {
                    callRequest.setMotifVisite(value.toString());
                } else if (criteriaName.contains("confirmed")) {
                    callRequest.setAppointmentConfirmed(Boolean.parseBoolean(value.toString()));
                }
            }
            
            if (transcript != null) {
                callRequest.setConversationTranscript(objectMapper.writeValueAsString(transcript));
            }
            
            inboundCallService.savePatientRequest(callRequest);
            
            log.info("📋 Patient data saved from ElevenLabs - Name: {}, Doctor: {}", 
                    callRequest.getNom(), callRequest.getDoctorName());
            
        } catch (Exception e) {
            log.error("❌ Error saving patient data from ElevenLabs", e);
        }
    }
    
    private void saveCostRecord(String conversationId, Integer durationSecs, Integer credits, 
                                Long startTimeUnix, String phoneNumber) {
        try {
            // Set TenantContext to saas_db to save costs in admin database ONLY
            TenantContext.setTenantId("saas_db");
            
            CallCostRecord costRecord = new CallCostRecord();
            costRecord.setCallSid(conversationId);
            costRecord.setProvider("ELEVENLABS");
            costRecord.setCallDurationSeconds(durationSecs);
            
            BigDecimal costUsd = calculateElevenLabsCost(credits);
            costRecord.setCost(costUsd);
            costRecord.setCurrency("USD");
            
            Map<String, Object> details = new HashMap<>();
            details.put("credits", credits);
            details.put("credits_per_minute", 667);
            costRecord.setCostDetails(details);
            
            LocalDateTime startTime = LocalDateTime.ofInstant(
                Instant.ofEpochSecond(startTimeUnix), ZoneId.systemDefault());
            costRecord.setCallStartTime(startTime);
            costRecord.setCallEndTime(startTime.plusSeconds(durationSecs));
            
            costRecord.setToNumber(phoneNumber);
            
            // Save to admin database (saas_db) - cost data is administrative information
            callCostTrackingService.saveCallCost(costRecord);
            
            log.info("💰 ElevenLabs cost record saved to ADMIN database (saas_db) - {} credits = ${} USD", 
                    credits, costUsd);
            
        } catch (Exception e) {
            log.error("❌ Error saving cost record to admin database", e);
        } finally {
            TenantContext.clear();
        }
    }
    
    private BigDecimal calculateElevenLabsCost(Integer credits) {
        double costPer10kCredits = 1.0;
        double costUsd = (credits / 10000.0) * costPer10kCredits;
        return BigDecimal.valueOf(costUsd).setScale(6, BigDecimal.ROUND_HALF_UP);
    }
    
    private String identifyTenantSchema(String phoneNumber) {
        if (phoneNumber == null) {
            return null;
        }
        
        try {
            // Set context to saas_db to query admin tables
            TenantContext.setTenantId("saas_db");
            
            Optional<PhoneNumber> phoneOpt = phoneNumberRepository.findByPhoneNumber(phoneNumber);
            
            if (phoneOpt.isEmpty()) {
                return null;
            }
            
            String tenantId = phoneOpt.get().getTenantId();
            Optional<Tenant> tenantOpt = tenantRepository.findByTenantId(tenantId);
            
            if (tenantOpt.isEmpty()) {
                return null;
            }
            
            return tenantOpt.get().getSchemaName();
            
        } finally {
            TenantContext.clear();
        }
    }
}
