Subversion Repositories SmartDukaan

Rev

Rev 36017 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.spice.profitmandi.web.service;

import com.spice.profitmandi.dao.entity.cs.RbmBreakLog;
import com.spice.profitmandi.dao.repository.cs.RbmBreakLogRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * DEPRECATED: This service is no longer actively used.
 * <p>
 * Live agent status is now retrieved from Redis via KnowlarityCallMonitorService in profitmandi-dao.
 * Break logs are now persisted by KnowlarityCallMonitorService via WebSocket in profitmandi-cron.
 * <p>
 * Use the following for live status:
 * - REST API: /api/agent-live-status endpoints in profitmandi-web (AgentLiveStatusController)
 * - DAO Service: KnowlarityCallMonitorService.getAgentStatusesFromRedis()
 * <p>
 * This service is kept for backward compatibility but should not be used for new features.
 */
@Deprecated
@Service
public class AgentLiveStatusService {

    private static final Logger LOGGER = LogManager.getLogger(AgentLiveStatusService.class);

    // In-memory map to store agent status by authId
    private final Map<Integer, AgentStatus> agentStatusMap = new ConcurrentHashMap<>();
    // Caller ID (Knowlarity SR Number) to Auth ID mapping (loaded from sip_master)
    private final Map<String, Integer> callerIdToAuthIdMap = new ConcurrentHashMap<>();
    // SR Number to Auth ID mapping (loaded from sip_master)
    private final Map<String, Integer> srNumberToAuthIdMap = new ConcurrentHashMap<>();
    // Agent name to Auth ID mapping (fallback)
    private final Map<String, Integer> agentNameToAuthIdMap = new ConcurrentHashMap<>();

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private RbmBreakLogRepository rbmBreakLogRepository;

    @Autowired
    private PlatformTransactionManager transactionManager;

    private TransactionTemplate transactionTemplate;

    private String lastError = null;
    private int lastQueryCount = 0;

