package com.saas.tenant.service;

import com.saas.tenant.entity.InboundCallData;
import com.saas.tenant.entity.InboundCallRequest;
import com.saas.tenant.repository.InboundCallDataRepository;
import com.saas.tenant.repository.InboundCallRequestRepository;
import jakarta.persistence.EntityManagerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j
public class InboundCallService {

    private final InboundCallDataRepository callDataRepository;
    private final InboundCallRequestRepository callRequestRepository;
    private final EntityManagerFactory entityManagerFactory;
    
    public InboundCallService(
            InboundCallDataRepository callDataRepository,
            InboundCallRequestRepository callRequestRepository,
            @Qualifier("tenantEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        this.callDataRepository = callDataRepository;
        this.callRequestRepository = callRequestRepository;
        this.entityManagerFactory = entityManagerFactory;
    }

    @Transactional(transactionManager = "tenantTransactionManager")
    public InboundCallData saveCallData(InboundCallData callData) {
        try {
            InboundCallData saved = callDataRepository.save(callData);
            log.info("Call data saved - CallSid: {}, From: {}, To: {}", 
                    saved.getCallSid(), saved.getFromNumber(), saved.getToNumber());
            return saved;
        } catch (Exception e) {
            log.error("Error saving call data for CallSid: {}", callData.getCallSid(), e);
            throw e;
        }
    }

    public InboundCallData saveInBothDatabases(InboundCallData callData, String tenantId, String tenantSchema) {
        return saveInTenantSchema(callData, tenantSchema);
    }

    public InboundCallData saveInTenantSchema(InboundCallData callData, String tenantSchema) {
        if (tenantSchema == null || tenantSchema.isBlank()) {
            throw new IllegalArgumentException("tenantSchema cannot be null or blank");
        }
        
        org.hibernate.Session session = entityManagerFactory.unwrap(org.hibernate.SessionFactory.class)
                .withOptions()
                .tenantIdentifier(tenantSchema)
                .openSession();
        
        try {
            session.beginTransaction();
            session.persist(callData);
            session.flush();
            session.getTransaction().commit();
            
            log.info("Call data saved to tenant schema '{}' - CallSid: {}", 
                    tenantSchema, callData.getCallSid());
            return callData;
        } catch (Exception e) {
            if (session.getTransaction() != null && session.getTransaction().isActive()) {
                session.getTransaction().rollback();
            }
            log.error("Error saving call data to tenant schema '{}' for CallSid: {}", 
                    tenantSchema, callData.getCallSid(), e);
            throw new RuntimeException("Failed to save call data to tenant schema: " + tenantSchema, e);
        } finally {
            session.close();
        }
    }

    @Transactional(transactionManager = "tenantTransactionManager")
    public InboundCallRequest savePatientRequest(InboundCallRequest request) {
        try {
            InboundCallRequest saved = callRequestRepository.save(request);
            log.info("Patient request saved - CallSid: {}, Nom: {}, Telephone: {}", 
                    saved.getCallSid(), saved.getNom(), saved.getTelephone());
            return saved;
        } catch (Exception e) {
            log.error("Error saving patient request for CallSid: {}", request.getCallSid(), e);
            throw e;
        }
    }

    @Transactional(transactionManager = "tenantTransactionManager")
    public void updateCallStatus(String callSid, String status, LocalDateTime endTime, Integer duration) {
        try {
            Optional<InboundCallData> callDataOpt = callDataRepository.findByCallSid(callSid);
            if (callDataOpt.isPresent()) {
                InboundCallData callData = callDataOpt.get();
                callData.setCallStatus(status);
                if (endTime != null) callData.setEndTime(endTime);
                if (duration != null) callData.setDuration(duration);
                callDataRepository.save(callData);
                log.info("Call status updated - CallSid: {}, Status: {}", callSid, status);
            }
        } catch (Exception e) {
            log.error("Error updating call status for CallSid: {}", callSid, e);
        }
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public Optional<InboundCallData> getCallByCallSid(String callSid) {
        return callDataRepository.findByCallSid(callSid);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public Optional<InboundCallRequest> getPatientRequestByCallSid(String callSid) {
        return callRequestRepository.findByCallSid(callSid);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public boolean callExists(String callSid) {
        return callDataRepository.existsByCallSid(callSid);
    }

    @Transactional(transactionManager = "tenantTransactionManager")
    public boolean updateSmsStatus(String smsSid, String smsStatus) {
        try {
            Optional<InboundCallRequest> requestOpt = callRequestRepository.findBySmsSid(smsSid);
            if (requestOpt.isPresent()) {
                InboundCallRequest request = requestOpt.get();
                request.setSmsStatus(smsStatus);
                callRequestRepository.save(request);
                log.info("SMS status updated - SID: {}, Status: {}", smsSid, smsStatus);
                return true;
            } else {
                log.warn("No InboundCallRequest found with SMS SID: {}", smsSid);
                return false;
            }
        } catch (Exception e) {
            log.error("Error updating SMS status for SID: {}", smsSid, e);
            return false;
        }
    }

    @Transactional(transactionManager = "tenantTransactionManager")
    public boolean updateTranscript(String callSessionId, List<java.util.Map<String, Object>> transcript) {
        try {
            Optional<InboundCallRequest> requestOpt = callRequestRepository.findByCallSid(callSessionId);
            if (requestOpt.isPresent()) {
                InboundCallRequest request = requestOpt.get();
                com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
                String transcriptJson = mapper.writeValueAsString(transcript);
                request.setConversationTranscript(transcriptJson);
                callRequestRepository.save(request);
                log.info("Transcript updated for CallSid: {}", callSessionId);
                return true;
            } else {
                log.warn("No InboundCallRequest found with CallSid: {}", callSessionId);
                return false;
            }
        } catch (Exception e) {
            log.error("Error updating transcript for CallSid: {}", callSessionId, e);
            return false;
        }
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public org.springframework.data.domain.Page<InboundCallData> getAllInboundCallsPaginated(org.springframework.data.domain.Pageable pageable) {
        return callDataRepository.findAll(pageable);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public Optional<InboundCallData> getInboundCallByCallSid(String callSid) {
        return callDataRepository.findByCallSid(callSid);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public List<InboundCallRequest> getCallRecordsByProvider(String provider) {
        return callRequestRepository.findAll().stream()
                .filter(req -> provider.equalsIgnoreCase(req.getProvider()))
                .collect(java.util.stream.Collectors.toList());
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public List<InboundCallRequest> getCallRecordsByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
        return callRequestRepository.findAll().stream()
                .filter(req -> req.getCreatedAt() != null)
                .filter(req -> !req.getCreatedAt().isBefore(startDate) && !req.getCreatedAt().isAfter(endDate))
                .collect(java.util.stream.Collectors.toList());
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public List<InboundCallRequest> getAllCallRecords() {
        return callRequestRepository.findAll();
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public Optional<InboundCallRequest> getCallRecordByCallSid(String callSid) {
        return callRequestRepository.findByCallSid(callSid);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public List<InboundCallRequest> getCallRecordsByPatientPhone(String phoneNumber) {
        return callRequestRepository.findByTelephone(phoneNumber);
    }

    @Transactional(transactionManager = "tenantTransactionManager", readOnly = true)
    public List<InboundCallRequest> getRecentCallRecords(int limit) {
        return callRequestRepository.findAll().stream()
                .sorted((r1, r2) -> r2.getCreatedAt().compareTo(r1.getCreatedAt()))
                .limit(limit)
                .collect(java.util.stream.Collectors.toList());
    }
}
