package com.eactive.eai.rms.onl.dashboard.modern.service;

import com.eactive.eai.data.entity.onl.adapter.AdapterGroup;
import com.eactive.eai.data.entity.onl.server.EAIServer;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterGroupService;
import com.eactive.eai.rms.data.entity.onl.server.EAIServerService;
import com.eactive.eai.rms.onl.common.util.DataSetUtil;
import com.eactive.eai.rms.onl.common.vo.HostInfomationForm;
import com.eactive.eai.rms.onl.dashboard.modern.dto.InstitutionStatusDTO;
import com.eactive.eai.rms.onl.dashboard.modern.dto.SocketProcessDTO;
import com.eactive.eai.rms.onl.dashboard.modern.dto.SocketStatusDTO;
import com.eactive.eai.rms.onl.manage.adapter.adapter.mapper.AdapterGroupUIMapper;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUI;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Service for socket monitoring in modern dashboard
 */
@Service
public class SocketMonitorService {
    private static final String AGENT_PAGE_STATUS = "adapter_socket2_agent1.jsp";
    private static final String AGENT_PAGE_PROCESS = "adapter_socket2_agent2.jsp";

    private final EAIServerService eaiServerService;
    private final AdapterGroupService adapterGroupService;
    private final AdapterGroupUIMapper adapterGroupUIMapper;

    @Autowired
    public SocketMonitorService(EAIServerService eaiServerService, AdapterGroupService adapterGroupService, AdapterGroupUIMapper adapterGroupUIMapper) {
        this.eaiServerService = eaiServerService;
        this.adapterGroupService = adapterGroupService;
        this.adapterGroupUIMapper = adapterGroupUIMapper;
    }

    /**
     * Get socket status data
     * @param serverName Optional server name filter
     * @param adapterGroup Optional adapter group filter
     * @return SocketStatusDTO with all socket status data
     */
    public SocketStatusDTO getSocketStatus(String serverName, String adapterGroup) {
        try {
            // Create a list to store all adapter data from all servers
            List<HashMap<String, Object>> allAdapterData = new ArrayList<>();

            // Loop through all online data source types
            for (DataSourceType server : DataSourceTypeManager.getOnlineDataSourceTypes()) {
                // Set server context
                DataSourceContextHolder.setDataSourceType(server);

                // Get server list for current data source
                List<EAIServer> serverList = eaiServerService.selectEaiServerIpList();
                if (serverList.isEmpty()) {
                    continue; // Skip if no servers found for this data source
                }

                List<String> urls = new ArrayList<>();
                for (EAIServer serverMap : serverList) {
                    String serverUrl = "http://" + serverMap.getEaisevrip() + ":"
                            + serverMap.getSevrlsnportname()
                            + HostInfomationForm.WEB_AGENT_CONTEXT_PATH
                            + AGENT_PAGE_STATUS + "?adapterType=SOC"
                            + "&eaiSvrInstNm="
                            + serverMap.getEaisevrinstncname();
                    urls.add(serverUrl);
                }

                // No filtering - get all data
                DataSetUtil gUtil = new DataSetUtil();
                List<HashMap<String, Object>> list = gUtil.flushXmlToClient(urls);

                // Add to the combined list
                allAdapterData.addAll(list);
            }

            // Reset the data source context
            DataSourceContextHolder.clearDataSourceType();

            // Convert the result to SocketStatusDTO
            SocketStatusDTO result = new SocketStatusDTO();
            result.setTotal(1);
            result.setPage(1);
            result.setRecords(allAdapterData.size());

            // Convert HashMap list to SocketStatusItemDTO list
            List<SocketStatusDTO.SocketStatusItemDTO> rows = new ArrayList<>();
            for (HashMap<String, Object> item : allAdapterData) {
                SocketStatusDTO.SocketStatusItemDTO row = new SocketStatusDTO.SocketStatusItemDTO();

                // Map the fields from HashMap to DTO
                row.setStatus((String) item.get("Status"));
                row.setEaiSevrInstncName((String) item.get("EaiSevrInstncName"));
                row.setTestMasterYn((String) item.get("TestMasterYn"));
                row.setAdptrNickName((String) item.get("AdptrNickName"));
                row.setStartStop((String) item.get("StartStop"));
                row.setAdptrBzwkGroupName((String) item.get("AdptrBzwkGroupName"));
                row.setAdptrBzwkName((String) item.get("AdptrBzwkName"));
                row.setAdptrType((String) item.get("AdptrType"));
                row.setStarting((String) item.get("Starting"));
                row.setAdptrBzwkDesc((String) item.get("AdptrBzwkDesc"));

                rows.add(row);
            }

            // Sort rows by AdptrBzwkGroupName in ascending order
            rows.sort(Comparator.comparing(
                    row -> row.getAdptrBzwkGroupName() != null ? row.getAdptrBzwkGroupName() : ""
            ));

            result.setRows(rows);
            return result;

        } catch (Exception e) {
            throw new RuntimeException("Failed to get socket status data", e);
        }
    }

