package com.saas.voip.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.saas.admin.dto.*;
import com.saas.shared.core.TenantContext;
import com.saas.tenant.entity.VapiAssistant;
import com.saas.tenant.entity.VapiCall;
import com.saas.tenant.repository.VapiAssistantRepository;
import com.saas.tenant.repository.VapiCallRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Vapi.ai Service - Manages voice assistants and calls
 * 
 * Integrates with Vapi.ai REST API for:
 * - Creating/updating/deleting assistants
 * - Starting outbound calls
 * - Retrieving call data
 * 
 * Architecture:
 * - Multi-tenant support via TenantContext routing
 * - RestTemplate for HTTP calls to Vapi.ai API
 * - Configuration externalized via @Value
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class VapiService {

    private final VapiAssistantRepository assistantRepository;
    private final VapiCallRepository callRepository;
    private final RestTemplate restTemplate = new RestTemplate();
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Value("${vapi.api.token:}")
    private String vapiApiToken;

    @Value("${vapi.api.base-url:https://api.vapi.ai}")
    private String vapiApiBaseUrl;

    // ========================================
    // ASSISTANT MANAGEMENT
    // ========================================

    /**
     * Create a new Vapi.ai assistant
     */
    public VapiAssistantDTO createAssistant(String tenantId, CreateVapiAssistantDTO dto) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("🚀 Creating Vapi assistant for tenant: {}", tenantId);

            // Build request payload
            Map<String, Object> payload = new HashMap<>();
            payload.put("name", dto.getName());

            // Model configuration
            Map<String, Object> model = new HashMap<>();
            model.put("provider", "openai");
            model.put("model", dto.getModel());
            model.put("messages", List.of(
                    Map.of("role", "system", "content",
                            dto.getPrompt() != null ? dto.getPrompt() : "You are a helpful assistant.")));
            payload.put("model", model);

            // Voice configuration
            Map<String, Object> voice = new HashMap<>();
            voice.put("voiceId", dto.getVoice());
            payload.put("voice", voice);

            // First message
            if (dto.getFirstMessage() != null) {
                payload.put("firstMessage", dto.getFirstMessage());
            }

            // Server URL for webhooks
            if (dto.getServerUrl() != null) {
                payload.put("serverUrl", dto.getServerUrl());
                if (dto.getServerUrlSecret() != null) {
                    payload.put("serverUrlSecret", dto.getServerUrlSecret());
                }
            }

            // Additional config
            if (dto.getConfig() != null) {
                payload.putAll(dto.getConfig());
            }

            // Call Vapi API
            HttpHeaders headers = createHeaders();
            HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);

            String url = vapiApiBaseUrl + "/assistant";
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, request, Map.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                String vapiAssistantId = (String) response.getBody().get("id");

                // Save to database
                VapiAssistant assistant = VapiAssistant.builder()
                        .vapiAssistantId(vapiAssistantId)
                        .name(dto.getName())
                        .model(dto.getModel())
                        .voice(dto.getVoice())
                        .prompt(dto.getPrompt())
                        .firstMessage(dto.getFirstMessage())
                        .serverUrl(dto.getServerUrl())
                        .serverUrlSecret(dto.getServerUrlSecret())
                        .config(dto.getConfig())
                        .status("ACTIVE")
                        .build();

                assistant = assistantRepository.save(assistant);
                log.info("✅ Vapi assistant created successfully: {} (ID: {})", assistant.getName(), vapiAssistantId);

                return toAssistantDTO(assistant, tenantId);
            } else {
                log.error("❌ Failed to create Vapi assistant. Status: {}", response.getStatusCode());
                throw new RuntimeException("Failed to create Vapi assistant");
            }

        } catch (Exception e) {
            log.error("❌ Error creating Vapi assistant: {}", e.getMessage(), e);
            throw new RuntimeException("Error creating Vapi assistant", e);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * Update an existing Vapi.ai assistant
     */
    public VapiAssistantDTO updateAssistant(String tenantId, Long assistantId, UpdateVapiAssistantDTO dto) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("🔄 Updating Vapi assistant {} for tenant: {}", assistantId, tenantId);

            // Find assistant
            VapiAssistant assistant = assistantRepository.findById(assistantId)
                    .orElseThrow(() -> new RuntimeException("Assistant not found"));

            // Build update payload (only non-null fields)
            Map<String, Object> payload = new HashMap<>();
            if (dto.getName() != null)
                payload.put("name", dto.getName());

            if (dto.getModel() != null || dto.getPrompt() != null) {
                Map<String, Object> model = new HashMap<>();
                model.put("provider", "openai");
                model.put("model", dto.getModel() != null ? dto.getModel() : assistant.getModel());
                model.put("messages", List.of(
                        Map.of("role", "system", "content",
                                dto.getPrompt() != null ? dto.getPrompt() : assistant.getPrompt())));
                payload.put("model", model);
            }

            if (dto.getVoice() != null) {
                payload.put("voice", Map.of("voiceId", dto.getVoice()));
            }

            if (dto.getFirstMessage() != null)
                payload.put("firstMessage", dto.getFirstMessage());
            if (dto.getServerUrl() != null)
                payload.put("serverUrl", dto.getServerUrl());
            if (dto.getServerUrlSecret() != null)
                payload.put("serverUrlSecret", dto.getServerUrlSecret());
            if (dto.getConfig() != null)
                payload.putAll(dto.getConfig());

            // Call Vapi API
            HttpHeaders headers = createHeaders();
            HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);

            String url = vapiApiBaseUrl + "/assistant/" + assistant.getVapiAssistantId();
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.PATCH, request, Map.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                // Update local database
                if (dto.getName() != null)
                    assistant.setName(dto.getName());
                if (dto.getModel() != null)
                    assistant.setModel(dto.getModel());
                if (dto.getVoice() != null)
                    assistant.setVoice(dto.getVoice());
                if (dto.getPrompt() != null)
                    assistant.setPrompt(dto.getPrompt());
                if (dto.getFirstMessage() != null)
                    assistant.setFirstMessage(dto.getFirstMessage());
                if (dto.getServerUrl() != null)
                    assistant.setServerUrl(dto.getServerUrl());
                if (dto.getServerUrlSecret() != null)
                    assistant.setServerUrlSecret(dto.getServerUrlSecret());
                if (dto.getConfig() != null)
                    assistant.setConfig(dto.getConfig());
                if (dto.getStatus() != null)
                    assistant.setStatus(dto.getStatus());

                assistant = assistantRepository.save(assistant);
                log.info("✅ Vapi assistant updated successfully: {}", assistant.getName());

                return toAssistantDTO(assistant, tenantId);
            } else {
                log.error("❌ Failed to update Vapi assistant. Status: {}", response.getStatusCode());
                throw new RuntimeException("Failed to update Vapi assistant");
            }

        } catch (Exception e) {
            log.error("❌ Error updating Vapi assistant: {}", e.getMessage(), e);
            throw new RuntimeException("Error updating Vapi assistant", e);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * Delete a Vapi.ai assistant
     */
    public void deleteAssistant(String tenantId, Long assistantId) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("🗑️ Deleting Vapi assistant {} for tenant: {}", assistantId, tenantId);

            // Find assistant
            VapiAssistant assistant = assistantRepository.findById(assistantId)
                    .orElseThrow(() -> new RuntimeException("Assistant not found"));

            // Call Vapi API
            HttpHeaders headers = createHeaders();
            HttpEntity<Void> request = new HttpEntity<>(headers);

            String url = vapiApiBaseUrl + "/assistant/" + assistant.getVapiAssistantId();
            ResponseEntity<Void> response = restTemplate.exchange(url, HttpMethod.DELETE, request, Void.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                // Mark as deleted (soft delete)
                assistant.setStatus("DELETED");
                assistantRepository.save(assistant);
                log.info("✅ Vapi assistant deleted successfully: {}", assistant.getName());
            } else {
                log.error("❌ Failed to delete Vapi assistant. Status: {}", response.getStatusCode());
                throw new RuntimeException("Failed to delete Vapi assistant");
            }

        } catch (Exception e) {
            log.error("❌ Error deleting Vapi assistant: {}", e.getMessage(), e);
            throw new RuntimeException("Error deleting Vapi assistant", e);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * List all assistants for a tenant
     */
    public List<VapiAssistantDTO> listAssistants(String tenantId) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("📋 Listing Vapi assistants for tenant: {}", tenantId);

            List<VapiAssistant> assistants = assistantRepository.findByStatus("ACTIVE");
            return assistants.stream()
                    .map(assistant -> toAssistantDTO(assistant, tenantId))
                    .collect(Collectors.toList());
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * Fetch all assistants directly from Vapi API
     * Used for Admin Provider View
     */
    public List<Map<String, Object>> fetchAssistantsFromApi() {
        try {
            log.info("📋 Fetching all Vapi assistants from API");

            HttpHeaders headers = createHeaders();
            HttpEntity<?> request = new HttpEntity<>(headers);

            String url = vapiApiBaseUrl + "/assistant";

            ResponseEntity<List> response = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    request,
                    List.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                return (List<Map<String, Object>>) response.getBody();
            }

            return List.of();

        } catch (Exception e) {
            log.error("❌ Error fetching Vapi assistants from API: {}", e.getMessage());
            // Return empty list instead of throwing to prevent UI crash
            return List.of();
        }
    }

    /**
     * Get a single assistant by ID
     */
    public VapiAssistantDTO getAssistant(String tenantId, Long assistantId) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("🔍 Getting Vapi assistant {} for tenant: {}", assistantId, tenantId);

            VapiAssistant assistant = assistantRepository.findById(assistantId)
                    .orElseThrow(() -> new RuntimeException("Assistant not found"));

            return toAssistantDTO(assistant, tenantId);
        } finally {
            TenantContext.clear();
        }
    }

    // ========================================
    // CALL MANAGEMENT
    // ========================================

    /**
     * Start an outbound call with Vapi.ai
     */
    public VapiCallDTO startCall(String tenantId, StartVapiCallDTO dto) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("📞 Starting Vapi call for tenant: {} to number: {}", tenantId, dto.getCustomerNumber());

            // Validate assistant exists
            VapiAssistant assistant = assistantRepository.findById(Long.parseLong(dto.getAssistantId()))
                    .orElseThrow(() -> new RuntimeException("Assistant not found"));

            // Build request payload
            Map<String, Object> payload = new HashMap<>();
            payload.put("assistantId", assistant.getVapiAssistantId());

            Map<String, Object> customer = new HashMap<>();
            customer.put("number", dto.getCustomerNumber());
            payload.put("customer", customer);

            if (dto.getPhoneNumberId() != null) {
                payload.put("phoneNumberId", dto.getPhoneNumberId());
            }

            if (dto.getAssistantOverrides() != null) {
                payload.put("assistantOverrides", dto.getAssistantOverrides());
            }

            // Call Vapi API - POST /call (not /call/phone)
            HttpHeaders headers = createHeaders();
            HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);

            String url = vapiApiBaseUrl + "/call";
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, request, Map.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                String vapiCallId = (String) response.getBody().get("id");
                String status = (String) response.getBody().get("status");

                // Save to database
                VapiCall call = VapiCall.builder()
                        .vapiCallId(vapiCallId)
                        .assistantId(assistant.getVapiAssistantId())
                        .customerNumber(dto.getCustomerNumber())
                        .callType("OUTBOUND")
                        .status(status != null ? status : "queued")
                        .startTime(LocalDateTime.now())
                        .build();

                call = callRepository.save(call);
                log.info("✅ Vapi call started successfully: {} (Status: {})", vapiCallId, status);

                return toCallDTO(call, tenantId);
            } else {
                log.error("❌ Failed to start Vapi call. Status: {}", response.getStatusCode());
                throw new RuntimeException("Failed to start Vapi call");
            }

        } catch (Exception e) {
            log.error("❌ Error starting Vapi call: {}", e.getMessage(), e);
            throw new RuntimeException("Error starting Vapi call", e);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * Get call details
     */
    public VapiCallDTO getCall(String tenantId, String vapiCallId) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("🔍 Getting Vapi call {} for tenant: {}", vapiCallId, tenantId);

            VapiCall call = callRepository.findByVapiCallId(vapiCallId)
                    .orElseThrow(() -> new RuntimeException("Call not found"));

            return toCallDTO(call, tenantId);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * List all calls for a tenant
     */
    public List<VapiCallDTO> listCalls(String tenantId) {
        TenantContext.setTenantId(tenantId);
        try {
            log.info("📋 Listing Vapi calls for tenant: {}", tenantId);

            List<VapiCall> calls = callRepository.findAll();
            return calls.stream()
                    .map(call -> toCallDTO(call, tenantId))
                    .collect(Collectors.toList());
        } finally {
            TenantContext.clear();
        }
    }

    // ========================================
    // HELPER METHODS
    // ========================================

    private HttpHeaders createHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(vapiApiToken);
        return headers;
    }

    private VapiAssistantDTO toAssistantDTO(VapiAssistant assistant, String tenantId) {
        return VapiAssistantDTO.builder()
                .id(assistant.getId())
                .tenantId(tenantId)
                .vapiAssistantId(assistant.getVapiAssistantId())
                .name(assistant.getName())
                .model(assistant.getModel())
                .voice(assistant.getVoice())
                .prompt(assistant.getPrompt())
                .firstMessage(assistant.getFirstMessage())
                .serverUrl(assistant.getServerUrl())
                .config(assistant.getConfig())
                .status(assistant.getStatus())
                .createdAt(assistant.getCreatedAt())
                .updatedAt(assistant.getUpdatedAt())
                .build();
    }

    private VapiCallDTO toCallDTO(VapiCall call, String tenantId) {
        return VapiCallDTO.builder()
                .id(call.getId())
                .tenantId(tenantId)
                .vapiCallId(call.getVapiCallId())
                .assistantId(call.getAssistantId())
                .phoneNumber(call.getPhoneNumber())
                .customerNumber(call.getCustomerNumber())
                .callType(call.getCallType())
                .status(call.getStatus())
                .endReason(call.getEndReason())
                .startTime(call.getStartTime())
                .endTime(call.getEndTime())
                .duration(call.getDuration())
                .transcript(call.getTranscript())
                .recordingUrl(call.getRecordingUrl())
                .cost(call.getCost())
                .costBreakdown(call.getCostBreakdown())
                .analysis(call.getAnalysis())
                .createdAt(call.getCreatedAt())
                .updatedAt(call.getUpdatedAt())
                .build();
    }

    /**
     * Update assistant knowledge base (for AI Knowledge Base system)
     * 
     * @param tenantId     Tenant ID
     * @param assistantId  Vapi assistant ID
     * @param systemPrompt System prompt to update
     * @param ragDocument  RAG document content
     */
    public void updateAssistantKnowledge(String tenantId, String assistantId, String systemPrompt, String ragDocument) {
        log.info("🔄 Updating knowledge for Vapi assistant: {}", assistantId);

        try {
            // Build update payload
            Map<String, Object> payload = new HashMap<>();

            // Update model with new system prompt
            Map<String, Object> model = new HashMap<>();
            model.put("provider", "openai");
            model.put("model", "gpt-4");
            model.put("messages", List.of(
                    Map.of("role", "system", "content", systemPrompt)));
            payload.put("model", model);

            // Update knowledge base
            Map<String, Object> knowledgeBase = new HashMap<>();
            knowledgeBase.put("provider", "custom");
            knowledgeBase.put("documents", List.of(
                    Map.of("content", ragDocument)));
            payload.put("knowledgeBase", knowledgeBase);

            // Call Vapi API
            HttpHeaders headers = createHeaders();
            HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);

            String url = vapiApiBaseUrl + "/assistant/" + assistantId;
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.PATCH, request, Map.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("✅ Vapi assistant knowledge updated successfully");
            } else {
                log.error("❌ Failed to update Vapi assistant knowledge. Status: {}", response.getStatusCode());
            }

        } catch (Exception e) {
            log.error("❌ Error updating Vapi assistant knowledge", e);
            throw new RuntimeException("Failed to update Vapi assistant knowledge", e);
        }
    }
}