    /**
     * Load mapping from sip_master table on startup
     * caller_id in sip_master = SR Number on Knowlarity dashboard
     * sip_master has auth_id directly
     */
    @PostConstruct
    public void loadMappingFromDatabase() {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        try {
            Session session = sessionFactory.openSession();

            // Query to get caller_id, auth_user.id, first_name from sip_master
            // Join with auth_user to get correct auth_id used by rbm_call_target page
            String sql = "SELECT sm.caller_id, au.id, sm.first_name " +
                    "FROM auth.sip_master sm " +
                    "JOIN auth.auth_user au ON sm.email_id = au.email_id " +
                    "WHERE au.id IS NOT NULL";

            List<Object[]> results = session.createNativeQuery(sql).getResultList();
            lastQueryCount = results.size();

            for (Object[] row : results) {
                String callerId = row[0] != null ? row[0].toString().trim() : "";
                int authId = row[1] != null ? ((Number) row[1]).intValue() : 0;
                String firstName = row[2] != null ? row[2].toString().trim() : "";

                if (authId > 0) {
                    // Map by caller_id if available
                    if (!callerId.isEmpty()) {
                        callerIdToAuthIdMap.put(callerId, authId);
                    }
                    // Map by first_name for name-based matching
                    if (!firstName.isEmpty()) {
                        agentNameToAuthIdMap.put(firstName.toLowerCase(), authId);
                        System.out.println("Mapping: '" + firstName + "' -> authId=" + authId + " (caller_id=" + callerId + ")");
                    }
                }
            }

            session.close();
            System.out.println("Loaded " + callerIdToAuthIdMap.size() + " caller_id -> auth_id mappings from sip_master");
            System.out.println("Loaded " + agentNameToAuthIdMap.size() + " name -> auth_id mappings");

        } catch (Exception e) {
            lastError = e.getClass().getSimpleName() + ": " + e.getMessage();
            System.err.println("Error loading sip_master mapping: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public String getLastError() {
        return lastError;
    }

    public int getLastQueryCount() {
        return lastQueryCount;
    }

    /**
     * Reload mapping from database (can be called via API)
     */
    public void reloadMapping() {
        callerIdToAuthIdMap.clear();
        srNumberToAuthIdMap.clear();
        agentNameToAuthIdMap.clear();
        lastError = null;
        lastQueryCount = 0;
        loadMappingFromDatabase();
    }

    public Integer getAuthIdByCallerId(String callerId) {
        return callerIdToAuthIdMap.get(callerId);
    }

    public void setSrNumberMapping(String srNumber, int authId) {
        srNumberToAuthIdMap.put(srNumber, authId);
    }

    public void setAgentNameMapping(String agentName, int authId) {
        agentNameToAuthIdMap.put(agentName.toLowerCase(), authId);
    }

    public Integer getAuthIdBySrNumber(String srNumber) {
        return srNumberToAuthIdMap.get(srNumber);
    }

    public Integer getAuthIdByAgentName(String agentName) {
        // Try exact match
        Integer authId = agentNameToAuthIdMap.get(agentName.toLowerCase());
        if (authId != null) return authId;

        // Try partial match
        for (Map.Entry<String, Integer> entry : agentNameToAuthIdMap.entrySet()) {
            if (agentName.toLowerCase().contains(entry.getKey()) || entry.getKey().contains(agentName.toLowerCase())) {
                return entry.getValue();
            }
        }
        return null;
    }

    public Map<String, Integer> getSrNumberMapping() {
        return srNumberToAuthIdMap;
    }

    public Map<String, Integer> getAgentNameMapping() {
        return agentNameToAuthIdMap;
    }

    public Map<String, Integer> getCallerIdMapping() {
        return callerIdToAuthIdMap;
    }

    public void clearMappings() {
        srNumberToAuthIdMap.clear();
        agentNameToAuthIdMap.clear();
    }

    public void updateStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {
        // DISABLED: Break log persistence is now handled by profitmandi-cron via WebSocket
        // This method only updates in-memory status map (also deprecated - use Redis via KnowlarityCallMonitorService)

        // Update current status (in-memory only, no persistence)
        AgentStatus status = new AgentStatus(authId, srNumber, agentName, agentStatus, timeInStatus, customerNumber);
        agentStatusMap.put(authId, status);
    }

    /**
     * Check if status should be tracked (Only Available and Break)
     */
    private boolean isTrackableStatus(String status) {
        if (status == null || status.isEmpty()) return false;
        String lower = status.toLowerCase();
        return lower.contains("available") || lower.contains("break");
    }

    /**
     * Parse timeInStatus string (e.g., "00:05:30" or "5m 30s") to seconds
     */
    private int parseTimeInStatusToSeconds(String timeInStatus) {
        if (timeInStatus == null || timeInStatus.isEmpty()) return 0;

        try {
            // Format: HH:mm:ss or H:mm:ss
            if (timeInStatus.contains(":")) {
                String[] parts = timeInStatus.split(":");
                if (parts.length == 3) {
                    int hours = Integer.parseInt(parts[0].trim());
                    int minutes = Integer.parseInt(parts[1].trim());
                    int seconds = Integer.parseInt(parts[2].trim());
                    return hours * 3600 + minutes * 60 + seconds;
                } else if (parts.length == 2) {
                    int minutes = Integer.parseInt(parts[0].trim());
                    int seconds = Integer.parseInt(parts[1].trim());
                    return minutes * 60 + seconds;
                }
            }

            // Format: "5m 30s" or "1h 5m 30s"
            int totalSeconds = 0;
            String lower = timeInStatus.toLowerCase();
            if (lower.contains("h")) {
                String hourPart = lower.split("h")[0].trim();
                totalSeconds += Integer.parseInt(hourPart.replaceAll("[^0-9]", "")) * 3600;
            }
            if (lower.contains("m")) {
                String minPart = lower.contains("h") ? lower.split("h")[1].split("m")[0] : lower.split("m")[0];
                totalSeconds += Integer.parseInt(minPart.trim().replaceAll("[^0-9]", "")) * 60;
            }
            if (lower.contains("s") && !lower.contains("h") && !lower.contains("m")) {
                totalSeconds += Integer.parseInt(lower.replaceAll("[^0-9]", ""));
            } else if (lower.contains("s")) {
                String secPart = lower.split("m")[1].split("s")[0];
                totalSeconds += Integer.parseInt(secPart.trim().replaceAll("[^0-9]", ""));
            }
            return totalSeconds;

        } catch (Exception e) {
            LOGGER.warn("Could not parse timeInStatus: {}", timeInStatus);
            return 0;
        }
    }

    /**
     * Save status duration to RbmBreakLog
     * Uses UPSERT logic: If record exists within time window, update duration; otherwise create new
     *
     * @param statusStartTime - the time when agent ENTERED this status (start time)
     */
    private void saveStatusDuration(int authId, String agentName, String srNumber, String status,
                                    String durationDisplay, int durationSeconds, LocalDateTime statusStartTime) {
        try {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus txStatus) {
                    LocalDate logDate = statusStartTime.toLocalDate();

                    // Check if existing record within 5 minute window
                    RbmBreakLog existing = rbmBreakLogRepository.findByAuthIdStatusAndLogTimeWindow(
                            authId, status, statusStartTime, 5);

                    if (existing != null) {
                        // Update existing record with latest duration
                        LOGGER.info("Updating existing break log id={}: agent={}, status={}, duration {}s -> {}s",
                                existing.getId(), agentName, status, existing.getDurationSeconds(), durationSeconds);
                        rbmBreakLogRepository.updateDuration(existing.getId(), durationSeconds, durationDisplay);
                    } else {
                        // Create new record
                        LOGGER.info("Creating new break log: agent={}, status={}, startTime={}, duration={}s ({})",
                                agentName, status, statusStartTime, durationSeconds, durationDisplay);

                        RbmBreakLog breakLog = new RbmBreakLog(
                                authId,
                                agentName,
                                srNumber,
                                status,
                                statusStartTime,
                                durationSeconds,
                                durationDisplay,
                                logDate
                        );
                        rbmBreakLogRepository.persist(breakLog);
                    }
                }
            });
        } catch (Exception e) {
            LOGGER.error("Error saving status duration for {}: {}", agentName, e.getMessage());
        }
    }

    public AgentStatus getStatusByAuthId(int authId) {
        return agentStatusMap.get(authId);
    }

    public Map<Integer, AgentStatus> getAllStatusMap() {
        return agentStatusMap;
    }

    public List<AgentStatus> getAllStatuses() {
        return new ArrayList<>(agentStatusMap.values());
    }

    public void clearAll() {
        agentStatusMap.clear();
    }

    // Inner class to hold agent status
    public static class AgentStatus {
        private int authId;
        private String srNumber;
        private String agentName;
        private String agentStatus;
        private String timeInStatus;
        private String customerNumber;
        private LocalDateTime lastUpdated;

        public AgentStatus() {
        }

        public AgentStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {
            this.authId = authId;
            this.srNumber = srNumber;
            this.agentName = agentName;
            this.agentStatus = agentStatus;
            this.timeInStatus = timeInStatus;
            this.customerNumber = customerNumber;
            this.lastUpdated = LocalDateTime.now();
        }

        public int getAuthId() {
            return authId;
        }

        public void setAuthId(int authId) {
            this.authId = authId;
        }

        public String getSrNumber() {
            return srNumber;
        }

        public void setSrNumber(String srNumber) {
            this.srNumber = srNumber;
        }

        public String getAgentName() {
            return agentName;
        }

        public void setAgentName(String agentName) {
            this.agentName = agentName;
        }

        public String getAgentStatus() {
            return agentStatus;
        }

        public void setAgentStatus(String agentStatus) {
            this.agentStatus = agentStatus;
        }

        public String getTimeInStatus() {
            return timeInStatus;
        }

        public void setTimeInStatus(String timeInStatus) {
            this.timeInStatus = timeInStatus;
        }

        public String getCustomerNumber() {
            return customerNumber;
        }

        public void setCustomerNumber(String customerNumber) {
            this.customerNumber = customerNumber;
        }

        public LocalDateTime getLastUpdated() {
            return lastUpdated;
        }

        public void setLastUpdated(LocalDateTime lastUpdated) {
            this.lastUpdated = lastUpdated;
        }
    }
}