package com.saas.admin.service;

import com.saas.admin.entity.AdminVapiAssistantSummary;
import com.saas.admin.entity.Tenant;
import com.saas.admin.repository.AdminVapiAssistantSummaryRepository;
import com.saas.admin.repository.TenantRepository;
import com.saas.shared.core.TenantContext;
import com.saas.tenant.entity.VapiAssistant;
import com.saas.tenant.repository.VapiAssistantRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Admin Vapi Sync Service - PURE JPA IMPLEMENTATION
 * 
 * Syncs Vapi assistants from all tenant databases to admin cache (AdminVapiAssistantSummary)
 * - Runs every 5 minutes via scheduler
 * - Uses PURE JPA (NO SQL hardcoded) with find() + save() pattern
 * - UPSERT logic via JPA merge semantics
 * - Enables admin provisioning and cross-tenant monitoring
 * 
 * Architecture:
 * - Source: tenant databases (tenant_X.vapi_assistants) via VapiAssistantRepository
 * - Destination: admin database (saas_db.admin_vapi_assistant_summary) via AdminVapiAssistantSummaryRepository
 * - PK: vapi_assistant_id (String UUID) - no ID collisions
 * - Sync frequency: Every 5 minutes (300,000 ms)
 * 
 * Design Pattern: find() + save()
 * - For each assistant: findById() in admin cache
 * - If exists: update fields, save() (Hibernate UPDATE)
 * - If not exists: create new, save() (Hibernate INSERT)
 * - Result: UPSERT semantics without raw SQL
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AdminVapiSyncService {
    
    private final TenantRepository tenantRepository;
    private final AdminVapiAssistantSummaryRepository adminVapiSummaryRepository;
    private final VapiAssistantRepository vapiAssistantRepository;
    
    /**
     * Scheduled sync every 5 minutes
     * Syncs all Vapi assistants from tenant databases to admin cache
     */
    @Scheduled(fixedRate = 300000) // 5 minutes
    public void syncVapiAssistantsSummary() {
        log.info("🔄 Starting Vapi assistants sync across all tenants...");
        
        List<Tenant> tenants = tenantRepository.findAll();
        int totalSynced = 0;
        int totalTenants = tenants.size();
        int failedTenants = 0;
        
        for (Tenant tenant : tenants) {
            try {
                int synced = syncTenantVapiAssistants(tenant);
                totalSynced += synced;
                log.debug("✅ Synced {} assistants for tenant: {}", synced, tenant.getTenantName());
            } catch (Exception e) {
                failedTenants++;
                log.error("❌ Failed to sync tenant: {} ({})", tenant.getTenantId(), tenant.getTenantName(), e);
            }
        }
        
        log.info("✅ Sync completed - Tenants: {}, Assistants synced: {}, Failed tenants: {}", 
                totalTenants, totalSynced, failedTenants);
    }
    
    /**
     * Sync assistants for a single tenant using PURE JPA (find + save pattern)
     * 
     * CRITICAL: TenantContext routing fixed
     * - Step 1: Set TenantContext → Read from tenant DB → CLEAR context
     * - Step 2: Write to admin DB (NO TenantContext, routes to saas_db)
     * 
     * @param tenant Tenant to sync
     * @return Number of assistants synced
     */
    @Transactional
    public int syncTenantVapiAssistants(Tenant tenant) {
        // STEP 1: Read from tenant DB with TenantContext
        List<VapiAssistant> activeAssistants;
        
        TenantContext.setTenantId(tenant.getSchemaName());
        try {
            // Fetch all ACTIVE assistants from tenant DB (JPA query)
            activeAssistants = vapiAssistantRepository.findByStatus("ACTIVE");
            log.debug("📖 Read {} active assistants from tenant DB: {}", 
                    activeAssistants.size(), tenant.getSchemaName());
        } finally {
            // CRITICAL: Clear context BEFORE admin writes
            TenantContext.clear();
        }
        
        // STEP 2: Write to admin DB (NO TenantContext = routes to saas_db)
        LocalDateTime now = LocalDateTime.now();
        Set<String> syncedIds = activeAssistants.stream()
                .map(VapiAssistant::getVapiAssistantId)
                .collect(Collectors.toSet());
        
        int count = 0;
        
        // UPSERT each assistant using find() + save() pattern
        for (VapiAssistant assistant : activeAssistants) {
            String vapiAssistantId = assistant.getVapiAssistantId();
            
            if (vapiAssistantId == null || vapiAssistantId.isEmpty()) {
                log.warn("⚠️ Skipping assistant with null vapi_assistant_id in tenant: {}", tenant.getTenantName());
                continue;
            }
            
            // Find existing summary in admin cache (JPA findById - admin DB)
            AdminVapiAssistantSummary summary = adminVapiSummaryRepository
                    .findById(vapiAssistantId)
                    .orElse(null);
            
            if (summary == null) {
                // CREATE: New assistant, insert to admin cache
                summary = AdminVapiAssistantSummary.builder()
                        .vapiAssistantId(vapiAssistantId)
                        .tenantId(tenant.getTenantId())
                        .tenantLocalId(assistant.getId())
                        .name(assistant.getName())
                        .voiceProvider(assistant.getVoice())
                        .isActive(true)
                        .lastSyncedAt(now)
                        .createdAt(assistant.getCreatedAt())
                        .updatedAt(assistant.getUpdatedAt())
                        .build();
                
                log.debug("📝 Creating new summary for assistant: {}", assistant.getName());
            } else {
                // UPDATE: Existing assistant, update fields
                summary.setTenantId(tenant.getTenantId());
                summary.setTenantLocalId(assistant.getId());
                summary.setName(assistant.getName());
                summary.setVoiceProvider(assistant.getVoice());
                summary.setIsActive(true);
                summary.setLastSyncedAt(now);
                summary.setUpdatedAt(assistant.getUpdatedAt());
                
                log.debug("✏️ Updating summary for assistant: {}", assistant.getName());
            }
            
            // SAVE: Hibernate decides INSERT vs UPDATE (admin DB, NO TenantContext)
            adminVapiSummaryRepository.save(summary);
            count++;
        }
        
        // Mark inactive assistants for this tenant (soft delete, admin DB)
        markInactiveAssistants(tenant.getTenantId(), syncedIds);
        
        return count;
    }
    
    /**
     * Mark assistants as inactive if they no longer exist in tenant DB (PURE JPA)
     * 
     * Called from syncTenantVapiAssistants (already @Transactional)
     * No need for separate @Transactional annotation (same class, same transaction)
     * 
     * @param tenantId Tenant ID
     * @param syncedIds List of assistant IDs that were synced
     */
    private void markInactiveAssistants(String tenantId, Set<String> syncedIds) {
        try {
            // Fetch existing summaries for this tenant (JPA query)
            List<AdminVapiAssistantSummary> existingAssistants = 
                adminVapiSummaryRepository.findByTenantId(tenantId);
            
            LocalDateTime now = LocalDateTime.now();
            
            for (AdminVapiAssistantSummary assistant : existingAssistants) {
                // If assistant not in synced list AND currently active, mark inactive
                if (!syncedIds.contains(assistant.getVapiAssistantId()) && assistant.getIsActive()) {
                    assistant.setIsActive(false);
                    assistant.setUpdatedAt(now);
                    adminVapiSummaryRepository.save(assistant); // JPA save
                    
                    log.info("⛔ Marked assistant as inactive: {} (Tenant: {})", 
                            assistant.getName(), tenantId);
                }
            }
        } catch (Exception e) {
            log.error("❌ Failed to mark inactive assistants for tenant: {}", tenantId, e);
        }
    }
    
    /**
     * Manually trigger sync for all tenants (admin endpoint can call this)
     */
    public void forceSyncAllTenants() {
        log.info("🔄 Manual sync triggered by admin");
        syncVapiAssistantsSummary();
    }
    
    /**
     * Manually trigger sync for a specific tenant
     * 
     * @param tenantId Tenant ID to sync
     * @return Number of assistants synced
     */
    public int forceSyncTenant(String tenantId) {
        log.info("🔄 Manual sync triggered for tenant: {}", tenantId);
        
        Tenant tenant = tenantRepository.findByTenantId(tenantId)
                .orElseThrow(() -> new RuntimeException("Tenant not found: " + tenantId));
        
        int synced = syncTenantVapiAssistants(tenant);
        log.info("✅ Manual sync completed - Synced {} assistants", synced);
        return synced;
    }
    
    /**
     * Invalidate (delete) cache for a specific assistant (PURE JPA)
     * Called when an assistant is deleted from tenant DB
     * 
     * @param vapiAssistantId Vapi assistant ID to invalidate
     */
    @Transactional
    public void invalidateAssistantCache(String vapiAssistantId) {
        adminVapiSummaryRepository.deleteById(vapiAssistantId); // JPA deleteById
        log.info("🗑️ Invalidated cache for assistant: {}", vapiAssistantId);
    }
}
