<?php

namespace idoit\Module\Check_mk\Console\Command;

use Exception;
use idoit\Console\Command\AbstractCommand;
use isys_cmdb_dao_category_g_cmk;
use isys_component_dao_logbook;
use isys_monitoring_dao_hosts;
use isys_monitoring_helper;
use isys_monitoring_livestatus;
use isys_tenantsettings;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CheckMkCommand extends AbstractCommand
{
    const NAME = 'check_mk-livestatus';

    private $m_cmk_instances = [];

    /**
     * Check_MK category DAO.
     *
     * @var  isys_cmdb_dao_category_g_cmk
     */
    private $m_dao_cmk;

    /**
     * Logbook DAO.
     *
     * @var  isys_component_dao_logbook
     */
    private $m_dao_logbook;

    /**
     * This will hold all hosts with "state > 0".
     *
     * @var  array
     */
    private $m_hosts = [];

    /**
     * @var OutputInterface
     */
    private $output;

    /**
     * Get name for command
     *
     * @return string
     */
    public function getCommandName()
    {
        return self::NAME;
    }

    /**
     * Get description for command
     *
     * @return string
     */
    public function getCommandDescription()
    {
        return 'Imports monitoring status changes from Livestatus to the i-doit Logbook';
    }

    /**
     * Retrieve Command InputDefinition
     *
     * @return InputDefinition
     */
    public function getCommandDefinition()
    {
        $definition = new InputDefinition();

        return $definition;
    }

    /**
     * Checks if a command can have a config file via --config
     *
     * @return bool
     */
    public function isConfigurable()
    {
        return true;
    }

    /**
     * Returns an array of command usages
     *
     * @return string[]
     */
    public function getCommandUsages()
    {
        return [];
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->output = $output;
        $this->output->writeln("Setting up system environment");

        $this->m_dao_logbook = new isys_component_dao_logbook($this->container->database);
        $this->m_dao_cmk = new isys_cmdb_dao_category_g_cmk($this->container->database);

        $this->output->writeln("Check_MK Handler initialized (" . date("Y-m-d H:i:s") . ")");

        isys_tenantsettings::initialize($this->container->database_system);

        $this->prepare_connection()
            ->read_log()
            ->write_logbook();
    }

    /**
     * This method will prepare the "m_cmk_instances" variable.
     *
     * @return  CheckMkCommand
     */
    private function prepare_connection()
    {
        // Load all hosts.
        $this->output->writeln("Load all defined Check_MK instances... ");
        $l_host_res = isys_monitoring_dao_hosts::instance($this->container->database)
            ->get_data(null, C__MONITORING__TYPE_LIVESTATUS);

        if (count($l_host_res)) {
            while ($l_host_row = $l_host_res->get_row()) {
                if (!$l_host_row['isys_monitoring_hosts__active']) {
                    $this->output->writeln('Found ' . $l_host_row['isys_monitoring_hosts__title'] . ', but its deactivated...');
                    continue;
                }

                try {
                    $this->m_cmk_instances[$l_host_row['isys_monitoring_hosts__id']] = isys_monitoring_livestatus::factory((int)$l_host_row['isys_monitoring_hosts__id']);
                    $this->output->writeln('Found ' . $l_host_row['isys_monitoring_hosts__title'] . ', and connected!');
                } catch (Exception $e) {
                    $this->output->writeln($l_host_row['isys_monitoring_hosts__title'] . ': ' . $e->getMessage());
                }
            }
        }

        return $this;
    }

    /**
     * This method will read the Check_MK log and write data in the i-doit logbook.
     *
     * @return  CheckMkCommand
     */
    private function read_log()
    {
        $l_last_check = isys_tenantsettings::get('check_mk.controller.last_log_check', '0');
        isys_tenantsettings::set('check_mk.controller.last_log_check', time());

        foreach ($this->m_cmk_instances as $l_cmk_host_id => $l_cmk_host) {
            $this->output->writeln('Last check was... ' . ($l_last_check > 0 ? date('d.m.Y H:i:s', $l_last_check) : 'Never!'));

            // Retrieve all log-entries since the last check and which are "not OK".
            $l_entries = $l_cmk_host->query([
                "GET log",
                "Filter: time > " . $l_last_check,
                "Filter: state != " . C__MODULE__CHECK_MK__LIVESTATUS_STATE__UP,
                "Columns: host_name time state plugin_output"
            ]);

            foreach ($l_entries as $l_entry) {
                list ($l_hostname, $l_time, $l_state, $l_state_message) = $l_entry;

                $l_host = isys_monitoring_helper::get_objects_by_hostname($l_cmk_host_id, $l_hostname);

                if (empty($l_host)) {
                    $this->output->writeln('  Could not find the host "' . $l_hostname . '" in your CMDB.');
                    continue;
                }

                $this->output->writeln('Got data for object "' . $l_host['isys_obj__title'] . '" (#' . $l_host['isys_obj__id'] . '), with the hostname "' . $l_hostname .
                    '".');

                if ($l_host['isys_obj__status'] != C__RECORD_STATUS__NORMAL || $l_host['isys_catg_monitoring_list__status'] != C__RECORD_STATUS__NORMAL) {
                    $this->output->writeln('> But the host is archived or deleted.');
                    continue;
                }

                // Format the host data and save it to an array.
                $this->m_hosts[$l_host['isys_obj__id']][$l_time][] = [
                    'hostname'      => $l_hostname,
                    'state'         => $l_state,
                    'state_message' => $l_state_message,
                    'cmdb_data'     => $l_host
                ];

                ksort($this->m_hosts[$l_host['isys_obj__id']]);
            }
        }

        return $this;
    }

    /**
     * This method will write the logbook entries to all the found hosts.
     *
     * @throws  Exception
     * @return  CheckMkCommand
     */
    private function write_logbook()
    {
        if (count($this->m_hosts)) {
            $this->output->writeln('Writing logbook entries...');

            foreach ($this->m_hosts as $l_obj_id => $l_dates) {
                foreach ($l_dates as $l_date => $l_rows) {
                    foreach ($l_rows as $l_data) {
                        $this->output->writeln('  Writing logbook for "' . $l_data['cmdb_data']['isys_obj__title'] . '" (#' . $l_obj_id . '), ' .
                            date('d.m.Y H:i:s', $l_date));

                        switch ($l_data['state']) {
                            case C__MODULE__CHECK_MK__LIVESTATUS_STATE__UP:
                                $this->output->writeln('  > OK');
                                $this->m_dao_logbook->set_entry($l_data['hostname'] . ": UP",
                                    "State time: " . date('d.m.Y h:i:s', $l_date) . "<br />Change to: UP<br />" . $l_data['state_message'], date('Y-m-d h:i:s', $l_date),
                                    C__LOGBOOK__ALERT_LEVEL__1, $l_obj_id, null, null, null, C__LOGBOOK_SOURCE__CMK);
                                break;

                            case C__MODULE__CHECK_MK__LIVESTATUS_STATE__DOWN:
                                $this->output->writeln('  > DOWN');
                                $this->m_dao_logbook->set_entry($l_data['hostname'] . ": DOWN",
                                    "State time: " . date('d.m.Y h:i:s', $l_date) . "<br />Change to: DOWN<br />" . $l_data['state_message'], date('Y-m-d h:i:s', $l_date),
                                    C__LOGBOOK__ALERT_LEVEL__3, $l_obj_id, null, null, null, C__LOGBOOK_SOURCE__CMK);
                                break;

                                break;
                            case C__MODULE__CHECK_MK__LIVESTATUS_STATE__UNREACHABLE:
                                $this->output->writeln('  > UNREACHABLE');
                                $this->m_dao_logbook->set_entry($l_data['hostname'] . ": DOWN",
                                    "State time: " . date('d.m.Y h:i:s', $l_date) . "<br />Change to: UNREACHABLE<br />" . $l_data['state_message'],
                                    date('Y-m-d h:i:s', $l_date), C__LOGBOOK__ALERT_LEVEL__3, $l_obj_id, null, null, null, C__LOGBOOK_SOURCE__CMK);
                                break;

                            default:
                            case C__MODULE__CHECK_MK__LIVESTATUS_STATE__UNKNOWN:
                                $this->output->writeln('  > UNKNOWN "' . $l_data['state_message'] . '"...');
                        }
                    }
                }
            }
        }

        return $this;
    }
}