    /**
     * Get socket process data
     * @param serverName Optional server name filter
     * @param adapterGroup Optional adapter group filter
     * @return SocketProcessDTO with all socket process data
     */
    public SocketProcessDTO getSocketProcess(String serverName, String adapterGroup) {
        try {
            // Create a list to store all adapter data from all servers
            List<HashMap<String, Object>> allAdapterData = new ArrayList<>();

            // Loop through all online data source types
            for (DataSourceType server : DataSourceTypeManager.getOnlineDataSourceTypes()) {
                // Set server context
                DataSourceContextHolder.setDataSourceType(server);

                // Get server list for current data source
                List<EAIServer> serverList = eaiServerService.selectEaiServerIpList();
                if (serverList.isEmpty()) {
                    continue; // Skip if no servers found for this data source
                }

                List<String> urls = new ArrayList<>();
                for (EAIServer serverMap : serverList) {
                    String serverUrl = "http://" + serverMap.getEaisevrip() + ":"
                            + serverMap.getSevrlsnportname()
                            + HostInfomationForm.WEB_AGENT_CONTEXT_PATH
                            + AGENT_PAGE_PROCESS + "?cmd=get+connection+summary"
                            + "&eaiSvrInstNm=" + serverMap.getEaisevrinstncname();

                    urls.add(serverUrl);
                }

                // No filtering - get all data
                DataSetUtil gUtil = new DataSetUtil();
                List<HashMap<String, Object>> list = gUtil.flushXmlToClient(urls);

                // Add to the combined list
                allAdapterData.addAll(list);
            }

            // Reset the data source context
            DataSourceContextHolder.clearDataSourceType();

            // Convert the result to SocketProcessDTO
            SocketProcessDTO result = new SocketProcessDTO();

            // Create JsonPageVoDTO
            SocketProcessDTO.JsonPageVoDTO jsonPageVo = new SocketProcessDTO.JsonPageVoDTO();
            jsonPageVo.setPage(1);
            jsonPageVo.setTotalCount(allAdapterData.size());
            jsonPageVo.setRows(10000);
            jsonPageVo.setTotal(1);
            jsonPageVo.setStartNum(1);
            jsonPageVo.setEndNum(10000);

            result.setJsonPageVo(jsonPageVo);
            result.setTotal(1);
            result.setPage(1);
            result.setRecords(allAdapterData.size());

            // Convert HashMap list to SocketProcessItemDTO list
            List<SocketProcessDTO.SocketProcessItemDTO> rows = new ArrayList<>();
            for (HashMap<String, Object> item : allAdapterData) {
                SocketProcessDTO.SocketProcessItemDTO row = new SocketProcessDTO.SocketProcessItemDTO();

                // Map the fields from HashMap to DTO
                row.setMaxConnection((String) item.get("MaxConnection"));
                row.setCurrentConnection((String) item.get("CurrentConnection"));
                row.setStatus((String) item.get("Status"));
                row.setEaiSevrInstncName((String) item.get("EaiSevrInstncName"));
                row.setAdptrBzwkGroupName((String) item.get("AdptrBzwkGroupName"));
                row.setRecvCount((String) item.get("RecvCount"));
                row.setAdptrBzwkName((String) item.get("AdptrBzwkName"));
                row.setAdptrBzwkDesc((String) item.get("AdptrBzwkDesc"));
                row.setSendCount((String) item.get("SendCount"));
                row.setErrorCount((String) item.get("ErrorCount"));

                rows.add(row);
            }

            // Sort rows by AdptrBzwkGroupName in ascending order
            rows.sort(Comparator.comparing(
                    row -> row.getAdptrBzwkGroupName() != null ? row.getAdptrBzwkGroupName() : ""
            ));

            result.setRows(rows);
            return result;

        } catch (Exception e) {
            throw new RuntimeException("Failed to get socket process data", e);
        }
    }


