package com.saas.shared.service;

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

/**
 * Service for saving call data to BOTH admin database and tenant database
 * This enables:
 * 1. Tenant isolation (tenant database)
 * 2. Cross-tenant reporting (admin database)
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class DualSaveCallDataService {

    private final InboundCallDataRepository callDataRepository;

    /**
     * Save call data to BOTH databases:
     * 1. Admin database (saas_db) for cross-tenant reporting
     * 2. Tenant database for tenant isolation
     */
    public void saveToBothDatabases(InboundCallData callData, String tenantSchema, String tenantId) {
        log.info("🚀 [DualSaveCallDataService] saveToBothDatabases() CALLED");
        log.info("🚀 [DualSaveCallDataService] CallSid: {}, Provider: {}, TenantSchema: {}, TenantId: {}", 
            callData.getCallSid(), callData.getProvider(), tenantSchema, tenantId);
        
        // Save to admin database (saas_db) for reporting
        saveToAdminDatabase(callData, tenantId);
        
        // Save to tenant database for isolation
        saveToTenantDatabase(callData, tenantSchema);
        
        log.info("✅ [DualSaveCallDataService] Dual save completed for CallSid: {}", callData.getCallSid());
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    protected void saveToAdminDatabase(InboundCallData callData, String tenantId) {
        log.info("💾 [DualSaveCallDataService] saveToAdminDatabase() START - CallSid: {}, TenantId: {}", 
            callData.getCallSid(), tenantId);
        
        try {
            log.info("🎯 [DualSaveCallDataService] Setting TenantContext to 'saas_db'");
            TenantContext.setTenantId("saas_db");
            
            // CHECK if already exists to prevent duplicate errors
            if (callDataRepository.existsByCallSid(callData.getCallSid())) {
                log.warn("⚠️ [DualSaveCallDataService] CallSid {} already exists in ADMIN database, skipping save", 
                    callData.getCallSid());
                return;
            }
            
            // Clone to avoid ID conflicts and SET tenant_id for admin DB
            InboundCallData adminCopy = cloneCallData(callData);
            adminCopy.setTenantId(tenantId); // CRITICAL: Set tenantId for admin DB
            log.info("📋 [DualSaveCallDataService] Cloned call data - CallSid: {}, Provider: {}, TenantId: {}", 
                adminCopy.getCallSid(), adminCopy.getProvider(), adminCopy.getTenantId());
            
            log.info("💾 [DualSaveCallDataService] Calling repository.save() for ADMIN database...");
            InboundCallData saved = callDataRepository.save(adminCopy);
            
            log.info("✅ [DualSaveCallDataService] Call data saved to ADMIN database (saas_db)!");
            log.info("✅ [DualSaveCallDataService] Saved ID: {}, CallSid: {}, Provider: {}, TenantId: {}", 
                saved.getId(), saved.getCallSid(), saved.getProvider(), saved.getTenantId());
        } catch (Exception e) {
            log.error("❌ [DualSaveCallDataService] EXCEPTION saving to ADMIN database!", e);
            log.error("❌ [DualSaveCallDataService] Exception type: {}, Message: {}", 
                e.getClass().getName(), e.getMessage());
            log.error("❌ [DualSaveCallDataService] CallSid: {}", callData.getCallSid());
        } finally {
            TenantContext.clear();
            log.info("🧹 [DualSaveCallDataService] TenantContext cleared after ADMIN save");
        }
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    protected void saveToTenantDatabase(InboundCallData callData, String tenantSchema) {
        log.info("💾 [DualSaveCallDataService] saveToTenantDatabase() START - CallSid: {}, Schema: {}", 
            callData.getCallSid(), tenantSchema);
        
        try {
            log.info("🎯 [DualSaveCallDataService] Setting TenantContext to '{}'", tenantSchema);
            TenantContext.setTenantId(tenantSchema);
            
            // CHECK if already exists to prevent duplicate errors
            if (callDataRepository.existsByCallSid(callData.getCallSid())) {
                log.warn("⚠️ [DualSaveCallDataService] CallSid {} already exists in TENANT database, skipping save", 
                    callData.getCallSid());
                return;
            }
            
            log.info("📝 [DualSaveCallDataService] Call data details - Provider: {}, From: {}, To: {}", 
                callData.getProvider(), callData.getFromNumber(), callData.getToNumber());
            
            log.info("💾 [DualSaveCallDataService] Calling repository.save() for TENANT database...");
            InboundCallData saved = callDataRepository.save(callData);
            
            log.info("✅ [DualSaveCallDataService] Call data saved to TENANT database ({})!", tenantSchema);
            log.info("✅ [DualSaveCallDataService] Saved ID: {}, CallSid: {}, Provider: {}", 
                saved.getId(), saved.getCallSid(), saved.getProvider());
        } catch (Exception e) {
            log.error("❌ [DualSaveCallDataService] EXCEPTION saving to TENANT database '{}'!", tenantSchema, e);
            log.error("❌ [DualSaveCallDataService] Exception type: {}, Message: {}", 
                e.getClass().getName(), e.getMessage());
            log.error("❌ [DualSaveCallDataService] CallSid: {}", callData.getCallSid());
            log.error("❌ [DualSaveCallDataService] FULL STACK TRACE:", e);
        } finally {
            TenantContext.clear();
            log.info("🧹 [DualSaveCallDataService] TenantContext cleared after TENANT save");
        }
    }
    
    private InboundCallData cloneCallData(InboundCallData source) {
        return InboundCallData.builder()
                .callSid(source.getCallSid())
                .provider(source.getProvider())
                .fromNumber(source.getFromNumber())
                .toNumber(source.getToNumber())
                .direction(source.getDirection())
                .callStatus(source.getCallStatus())
                .startTime(source.getStartTime())
                .endTime(source.getEndTime())
                .duration(source.getDuration())
                .fromCity(source.getFromCity())
                .fromState(source.getFromState())
                .fromZip(source.getFromZip())
                .fromCountry(source.getFromCountry())
                .toCity(source.getToCity())
                .toState(source.getToState())
                .toZip(source.getToZip())
                .toCountry(source.getToCountry())
                .accountSid(source.getAccountSid())
                .apiVersion(source.getApiVersion())
                .called(source.getCalled())
                .caller(source.getCaller())
                .calledCity(source.getCalledCity())
                .calledCountry(source.getCalledCountry())
                .calledState(source.getCalledState())
                .calledZip(source.getCalledZip())
                .callerCity(source.getCallerCity())
                .callerCountry(source.getCallerCountry())
                .callerState(source.getCallerState())
                .callerZip(source.getCallerZip())
                .callToken(source.getCallToken())
                .forwardedFrom(source.getForwardedFrom())
                .parentCallSid(source.getParentCallSid())
                .recordingSid(source.getRecordingSid())
                .recordingUrl(source.getRecordingUrl())
                .stirVerstat(source.getStirVerstat())
                .build();
    }
}
