package com.saas.admin.service;

import com.saas.admin.entity.*;
import com.saas.admin.repository.*;
import com.saas.shared.core.TenantContext;
import com.saas.tenant.entity.*;
import com.saas.tenant.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Admin to Tenant Data Migration Service - PURE JPA
 * 
 * Migrates data from admin DB (saas_db) to tenant databases (tenant_X)
 * - Phase 1.4 of Admin/Tenant architecture refactoring
 * - Uses PURE JPA (NO SQL hardcoded) with TenantContext routing
 * - Migrates: CallCostRecord, AiApiCostRecord, VapiAssistant, VapiCall, VapiCallCostRecord
 * 
 * Migration Strategy:
 * - Step 1: Read from admin DB (old entities with tenantId)
 * - Step 2: Set TenantContext to target tenant schema
 * - Step 3: Write to tenant DB (new entities WITHOUT tenantId)
 * - Step 4: Verify counts
 * 
 * CRITICAL: One-time migration for existing data
 * - Run ONCE per tenant after Phase 1.3 deployment
 * - New data goes directly to tenant DBs via new services
 * - Old admin entities remain for backwards compatibility during transition
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AdminToTenantDataMigrationService {
    
    // ADMIN repositories (OLD entities with tenantId)
    private final com.saas.admin.repository.CallCostRecordRepository adminCallCostRepo;
    private final com.saas.admin.repository.AiApiCostRecordRepository adminAiCostRepo;
    private final com.saas.admin.repository.VapiAssistantRepository adminVapiAssistantRepo;
    private final com.saas.admin.repository.VapiCallRepository adminVapiCallRepo;
    private final com.saas.admin.repository.VapiCallCostRecordRepository adminVapiCostRepo;
    
    // TENANT repositories (NEW entities WITHOUT tenantId)
    private final com.saas.tenant.repository.CallCostRecordRepository tenantCallCostRepo;
    private final com.saas.tenant.repository.AiApiCostRecordRepository tenantAiCostRepo;
    private final com.saas.tenant.repository.VapiAssistantRepository tenantVapiAssistantRepo;
    private final com.saas.tenant.repository.VapiCallRepository tenantVapiCallRepo;
    private final com.saas.tenant.repository.VapiCallCostRecordRepository tenantVapiCostRepo;
    
    private final TenantRepository tenantRepository;
    
    /**
     * Migrate ALL data for a specific tenant (admin DB → tenant DB)
     * 
     * @param tenantId Tenant ID to migrate
     * @return Migration result summary
     */
    @Transactional
    public MigrationResult migrateAllDataForTenant(String tenantId) {
        log.info("🔄 Starting data migration for tenant: {}", tenantId);
        
        Tenant tenant = tenantRepository.findByTenantId(tenantId)
                .orElseThrow(() -> new RuntimeException("Tenant not found: " + tenantId));
        
        MigrationResult result = new MigrationResult(tenantId, tenant.getSchemaName());
        
        try {
            // Migrate each entity type
            result.callCostRecords = migrateCallCostRecords(tenant);
            result.aiApiCostRecords = migrateAiApiCostRecords(tenant);
            result.vapiAssistants = migrateVapiAssistants(tenant);
            result.vapiCalls = migrateVapiCalls(tenant);
            result.vapiCallCostRecords = migrateVapiCallCostRecords(tenant);
            
            result.success = true;
            log.info("✅ Migration completed successfully for tenant: {}", tenantId);
            
        } catch (Exception e) {
            result.success = false;
            result.errorMessage = e.getMessage();
            log.error("❌ Migration failed for tenant: {}", tenantId, e);
            throw new RuntimeException("Migration failed for tenant: " + tenantId, e);
        }
        
        return result;
    }
    
    /**
     * Migrate CallCostRecord (admin → tenant) using PURE JPA
     */
    private int migrateCallCostRecords(Tenant tenant) {
        log.info("📋 Migrating CallCostRecords for tenant: {}", tenant.getTenantId());
        
        // STEP 1: Read from admin DB (entities with tenantId field)
        List<com.saas.admin.entity.CallCostRecord> adminRecords = 
            adminCallCostRepo.findByTenantId(tenant.getTenantId());
        
        log.info("   Found {} records in admin DB", adminRecords.size());
        
        if (adminRecords.isEmpty()) {
            return 0;
        }
        
        // STEP 2: Set TenantContext to route to tenant DB
        TenantContext.setTenantId(tenant.getSchemaName());
        
        try {
            // STEP 3: Transform and save to tenant DB (entities WITHOUT tenantId)
            int count = 0;
            for (com.saas.admin.entity.CallCostRecord adminRecord : adminRecords) {
                com.saas.tenant.entity.CallCostRecord tenantRecord = 
                    com.saas.tenant.entity.CallCostRecord.builder()
                        .callSid(adminRecord.getCallSid())
                        .provider(adminRecord.getProvider())
                        .direction(adminRecord.getDirection())
                        .fromNumber(adminRecord.getFromNumber())
                        .toNumber(adminRecord.getToNumber())
                        .callStartTime(adminRecord.getCallStartTime())
                        .callEndTime(adminRecord.getCallEndTime())
                        .callDurationSeconds(adminRecord.getCallDurationSeconds())
                        .cost(adminRecord.getCost())
                        .currency(adminRecord.getCurrency())
                        .costDetails(adminRecord.getCostDetails())
                        .createdAt(adminRecord.getCreatedAt())
                        .updatedAt(adminRecord.getUpdatedAt())
                        .build();
                
                // JPA save to tenant DB (auto-routed via TenantContext)
                tenantCallCostRepo.save(tenantRecord);
                count++;
                
                if (count % 100 == 0) {
                    log.debug("   Migrated {} / {} records...", count, adminRecords.size());
                }
            }
            
            log.info("✅ Migrated {} CallCostRecords to tenant: {}", count, tenant.getSchemaName());
            return count;
            
        } finally {
            TenantContext.clear();
        }
    }
    
    /**
     * Migrate AiApiCostRecord (admin → tenant) using PURE JPA
     */
    private int migrateAiApiCostRecords(Tenant tenant) {
        log.info("📋 Migrating AiApiCostRecords for tenant: {}", tenant.getTenantId());
        
        List<com.saas.admin.entity.AiApiCostRecord> adminRecords = 
            adminAiCostRepo.findByTenantId(tenant.getTenantId());
        
        log.info("   Found {} records in admin DB", adminRecords.size());
        
        if (adminRecords.isEmpty()) {
            return 0;
        }
        
        TenantContext.setTenantId(tenant.getSchemaName());
        
        try {
            int count = 0;
            for (com.saas.admin.entity.AiApiCostRecord adminRecord : adminRecords) {
                com.saas.tenant.entity.AiApiCostRecord tenantRecord = 
                    com.saas.tenant.entity.AiApiCostRecord.builder()
                        .callSid(adminRecord.getCallSid())
                        .aiProvider(adminRecord.getAiProvider())
                        .aiModel(adminRecord.getAiModel())
                        .costType(adminRecord.getCostType())
                        .inputTokens(adminRecord.getInputTokens())
                        .outputTokens(adminRecord.getOutputTokens())
                        .totalTokens(adminRecord.getTotalTokens())
                        .durationSeconds(adminRecord.getDurationSeconds())
                        .cost(adminRecord.getCost())
                        .currency(adminRecord.getCurrency())
                        .callStartTime(adminRecord.getCallStartTime())
                        .costDetails(adminRecord.getCostDetails())
                        .createdAt(adminRecord.getCreatedAt())
                        .build();
                
                tenantAiCostRepo.save(tenantRecord);
                count++;
            }
            
            log.info("✅ Migrated {} AiApiCostRecords to tenant: {}", count, tenant.getSchemaName());
            return count;
            
        } finally {
            TenantContext.clear();
        }
    }
    
    /**
     * Migrate VapiAssistant (admin → tenant) using PURE JPA
     */
    private int migrateVapiAssistants(Tenant tenant) {
        log.info("📋 Migrating VapiAssistants for tenant: {}", tenant.getTenantId());
        
        List<com.saas.admin.entity.VapiAssistant> adminRecords = 
            adminVapiAssistantRepo.findByTenantId(tenant.getTenantId());
        
        log.info("   Found {} records in admin DB", adminRecords.size());
        
        if (adminRecords.isEmpty()) {
            return 0;
        }
        
        TenantContext.setTenantId(tenant.getSchemaName());
        
        try {
            int count = 0;
            for (com.saas.admin.entity.VapiAssistant adminRecord : adminRecords) {
                com.saas.tenant.entity.VapiAssistant tenantRecord = 
                    com.saas.tenant.entity.VapiAssistant.builder()
                        .vapiAssistantId(adminRecord.getVapiAssistantId())
                        .name(adminRecord.getName())
                        .model(adminRecord.getModel())
                        .voice(adminRecord.getVoice())
                        .prompt(adminRecord.getPrompt())
                        .firstMessage(adminRecord.getFirstMessage())
                        .serverUrl(adminRecord.getServerUrl())
                        .serverUrlSecret(adminRecord.getServerUrlSecret())
                        .config(adminRecord.getConfig())
                        .status(adminRecord.getStatus())
                        .createdAt(adminRecord.getCreatedAt())
                        .updatedAt(adminRecord.getUpdatedAt())
                        .build();
                
                tenantVapiAssistantRepo.save(tenantRecord);
                count++;
            }
            
            log.info("✅ Migrated {} VapiAssistants to tenant: {}", count, tenant.getSchemaName());
            return count;
            
        } finally {
            TenantContext.clear();
        }
    }
    
    /**
     * Migrate VapiCall (admin → tenant) using PURE JPA
     */
    private int migrateVapiCalls(Tenant tenant) {
        log.info("📋 Migrating VapiCalls for tenant: {}", tenant.getTenantId());
        
        List<com.saas.admin.entity.VapiCall> adminRecords = 
            adminVapiCallRepo.findByTenantId(tenant.getTenantId());
        
        log.info("   Found {} records in admin DB", adminRecords.size());
        
        if (adminRecords.isEmpty()) {
            return 0;
        }
        
        TenantContext.setTenantId(tenant.getSchemaName());
        
        try {
            int count = 0;
            for (com.saas.admin.entity.VapiCall adminRecord : adminRecords) {
                com.saas.tenant.entity.VapiCall tenantRecord = 
                    com.saas.tenant.entity.VapiCall.builder()
                        .vapiCallId(adminRecord.getVapiCallId())
                        .assistantId(adminRecord.getAssistantId())
                        .phoneNumber(adminRecord.getPhoneNumber())
                        .customerNumber(adminRecord.getCustomerNumber())
                        .type(adminRecord.getType())
                        .status(adminRecord.getStatus())
                        .startTime(adminRecord.getStartTime())
                        .endTime(adminRecord.getEndTime())
                        .duration(adminRecord.getDuration())
                        .cost(adminRecord.getCost())
                        .transcript(adminRecord.getTranscript())
                        .messages(adminRecord.getMessages())
                        .metadata(adminRecord.getMetadata())
                        .createdAt(adminRecord.getCreatedAt())
                        .build();
                
                tenantVapiCallRepo.save(tenantRecord);
                count++;
            }
            
            log.info("✅ Migrated {} VapiCalls to tenant: {}", count, tenant.getSchemaName());
            return count;
            
        } finally {
            TenantContext.clear();
        }
    }
    
    /**
     * Migrate VapiCallCostRecord (admin → tenant) using PURE JPA
     */
    private int migrateVapiCallCostRecords(Tenant tenant) {
        log.info("📋 Migrating VapiCallCostRecords for tenant: {}", tenant.getTenantId());
        
        List<com.saas.admin.entity.VapiCallCostRecord> adminRecords = 
            adminVapiCostRepo.findByTenantId(tenant.getTenantId());
        
        log.info("   Found {} records in admin DB", adminRecords.size());
        
        if (adminRecords.isEmpty()) {
            return 0;
        }
        
        TenantContext.setTenantId(tenant.getSchemaName());
        
        try {
            int count = 0;
            for (com.saas.admin.entity.VapiCallCostRecord adminRecord : adminRecords) {
                com.saas.tenant.entity.VapiCallCostRecord tenantRecord = 
                    com.saas.tenant.entity.VapiCallCostRecord.builder()
                        .vapiCallId(adminRecord.getVapiCallId())
                        .callStartTime(adminRecord.getCallStartTime())
                        .durationMinutes(adminRecord.getDurationMinutes())
                        .costPerMinute(adminRecord.getCostPerMinute())
                        .totalCost(adminRecord.getTotalCost())
                        .currency(adminRecord.getCurrency())
                        .costBreakdown(adminRecord.getCostBreakdown())
                        .createdAt(adminRecord.getCreatedAt())
                        .build();
                
                tenantVapiCostRepo.save(tenantRecord);
                count++;
            }
            
            log.info("✅ Migrated {} VapiCallCostRecords to tenant: {}", count, tenant.getSchemaName());
            return count;
            
        } finally {
            TenantContext.clear();
        }
    }
    
    /**
     * Migrate ALL tenants (orchestrator method)
     * Iterates through all active tenants and migrates their data
     */
    public MigrationSummary migrateAllTenants() {
        log.info("🚀 Starting data migration for ALL tenants...");
        
        List<Tenant> tenants = tenantRepository.findAll();
        MigrationSummary summary = new MigrationSummary();
        summary.totalTenants = tenants.size();
        
        for (Tenant tenant : tenants) {
            try {
                MigrationResult result = migrateAllDataForTenant(tenant.getTenantId());
                summary.results.add(result);
                
                if (result.success) {
                    summary.successCount++;
                } else {
                    summary.failedCount++;
                }
                
            } catch (Exception e) {
                summary.failedCount++;
                log.error("❌ Failed to migrate tenant: {}", tenant.getTenantId(), e);
            }
        }
        
        log.info("✅ Migration completed - Success: {}, Failed: {}", 
                summary.successCount, summary.failedCount);
        
        return summary;
    }
    
    /**
     * Migration Result for a single tenant
     */
    public static class MigrationResult {
        public String tenantId;
        public String schemaName;
        public int callCostRecords = 0;
        public int aiApiCostRecords = 0;
        public int vapiAssistants = 0;
        public int vapiCalls = 0;
        public int vapiCallCostRecords = 0;
        public boolean success = false;
        public String errorMessage;
        
        public MigrationResult(String tenantId, String schemaName) {
            this.tenantId = tenantId;
            this.schemaName = schemaName;
        }
        
        public int getTotalRecords() {
            return callCostRecords + aiApiCostRecords + vapiAssistants + 
                   vapiCalls + vapiCallCostRecords;
        }
        
        @Override
        public String toString() {
            return String.format("Tenant: %s (%s) - Total: %d records (CallCost: %d, AiCost: %d, VapiAsst: %d, VapiCall: %d, VapiCost: %d) - %s",
                tenantId, schemaName, getTotalRecords(), 
                callCostRecords, aiApiCostRecords, vapiAssistants, vapiCalls, vapiCallCostRecords,
                success ? "SUCCESS" : "FAILED: " + errorMessage);
        }
    }
    
    /**
     * Migration Summary for all tenants
     */
    public static class MigrationSummary {
        public int totalTenants = 0;
        public int successCount = 0;
        public int failedCount = 0;
        public List<MigrationResult> results = new java.util.ArrayList<>();
        
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(String.format("=== Migration Summary ===\n"));
            sb.append(String.format("Total Tenants: %d\n", totalTenants));
            sb.append(String.format("Success: %d\n", successCount));
            sb.append(String.format("Failed: %d\n", failedCount));
            sb.append("\nDetails:\n");
            for (MigrationResult result : results) {
                sb.append("  - ").append(result.toString()).append("\n");
            }
            return sb.toString();
        }
    }
}