    /**
     * Get institution monitoring status data
     * @param statusData Socket status data with adapter information
     * @return List of institution status information
     */
    public List<InstitutionStatusDTO> getInstitutionStatus(SocketStatusDTO statusData) {
        try {
            // Set context to access AdapterGroup data
            DataSourceContextHolder.setDataSourceType(DataSourceTypeManager.getOnlineDataSourceTypes().get(0));

            // Get institutions with non-empty adptrinsticode
            List<AdapterGroup> adapterGroups = adapterGroupService.findByAdptrinsticodeNotEmpty();
            List<AdapterGroupUI> adapterGroupUIs = adapterGroups.stream()
                    .map(adapterGroupUIMapper::toVoWithoutChildrenMapping)
                    .collect(Collectors.toList());

            // Group adapter groups by institution code
            Map<String, List<AdapterGroupUI>> institutionGroups = adapterGroupUIs.stream()
                    .filter(ag -> StringUtils.isNotBlank(ag.getAdptrinsticode()))
                    .collect(Collectors.groupingBy(AdapterGroupUI::getAdptrinsticode));

            List<InstitutionStatusDTO> result = new ArrayList<>();

            // Process each institution
            for (Map.Entry<String, List<AdapterGroupUI>> entry : institutionGroups.entrySet()) {
                String institutionCode = entry.getKey();
                List<AdapterGroupUI> institutionAdapters = entry.getValue();

                if (institutionAdapters.isEmpty()) {
                    continue;
                }

                // Use the first adapter's information for institution details
                AdapterGroupUI firstAdapter = institutionAdapters.get(0);

                // Create a new institution status DTO
                InstitutionStatusDTO institutionStatus = new InstitutionStatusDTO();
                institutionStatus.setInstitutionCode(institutionCode);
                institutionStatus.setInstitutionName(firstAdapter.getAdptrinstiName());
                institutionStatus.setContactPerson(firstAdapter.getOsidinstibzwkrsempname());
                institutionStatus.setContactNumber(firstAdapter.getOsidinstitelno());

                // Get adapter group names for this institution
                List<String> adapterGroupNames = institutionAdapters.stream()
                        .map(AdapterGroupUI::getAdptrbzwkgroupname)
                        .collect(Collectors.toList());

                // Count normal and error adapters
                int normalCount = 0;
                int errorCount = 0;

                // Check status in statusData
                if (statusData != null && statusData.getRows() != null) {
                    for (SocketStatusDTO.SocketStatusItemDTO statusItem : statusData.getRows()) {
                        if (adapterGroupNames.contains(statusItem.getAdptrBzwkGroupName())) {
                            if ("NORMAL".equals(statusItem.getStatus()) ||
                                    "true".equals(statusItem.getStatus()) ||
                                    "OK".equals(statusItem.getStatus())) {
                                normalCount++;
                            } else {
                                errorCount++;
                            }
                        }
                    }
                }

                institutionStatus.setNormalAdapterCount(normalCount);
                institutionStatus.setErrorAdapterCount(errorCount);

                // Determine line status
                if (normalCount == 0 && errorCount == 0) {
                    institutionStatus.setLineStatus("UNDEFINED");
                } else if (errorCount == 0 && normalCount > 0) {
                    institutionStatus.setLineStatus("NORMAL");
                } else if (errorCount > 0 && normalCount > 0) {
                    institutionStatus.setLineStatus("WARN");
                } else if (errorCount > 0 && normalCount == 0) {
                    institutionStatus.setLineStatus("ERROR");
                }

                result.add(institutionStatus);
            }

            // Reset data source context
            DataSourceContextHolder.clearDataSourceType();

            // Sort the result by status priority: ERROR > WARN > NORMAL > UNDEFINED
            result.sort((a, b) -> {
                // Define status priority (higher number = higher priority)
                Map<String, Integer> statusPriority = new HashMap<>();
                statusPriority.put("ERROR", 3);
                statusPriority.put("WARN", 2);
                statusPriority.put("NORMAL", 1);
                statusPriority.put("UNDEFINED", 0);

                // Get priority values for comparison
                int priorityA = statusPriority.getOrDefault(a.getLineStatus(), 0);
                int priorityB = statusPriority.getOrDefault(b.getLineStatus(), 0);

                // Sort by priority (descending)
                return Integer.compare(priorityB, priorityA);
            });

            return result;
        } catch (Exception e) {
            DataSourceContextHolder.clearDataSourceType();
            throw new RuntimeException("Failed to get institution status data", e);
        }
    }

    /**
     * Get combined socket data (both status and process)
     * @param serverName Optional server name filter
     * @param adapterGroup Optional adapter group filter
     * @return Map containing both socket status and process data
     */
    public Map<String, Object> getCombinedSocketData(String serverName, String adapterGroup) {
        Map<String, Object> result = new HashMap<>();

        SocketStatusDTO statusData = getSocketStatus(serverName, adapterGroup);
        SocketProcessDTO processData = getSocketProcess(serverName, adapterGroup);
        List<InstitutionStatusDTO> institutionStatus = getInstitutionStatus(statusData);

        result.put("socketStatus", statusData);
        result.put("socketProcess", processData);
        result.put("institutionStatus", institutionStatus);

        return result;
    }
}