/*
 *   Copyright (c) Motadata 2025. All rights reserved.
 *
 *   This source code is the property of Motadata and constitutes
 *   proprietary and confidential information. Unauthorized copying, distribution,
 *   modification, or use of this file, via any medium, is strictly prohibited
 *   unless prior written permission is obtained from Motadata.
 *
 *   Unauthorized access or use of this software may result in legal action
 *   and/or prosecution to the fullest extent of the law.
 *
 *   This software is provided "AS IS," without warranties of any kind, express
 *   or implied, including but not limited to implied warranties of
 *   merchantability or fitness for a particular purpose. In no event shall
 *   Motadata be held liable for any damages arising from the use
 *   of this software.
 *
 *   For inquiries, contact: engg@motadata.com
 *
 */

import com.ibm.as400.access.*;
import java.util.*;
import org.json.JSONArray;
import org.json.JSONObject;

public class As400DataCollector {

    @SuppressWarnings({"deprecation"})
    public static void main(String[] args)
    {
        var data = new JSONObject();

        var encodedString = args[0];

        var decodedJson = new String(Base64.getDecoder().decode(encodedString));

        var payload = new JSONObject(decodedJson);

        var pluginType = payload.getString("plugin.type");

        var hostname = payload.getString("ip");

        var username = payload.getString("username");

        var password = payload.getString("password");

        try
        {
            var as400 = new AS400(hostname, username, password);

            as400.setGuiAvailable(false);

            as400.validateSignon();

            switch (pluginType)
            {
                case "system":

                    var sysstat = new SystemStatus(as400);

                    data.put("system.asp.percent", sysstat.getPercentSystemASPUsed());

                    data.put("system.asp.bytes", sysstat.getSystemASP() * 1024 * 1024);

                    data.put("system.cpu.percent", sysstat.getPercentProcessingUnitUsed());

                    data.put("system.permanent.address.percent", sysstat.getPercentPermanentAddresses());

                    data.put("system.temporary.address.percent", sysstat.getPercentTemporaryAddresses());

                    data.put("System.unprotected.storage.used.bytes", sysstat.getCurrentUnprotectedStorageUsed()* 1024 * 1024);

                    data.put("system.unprotected.storage.maximum.bytes", sysstat.getMaximumUnprotectedStorageUsed()* 1024 * 1024);

                    data.put("System.main.storage.capacity.bytes", sysstat.getMainStorageSize() * 1024);

                    data.put("system.cpu.cores", sysstat.getNumberOfProcessors());

                    data.put("system.pools", sysstat.getPoolsNumber());

                    data.put("system.signed.on.users", sysstat.getUsersCurrentSignedOn());

                    data.put("system.total.jobs", sysstat.getJobsInSystem());

                    data.put("system.active.jobs", sysstat.getActiveJobsInSystem());

                    data.put("system.batch.jobs", sysstat.getBatchJobsRunning());

                    data.put("system.jobs.waiting.message", sysstat.getBatchJobsWaitingForMessage());

                    data.put("system.active.threads", sysstat.getActiveThreadsInSystem());

                    data.put("system.jobs.waiting.print", sysstat.getBatchJobsEndedWithPrinterOutputWaitingToPrint());

                    data.put("system.batch.jobs.ending", sysstat.getBatchJobsEnding());

                    data.put("system.batch.jobs.queued", sysstat.getBatchJobsHeldOnJobQueue());

                    data.put("system.batch.jobs.running", sysstat.getBatchJobsHeldWhileRunning());

                    data.put("system.batch.jobs.on.held.job.queue", sysstat.getBatchJobsOnAHeldJobQueue());

                    data.put("system.batch.jobs.on.unassigned.job.queue", sysstat.getBatchJobsOnUnassignedJobQueue());

                    data.put("system.batch.jobs.waiting.run", sysstat.getBatchJobsWaitingToRunOrAlreadyScheduled());

                    data.put("system.partitions", sysstat.getNumberOfPartitions());

                    data.put("system.auxiliary.storage.bytes", sysstat.getTotalAuxiliaryStorage() * 1024 * 1024);

                    data.put("system.user.sessions.ended.waiting.print", sysstat.getUsersSignedOffWithPrinterOutputWaitingToPrint());

                    data.put("system.user.suspended.by.groupJobs", sysstat.getUsersSuspendedByGroupJobs());

                    data.put("system.user.suspended.by.systemRequest", sysstat.getUsersSuspendedBySystemRequest());

                    data.put("system.temporarily.signed.off.users", sysstat.getUsersTemporarilySignedOff());

                    data.put("system.current.processing.capacity", sysstat.getCurrentProcessingCapacity());

                    data.put("system.interactive.performance.percent", sysstat.getPercentCurrentInteractivePerformance());

                    data.put("system.maximum.jobs", String.valueOf(sysstat.getMaximumJobsInSystem()));

                    SystemValue sv;

                    var svl = new SystemValueList(as400);

                    var p = new HashMap<>();

                    var v = svl.getGroup(SystemValueList.GROUP_ALL);

                    var list = v.elements();

                    while (list.hasMoreElements())
                    {
                        sv = (SystemValue) list.nextElement();

                        p.put(sv.getName(), sv.getValue());
                    }

                    data.put("system.name", p.get("SYSNAME"));

                    data.put("system.model", p.get("QMODEL") + " " + p.get("QPRCFEAT"));

                    data.put("system.version", "V" + as400.getVersion() + "R" + as400.getRelease() + "M" + as400.getModification());

                    data.put("system.serial.number", p.get("QSRLNBR"));

                    data.put("system.security.level", p.get("QSECURITY"));

                    break;

                case "pool":

                    sysstat = new SystemStatus(as400);

                    var poolsArray = new JSONArray();

                    var totalPoolSize = 0;

                    var totalReservedSize = 0;

                    var totalDBPages = 0;

                    var totalDBFaults = 0;

                    var totalNonDBPages = 0;

                    var totalNonDBFaults = 0;

                    var systemPools = sysstat.getSystemPools();

                    while (systemPools.hasMoreElements())
                    {
                        var pool = (SystemPool) systemPools.nextElement();

                        try
                        {
                            var poolName = pool.getName();

                            if (poolName == null)
                            {
                                poolName = "Pool_" + pool.getIdentifier();
                            }


                            if (poolName.contains("*"))
                            {
                                var poolMetrics = new JSONObject();

                                var poolSize = pool.getSizeLong();

                                var reservedSize = pool.getReservedSize();

                                var dbPages = pool.getDatabasePages();

                                var dbFaults = pool.getDatabaseFaults();

                                var nonDbPages = pool.getNonDatabasePages();

                                var nonDbFaults = pool.getNonDatabaseFaults();

                                poolMetrics.put("system.pool", poolName);

                                poolMetrics.put("system.pool.size.bytes", poolSize * 1024);

                                poolMetrics.put("system.pool.reserved.size.bytes", reservedSize * 1024);

                                poolMetrics.put("system.pool.db.pages", dbPages);

                                poolMetrics.put("system.pool.db.faults", dbFaults);

                                poolMetrics.put("system.pool.non.db.pages", nonDbPages);

                                poolMetrics.put("system.pool.non.db.faults", nonDbFaults);

                                poolsArray.put(poolMetrics);

                                totalPoolSize += poolSize;

                                totalReservedSize += reservedSize;

                                totalDBPages += dbPages;

                                totalDBFaults += dbFaults;

                                totalNonDBPages += nonDbPages;

                                totalNonDBFaults += nonDbFaults;

                            }

                        }
                        catch (Exception e) {
                            // Skip this pool if there's an error
                        }
                    }

                    if (poolsArray.length() > 0)
                    {
                        data.put("system.pool", poolsArray);
                    }

                    data.put("system.pools.size.bytes", totalPoolSize * 1024);

                    data.put("system.pools.reserved.size.bytes", totalReservedSize * 1024);

                    data.put("system.pools.db.pages", totalDBPages);

                    data.put("system.pools.db.faults", totalDBFaults);

                    data.put("system.pools.non.db.pages", totalNonDBPages);

                    data.put("system.pools.non.db.faults", totalNonDBFaults);

                    break;

                case "job" :

                    var jobsArray = new JSONArray();

                    try
                    {
                        // Create a job list to get all active jobs
                        var jobList = new JobList(as400);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_PRIMARY_JOB_STATUS_ACTIVE, Boolean.TRUE);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_JOB_QUEUE_STATUS_HELD, Boolean.FALSE);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_JOB_QUEUE_STATUS_READY, Boolean.FALSE);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_JOB_QUEUE_STATUS_SCHEDULE, Boolean.FALSE);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_PRIMARY_JOB_STATUS_JOBQ, Boolean.FALSE);

                        jobList.addJobSelectionCriteria(JobList.SELECTION_PRIMARY_JOB_STATUS_OUTQ, Boolean.FALSE);

                        jobList.load();

                        var jobs = jobList.getJobs();

                        while (jobs.hasMoreElements())
                        {
                            var job = (Job) jobs.nextElement();

                            var jobMetrics = new JSONObject();

                            try
                            {
                                // Create a JSON object for this job
                                // Get job metrics
                                jobMetrics.put("system.job.name", job.getName());

                                jobMetrics.put("system.job.user", job.getUser());

                                jobMetrics.put("system.job", job.getNumber());

                                jobMetrics.put("system.job.type", job.getValue(Job.JOB_TYPE).toString());

                                jobMetrics.put("system.job.status", job.getStatus());

                                jobMetrics.put("system.job.subsystem", job.getValue(Job.SUBSYSTEM).toString());

                                jobMetrics.put("system.job.active.status", job.getValue(Job.ACTIVE_JOB_STATUS).toString());

                                jobMetrics.put("system.job.pool", job.getValue(Job.CURRENT_SYSTEM_POOL_ID).toString());

                                jobMetrics.put("system.job.function", job.getValue(Job.FUNCTION_TYPE).toString());

                                jobMetrics.put("system.job.priority", job.getValue(Job.RUN_PRIORITY));

                                jobMetrics.put("system.job.threads", job.getValue(Job.MAX_THREADS));

                                jobMetrics.put("system.job.queue", job.getValue(Job.JOB_QUEUE).toString());

                                jobMetrics.put("system.job.pool.name", job.getValue(Job.MEMORY_POOL).toString());

                                jobMetrics.put("system.job.cpu", job.getValue(Job.ELAPSED_CPU_PERCENT_USED));

                                var jobStartTime = job.getDate();

                                if (jobStartTime != null)
                                {
                                    var uptimeMillis = System.currentTimeMillis() - jobStartTime.getTime();

                                    var uptimeSeconds = uptimeMillis / 1000;

                                    jobMetrics.put("system.job.uptime", uptimeSeconds);
                                }

                                // Add job to jobs array
                                jobsArray.put(jobMetrics);

                            }
                            catch (Exception e)
                            {
                                // Skip this job if there's an error
                                jobsArray.put(jobMetrics);

                                continue;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // If job list creation fails, add empty array
                    }

                    if (jobsArray.length() > 0)
                    {
                        // Add jobs array to main data object
                        data.put("system.job", jobsArray);
                    }

                    break;

                case "message":

                    var messagesArray = new JSONArray();

                    try
                    {
                        // Create a message queue to get system messages
                        // QSYSOPR is the system operator message queue
                        var messageQueue = new MessageQueue(as400, "/QSYS.LIB/QSYSOPR.MSGQ");

                        messageQueue.load();

                        var length = messageQueue.getLength();


                        var start = Math.max(0, length - 1000);
                        // Get messages from the queue
                        var messages = messageQueue.getMessages(start,length);

                        for (QueuedMessage message : messages)
                        {
                            try {

                                if (message.getDate().getTimeInMillis() > (System.currentTimeMillis() - 360000))
                                {
                                    // Create a JSON object for this message
                                    var messageMetrics = new JSONObject();
                                    // Get message metrics

                                    messageMetrics.put("system.message", message.getID());

                                    messageMetrics.put("system.message.severity", message.getSeverity());

                                    messageMetrics.put("system.message.type", message.getType());

                                    messageMetrics.put("system.message.text", message.getText());

                                    messageMetrics.put("system.message.date", message.getDate().getTime());

                                    messageMetrics.put("system.message.default.help", message.getHelp());

                                    // Add message to messages array
                                    messagesArray.put(messageMetrics);

                                }

                            }
                            catch (Exception e)
                            {
                                // Skip this message if there's an error
                                continue;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // If message queue access fails, add empty array
                    }

                    if (messagesArray.length() > 0)
                    {
                        // Add messages array to main data object
                        data.put("system.message", messagesArray);
                    }

                    break;

                case "spool":
                    // Add spool details metrics
                    var spoolsArray = new JSONArray();

                    try
                    {
                        // Create a spooled file list to get all spooled files
                        var spooledFileList = new SpooledFileList(as400);

                        // Set filter to get all spooled files
                        spooledFileList.setUserFilter(username);

                        var spoolCount = 0;
                        // Enumerate through all spooled files
                        var spooledFiles = spooledFileList.getObjects();

                        while (spooledFiles.hasMoreElements() && spoolCount++ < 100)
                        {
                            var spooledFile = (SpooledFile) spooledFiles.nextElement();

                            try
                            {
                                // Create a JSON object for this spooled file
                                var spoolMetrics = new JSONObject();

                                // Get spool metrics
                                spoolMetrics.put("system.spool.name", spooledFile.getName());

                                spoolMetrics.put("system.spool", spooledFile.getNumber());

                                spoolMetrics.put("system.spool.job.name", spooledFile.getJobName());

                                spoolMetrics.put("system.spool.job.owner", spooledFile.getJobUser());

                                spoolMetrics.put("system.spool.job.number", spooledFile.getJobNumber());

                                spoolMetrics.put("system.spool.status", spooledFile.getStringAttribute(SpooledFile.ATTR_SPLFSTATUS));

                                spoolMetrics.put("system.spool.printer", spooledFile.getStringAttribute(SpooledFile.ATTR_PRINTER));

                                spoolMetrics.put("system.spool.total.pages", spooledFile.getIntegerAttribute(SpooledFile.ATTR_PAGES));
                                // Add spooled file to spools array
                                spoolsArray.put(spoolMetrics);

                            }
                            catch (Exception e)
                            {
                                // Skip this spooled file if there's an error
                                continue;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // If spooled file list creation fails, add empty array
                    }

                    if (spoolsArray.length() > 0)
                    {
                        data.put("system.spool", spoolsArray);
                    }
                    // Add spools array to main data object

                default:
                    break;
            }

            as400.disconnectAllServices();

        }
        catch (Exception e)
        {

        }
        finally
        {
            System.out.println(data);
        }
    }

}
