package com.saas.tenant.service;

import com.saas.shared.core.TenantContext;
import com.saas.shared.enums.KnowledgeType;
import com.saas.tenant.entity.*;
import com.saas.admin.entity.TenantAIConfig;
import com.saas.tenant.repository.*;
import com.saas.voip.service.RetellApiClient;
import com.saas.voip.service.VapiService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * Knowledge Base Service
 * 
 * Automatically generates RAG documents and system prompts for AI assistants
 * when tenant data changes (doctors, services, resources, etc.)
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class KnowledgeBaseService {

    private final TenantAIKnowledgeRepository knowledgeRepository;
    private final DoctorRepository doctorRepository;
    private final MedicalServiceRepository serviceRepository;
    private final PhysicalResourceRepository resourceRepository;
    private final TenantAIConfigService aiConfigService;
    private final VapiService vapiService;
    private final RetellApiClient retellApiClient;

    /**
     * Regenerate all knowledge for a tenant
     * Called when any tenant data changes
     */
    @Async
    @Transactional
    public void regenerateAllKnowledge(String tenantId) {
        log.info("🔄 Regenerating all knowledge for tenant: {}", tenantId);

        TenantContext.setTenantId(tenantId);

        try {
            // Generate individual knowledge documents
            generateDoctorsKnowledge(tenantId);
            generateServicesKnowledge(tenantId);
            generateResourcesKnowledge(tenantId);

            // Generate consolidated knowledge
            generateConsolidatedKnowledge(tenantId);

            // Sync to AI providers
            syncToAIProviders(tenantId);

            log.info("✅ Knowledge regeneration completed for tenant: {}", tenantId);

        } catch (Exception e) {
            log.error("❌ Error regenerating knowledge for tenant: {}", tenantId, e);
        } finally {
            TenantContext.clear();
        }
    }

    /**
     * Generate knowledge document for doctors
     */
    @Transactional
    public void generateDoctorsKnowledge(String tenantId) {
        log.info("📝 Generating doctors knowledge for tenant: {}", tenantId);

        List<Doctor> doctors = doctorRepository.findByStatus(DoctorStatus.ACTIVE);

        StringBuilder rag = new StringBuilder();
        rag.append("# MÉDECINS DISPONIBLES\n\n");

        if (doctors.isEmpty()) {
            rag.append("Aucun médecin disponible actuellement.\n");
        } else {
            for (Doctor doctor : doctors) {
                rag.append(String.format(
                        "## Dr. %s %s\n" +
                                "- **Spécialité**: %s\n" +
                                "- **Département**: %s\n" +
                                "- **Langues parlées**: %s\n" +
                                "- **Actes médicaux**: %s\n" +
                                "- **Email**: %s\n" +
                                "- **Téléphone**: %s\n\n",
                        doctor.getFirstName(),
                        doctor.getLastName(),
                        doctor.getMedicalSpecialty(),
                        doctor.getDepartment() != null ? doctor.getDepartment() : "Non spécifié",
                        doctor.getLanguagesSpoken() != null ? doctor.getLanguagesSpoken() : "Français",
                        doctor.getMedicalActsPerformed() != null ? doctor.getMedicalActsPerformed()
                                : "Consultation générale",
                        doctor.getEmail(),
                        doctor.getPhoneNumber()));

                if (doctor.getBiography() != null && !doctor.getBiography().isEmpty()) {
                    rag.append(String.format("- **Biographie**: %s\n\n", doctor.getBiography()));
                }
            }
        }

        // Save knowledge
        saveKnowledge(tenantId, KnowledgeType.DOCTORS, rag.toString(), Map.of(
                "doctorCount", doctors.size(),
                "generatedAt", LocalDateTime.now().toString()));
    }

    /**
     * Generate knowledge document for medical services
     */
    @Transactional
    public void generateServicesKnowledge(String tenantId) {
        log.info("📝 Generating services knowledge for tenant: {}", tenantId);

        List<MedicalService> services = serviceRepository.findAll();

        StringBuilder rag = new StringBuilder();
        rag.append("# SERVICES MÉDICAUX\n\n");

        if (services.isEmpty()) {
            rag.append("Aucun service médical configuré.\n");
        } else {
            // Group by service type
            Map<ServiceType, List<MedicalService>> servicesByType = new HashMap<>();
            for (MedicalService service : services) {
                servicesByType.computeIfAbsent(service.getType(), k -> new java.util.ArrayList<>()).add(service);
            }

            for (Map.Entry<ServiceType, List<MedicalService>> entry : servicesByType.entrySet()) {
                rag.append(String.format("## %s\n\n", entry.getKey()));

                for (MedicalService service : entry.getValue()) {
                    rag.append(String.format(
                            "### %s\n" +
                                    "- **Prix**: %.2f EUR\n" +
                                    "- **Durée moyenne**: %d minutes\n" +
                                    "- **Description**: %s\n",
                            service.getName(),
                            service.getStandardPrice(),
                            service.getDurationMinutes(),
                            service.getDescription() != null ? service.getDescription() : "Non spécifié"));

                    if (service.getAssociatedSymptoms() != null && !service.getAssociatedSymptoms().isEmpty()) {
                        rag.append(String.format("- **Symptômes courants**: %s\n", service.getAssociatedSymptoms()));
                    }

                    if (service.getKeywords() != null && !service.getKeywords().isEmpty()) {
                        rag.append(String.format("- **Mots-clés**: %s\n", service.getKeywords()));
                    }

                    rag.append("\n");
                }
            }
        }

        // Calculate average price
        BigDecimal avgPrice = services.isEmpty() ? BigDecimal.ZERO
                : services.stream()
                        .map(MedicalService::getStandardPrice)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)
                        .divide(BigDecimal.valueOf(services.size()), 2, RoundingMode.HALF_UP);

        // Save knowledge
        saveKnowledge(tenantId, KnowledgeType.SERVICES, rag.toString(), Map.of(
                "serviceCount", services.size(),
                "averagePrice", avgPrice.toString(),
                "generatedAt", LocalDateTime.now().toString()));
    }

    /**
     * Generate knowledge document for physical resources
     */
    @Transactional
    public void generateResourcesKnowledge(String tenantId) {
        log.info("📝 Generating resources knowledge for tenant: {}", tenantId);

        List<PhysicalResource> resources = resourceRepository.findByStatus(ResourceStatus.AVAILABLE);

        StringBuilder rag = new StringBuilder();
        rag.append("# RESSOURCES PHYSIQUES\n\n");

        if (resources.isEmpty()) {
            rag.append("Aucune ressource disponible.\n");
        } else {
            for (PhysicalResource resource : resources) {
                rag.append(String.format(
                        "## %s\n" +
                                "- **Type**: %s\n" +
                                "- **Localisation**: %s\n" +
                                "- **Étage**: %s\n" +
                                "- **Capacité**: %d personnes\n" +
                                "- **Description**: %s\n\n",
                        resource.getName(),
                        resource.getType(),
                        resource.getLocation() != null ? resource.getLocation() : "Non spécifié",
                        resource.getFloorNumber() != null ? resource.getFloorNumber().toString() : "Non spécifié",
                        resource.getCapacity() != null ? resource.getCapacity() : 1,
                        resource.getDescription() != null ? resource.getDescription() : "Standard"));
            }
        }

        // Save knowledge
        saveKnowledge(tenantId, KnowledgeType.RESOURCES, rag.toString(), Map.of(
                "resourceCount", resources.size(),
                "generatedAt", LocalDateTime.now().toString()));
    }

    /**
     * Generate consolidated knowledge document
     * Combines all knowledge types into one document
     */
    @Transactional
    public void generateConsolidatedKnowledge(String tenantId) {
        log.info("📝 Generating consolidated knowledge for tenant: {}", tenantId);

        StringBuilder consolidatedRAG = new StringBuilder();

        // Get all knowledge documents
        Optional<TenantAIKnowledge> doctorsKnowledge = knowledgeRepository
                .findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, KnowledgeType.DOCTORS);
        Optional<TenantAIKnowledge> servicesKnowledge = knowledgeRepository
                .findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, KnowledgeType.SERVICES);
        Optional<TenantAIKnowledge> resourcesKnowledge = knowledgeRepository
                .findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, KnowledgeType.RESOURCES);

        // Combine all RAG documents
        doctorsKnowledge.ifPresent(k -> consolidatedRAG.append(k.getRagDocument()).append("\n\n"));
        servicesKnowledge.ifPresent(k -> consolidatedRAG.append(k.getRagDocument()).append("\n\n"));
        resourcesKnowledge.ifPresent(k -> consolidatedRAG.append(k.getRagDocument()).append("\n\n"));

        // Generate system prompt
        String systemPrompt = generateSystemPrompt(tenantId);

        // Save consolidated knowledge
        TenantAIKnowledge knowledge = TenantAIKnowledge.builder()
                .tenantId(tenantId)
                .knowledgeType(KnowledgeType.CONSOLIDATED)
                .ragDocument(consolidatedRAG.toString())
                .systemPrompt(systemPrompt)
                .isActive(true)
                .metadata(Map.of(
                        "generatedAt", LocalDateTime.now().toString(),
                        "totalLength", consolidatedRAG.length()))
                .build();

        // Deactivate previous consolidated knowledge
        knowledgeRepository.findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, KnowledgeType.CONSOLIDATED)
                .ifPresent(old -> {
                    old.setIsActive(false);
                    knowledgeRepository.save(old);
                });

        // Get next version number
        long versionCount = knowledgeRepository.countByTenantIdAndKnowledgeType(tenantId, KnowledgeType.CONSOLIDATED);
        knowledge.setVersion((int) versionCount + 1);

        knowledgeRepository.save(knowledge);
        log.info("✅ Consolidated knowledge saved (version {})", knowledge.getVersion());
    }

    /**
     * Generate dynamic system prompt based on tenant data
     */
    private String generateSystemPrompt(String tenantId) {
        List<Doctor> doctors = doctorRepository.findByStatus(DoctorStatus.ACTIVE);
        List<MedicalService> services = serviceRepository.findAll();

        BigDecimal avgPrice = services.isEmpty() ? BigDecimal.ZERO
                : services.stream()
                        .map(MedicalService::getStandardPrice)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)
                        .divide(BigDecimal.valueOf(services.size()), 2, RoundingMode.HALF_UP);

        return String.format(
                "# RÔLE\n" +
                        "Tu es l'assistant virtuel de notre clinique médicale. Tu es professionnel, empathique et efficace.\n\n"
                        +
                        "# CONTEXTE DE LA CLINIQUE\n" +
                        "- Nous avons **%d médecins** disponibles\n" +
                        "- Nous proposons **%d services médicaux**\n" +
                        "- Prix moyen d'une consultation: **%.2f EUR**\n\n" +
                        "# TES RESPONSABILITÉS\n" +
                        "1. **Accueillir** chaleureusement le patient\n" +
                        "2. **Comprendre** son besoin médical (symptômes, urgence)\n" +
                        "3. **Proposer** le service et le médecin appropriés\n" +
                        "4. **Prendre rendez-vous** si le patient le souhaite\n" +
                        "5. **Confirmer** tous les détails (date, heure, médecin, prix)\n\n" +
                        "# RÈGLES IMPORTANTES\n" +
                        "- ❌ **NE JAMAIS** donner de diagnostic médical\n" +
                        "- ❌ **NE JAMAIS** prescrire de médicaments\n" +
                        "- ✅ **TOUJOURS** être empathique et rassurant\n" +
                        "- ✅ **TOUJOURS** proposer un rappel si pas de créneau disponible\n" +
                        "- ✅ **TOUJOURS** demander confirmation avant de finaliser un rendez-vous\n" +
                        "- ✅ **TOUJOURS** parler en français (sauf si le patient préfère une autre langue)\n\n" +
                        "# INFORMATIONS À COLLECTER\n" +
                        "Pour prendre un rendez-vous, tu dois obtenir:\n" +
                        "1. Nom et prénom du patient\n" +
                        "2. Numéro de téléphone\n" +
                        "3. Raison de la consultation\n" +
                        "4. Préférence de date et heure\n" +
                        "5. Préférence de médecin (si applicable)\n\n" +
                        "# TON STYLE\n" +
                        "- Professionnel mais chaleureux\n" +
                        "- Concis et clair\n" +
                        "- Patient et à l'écoute\n" +
                        "- Rassurant en cas d'inquiétude\n\n" +
                        "# EXEMPLE DE CONVERSATION\n" +
                        "Patient: \"Bonjour, je voudrais un rendez-vous.\"\n" +
                        "Toi: \"Bonjour ! Je serais ravi de vous aider à prendre rendez-vous. Pouvez-vous me dire quelle est la raison de votre consultation ?\"\n\n"
                        +
                        "Utilise la base de connaissances ci-dessous pour répondre aux questions sur nos médecins, services et disponibilités.",
                doctors.size(),
                services.size(),
                avgPrice);
    }

    /**
     * Save knowledge document
     */
    private void saveKnowledge(String tenantId, KnowledgeType type, String ragDocument, Map<String, Object> metadata) {
        // Deactivate previous knowledge
        knowledgeRepository.findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, type)
                .ifPresent(old -> {
                    old.setIsActive(false);
                    knowledgeRepository.save(old);
                });

        // Get next version number
        long versionCount = knowledgeRepository.countByTenantIdAndKnowledgeType(tenantId, type);

        // Create new knowledge
        TenantAIKnowledge knowledge = TenantAIKnowledge.builder()
                .tenantId(tenantId)
                .knowledgeType(type)
                .ragDocument(ragDocument)
                .metadata(metadata)
                .version((int) versionCount + 1)
                .isActive(true)
                .build();

        knowledgeRepository.save(knowledge);
        log.info("✅ {} knowledge saved (version {})", type, knowledge.getVersion());
    }

    /**
     * Sync knowledge to AI providers (Vapi and Retell)
     */
    @Async
    public void syncToAIProviders(String tenantId) {
        log.info("🔄 Syncing knowledge to AI providers for tenant: {}", tenantId);

        try {
            // Get consolidated knowledge
            Optional<TenantAIKnowledge> knowledgeOpt = knowledgeRepository
                    .findByTenantIdAndKnowledgeTypeAndIsActiveTrue(tenantId, KnowledgeType.CONSOLIDATED);

            if (knowledgeOpt.isEmpty()) {
                log.warn("⚠️ No consolidated knowledge found for tenant: {}", tenantId);
                return;
            }

            TenantAIKnowledge knowledge = knowledgeOpt.get();

            // Get AI config
            Optional<TenantAIConfig> configOpt = aiConfigService.getConfigForTenant(tenantId);

            if (configOpt.isEmpty()) {
                log.warn("⚠️ No AI config found for tenant: {}", tenantId);
                return;
            }

            TenantAIConfig config = configOpt.get();

            // Sync to Vapi
            if (config.getVapiAssistantId() != null) {
                try {
                    vapiService.updateAssistantKnowledge(
                            tenantId,
                            config.getVapiAssistantId(),
                            knowledge.getSystemPrompt(),
                            knowledge.getRagDocument());
                    knowledge.setLastSyncedToVapi(LocalDateTime.now());
                    log.info("✅ Synced to Vapi assistant: {}", config.getVapiAssistantId());
                } catch (Exception e) {
                    log.error("❌ Failed to sync to Vapi", e);
                    knowledge.setSyncError("Vapi sync failed: " + e.getMessage());
                }
            }

            // Sync to Retell
            if (config.getRetellAgentId() != null) {
                try {
                    retellApiClient.updateAgentKnowledge(
                            config.getRetellAgentId(),
                            knowledge.getSystemPrompt(),
                            knowledge.getRagDocument());
                    knowledge.setLastSyncedToRetell(LocalDateTime.now());
                    log.info("✅ Synced to Retell agent: {}", config.getRetellAgentId());
                } catch (Exception e) {
                    log.error("❌ Failed to sync to Retell", e);
                    knowledge.setSyncError("Retell sync failed: " + e.getMessage());
                }
            }

            knowledgeRepository.save(knowledge);

        } catch (Exception e) {
            log.error("❌ Error syncing to AI providers", e);
        }
    }
}
