<?php declare(strict_types = 0);
/*
** Copyright (C) 2001-2024 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/


namespace Widgets\HostNavigator\Actions;

use API,
	CArrayHelper,
	CControllerDashboardWidgetView,
	CControllerResponseData,
	CProfile,
	CSeverityHelper;

use Widgets\HostNavigator\Includes\{
	CWidgetFieldHostGrouping,
	WidgetForm
};

class WidgetView extends CControllerDashboardWidgetView {

	protected function init(): void {
		parent::init();

		$this->addValidationRules([
			'with_config' =>	'in 1',
			'widgetid' =>		'db widget.widgetid',
			'fields' =>			'array'
		]);
	}

	protected function doAction(): void {
		$data = [
			'name' => $this->getInput('name', $this->widget->getDefaultName()),
			'user' => [
				'debug_mode' => $this->getDebugMode()
			],
			'vars' => $this->getHosts()
		];

		if ($this->hasInput('with_config')) {
			$data['vars']['config'] = $this->getConfig($this->hasInput('widgetid')
				? $this->getInput('widgetid')
				: null
			);
		}

		$this->setResponse(new CControllerResponseData($data));
	}

	private function getHosts(): array {
		$no_data = [
			'hosts' => [],
			'is_limit_exceeded' => false,
			'maintenances' => []
		];

		$override_hostid = $this->fields_values['override_hostid'] ? $this->fields_values['override_hostid'][0] : '';

		if ($override_hostid === '' && $this->isTemplateDashboard()) {
			return $no_data;
		}

		$is_show_in_maintenance_on = $this->fields_values['maintenance'] == 1;

		$output = $is_show_in_maintenance_on
			? ['hostid', 'name', 'status', 'maintenanceid', 'maintenance_status']
			: ['hostid', 'name'];

		$group_by_host_groups = false;
		$group_by_severity = $this->fields_values['problems'] != WidgetForm::PROBLEMS_NONE;
		$tags_to_keep = [];

		foreach ($this->fields_values['group_by'] as $group_by_attribute) {
			switch ($group_by_attribute['attribute']) {
				case CWidgetFieldHostGrouping::GROUP_BY_TAG_VALUE:
					$tags_to_keep[] = $group_by_attribute['tag_name'];
					break;
				case CWidgetFieldHostGrouping::GROUP_BY_HOST_GROUP:
					$group_by_host_groups = true;
					break;
				case CWidgetFieldHostGrouping::GROUP_BY_SEVERITY:
					$group_by_severity = true;
					break;
			}
		}

		$hosts = [];

		if ($override_hostid === '' && !$this->isTemplateDashboard()) {
			$groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;

			// Get hosts from host pattern and search narrowing criteria.
			$hosts = API::Host()->get([
				'output' => [],
				'groupids' => $groupids,
				'evaltype' => $this->fields_values['host_tags_evaltype'],
				'tags' => $this->fields_values['host_tags'] ?: null,
				'search' => [
					'name' => in_array('*', $this->fields_values['hosts'], true) ? null : $this->fields_values['hosts']
				],
				'searchByAny' => true,
				'searchWildcardsEnabled' => true,
				'severities' => $this->fields_values['severities'] ?: null,
				'preservekeys' => true
			]);

			if (!$hosts) {
				return $no_data;
			}

			// Get additional info for narrowed down hosts and filter them by status and maintenance status.
			$hosts = API::Host()->get([
				'output' => $output,
				'hostids' => array_keys($hosts),
				'filter' => [
					'status' => $this->fields_values['status'] == WidgetForm::HOST_STATUS_ANY
						? null
						: $this->fields_values['status'],
					'maintenance_status' => $is_show_in_maintenance_on ? null : HOST_MAINTENANCE_STATUS_OFF
				],
				'selectHostGroups' => $group_by_host_groups ? ['groupid', 'name'] : null,
				'selectTags' => $tags_to_keep ? ['tag', 'value'] : null,
				'sortfield' => 'name',
				// Request more than the set limit to distinguish if there are even more hosts available.
				'limit' => $this->fields_values['show_lines'] + 1
			]);
		}
		elseif ($override_hostid !== '') {
			$hosts = API::Host()->get([
				'output' => $output,
				'hostids' => [$override_hostid],
				'severities' => $this->fields_values['severities'] ?: null,
				'filter' => [
					'maintenance_status' => $is_show_in_maintenance_on ? null : HOST_MAINTENANCE_STATUS_OFF
				],
				'selectHostGroups' => $group_by_host_groups ? ['groupid', 'name'] : null,
				'selectTags' => $tags_to_keep ? ['tag', 'value'] : null
			]);
		}

		if (!$hosts) {
			return $no_data;
		}

		CArrayHelper::sort($hosts, ['name']);
		$hosts = array_values($hosts);

		$is_limit_exceeded = false;

		if (!$this->isTemplateDashboard() && count($hosts) > $this->fields_values['show_lines']) {
			$is_limit_exceeded = true;

			array_pop($hosts);
		}

		if ($group_by_severity) {
			// Select triggers and problems to calculate number of problems for each host.
			$triggers = API::Trigger()->get([
				'output' => [],
				'selectHosts' => ['hostid'],
				'hostids' => array_column($hosts, 'hostid'),
				'skipDependent' => true,
				'monitored' => true,
				'preservekeys' => true
			]);

			$problems = API::Problem()->get([
				'output' => ['eventid', 'objectid', 'severity'],
				'source' => EVENT_SOURCE_TRIGGERS,
				'object' => EVENT_OBJECT_TRIGGER,
				'objectids' => array_keys($triggers),
				'suppressed' => $this->fields_values['problems'] == WidgetForm::PROBLEMS_UNSUPPRESSED ? false : null,
				'severities' => $this->fields_values['severities'] ?: null,
				'symptom' => false
			]);

			// Group all problems per host per severity.
			$host_problems = [];

			foreach ($problems as $problem) {
				foreach ($triggers[$problem['objectid']]['hosts'] as $trigger_host) {
					$host_problems[$trigger_host['hostid']][$problem['severity']][$problem['eventid']] = true;
				}
			}
		}

		$maintenanceids = [];

		if ($group_by_severity || $is_show_in_maintenance_on || $tags_to_keep || $group_by_host_groups) {
			foreach ($hosts as &$host) {
				if ($tags_to_keep) {
					$host['tags'] = array_values(array_filter($host['tags'], function($tag) use ($tags_to_keep) {
						return in_array($tag['tag'], $tags_to_keep, true);
					}));
				}

				if ($group_by_severity) {
					$host['problem_count'] = array_fill(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT, 0);

					// Count the number of problems (as value) per severity (as key).
					if ($host_problems && array_key_exists($host['hostid'], $host_problems)) {
						foreach ($host_problems[$host['hostid']] as $severity => $problems) {
							$host['problem_count'][$severity] = count($problems);
						}
					}
				}

				if ($is_show_in_maintenance_on) {
					if ($host['status'] == HOST_STATUS_MONITORED
							&& $host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
						$maintenanceids[$host['maintenanceid']] = true;
					}
					else {
						unset($host['maintenanceid']);
					}
					unset($host['maintenance_status'], $host['status']);
				}

				if ($group_by_host_groups && $override_hostid === '' && !$this->isTemplateDashboard()
						&& $groupids !== null) {
					$host['hostgroups'] = array_values(
						array_filter($host['hostgroups'], function($group) use ($groupids) {
							return in_array($group['groupid'], $groupids);
						})
					);
				}
			}
			unset($host);
		}

		if ($maintenanceids) {
			$maintenances = API::Maintenance()->get([
				'output' => ['name', 'maintenance_type', 'description'],
				'maintenanceids' => array_keys($maintenanceids),
				'preservekeys' => true
			]);

			foreach ($maintenances as &$maintenance) {
				$maintenance['maintenance_type'] = (int) $maintenance['maintenance_type'];

				unset($maintenance['maintenanceid']);
			}
			unset($maintenance);
		}
		else {
			$maintenances = [];
		}

		return [
			'hosts' => $hosts,
			'is_limit_exceeded' => $is_limit_exceeded,
			'maintenances' => $maintenances
		];
	}

	private function getConfig(string $widgetid = null): array {
		$open_groups = [];

		if ($widgetid !== null) {
			$open_groupids = CProfile::findByIdxPattern('web.dashboard.widget.open.%', $widgetid);

			foreach ($open_groupids as $open_groupid) {
				$open_group = CProfile::get($open_groupid, null, $widgetid);

				if ($open_group !== null) {
					$open_groups[] = $open_group;
				}
			}
		}

		$severities = [];

		if ($this->fields_values['problems'] != WidgetForm::PROBLEMS_NONE
				|| in_array(CWidgetFieldHostGrouping::GROUP_BY_SEVERITY,
					array_column($this->fields_values['group_by'], 'attribute')
				)) {
			$severities = CSeverityHelper::getSeverities();

			foreach ($severities as &$severity) {
				$severity['status_style'] = CSeverityHelper::getStatusStyle($severity['value']);
			}
			unset($severity);
		}

		return [
			'group_by' => $this->fields_values['group_by'],
			'open_groups' => $open_groups,
			'show_problems' => $this->fields_values['problems'] != WidgetForm::PROBLEMS_NONE,
			'severities' => $severities
		];
	}
}
