<?php
/*
** Copyright (C) 2001-2025 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/>.
**/


require_once dirname(__FILE__) . '/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
require_once dirname(__FILE__).'/../behaviors/CTableBehavior.php';

//define('LOGIN', true);
//define('WITHOUT_LOGIN', false);

/**
 * @backup widget, profiles
 *
 * @dataSource AllItemValueTypes
 *
 * @onBefore prepareData
 */

class testDashboardWebMonitoringWidget extends testWidgets {
	protected static $dashboardid;
	protected static $groupids;
	protected static $update_widget = 'Update Web monitoring widget';
	const DEFAULT_DASHBOARD = 'Dashboard for Web monitoring widget test';
	const DASHBOARD_FOR_WIDGET_ACTIONS = 'Dashboard for Item navigator widget create/update test';

	/**
	 * Attach MessageBehavior and TableBehavior to the test.
	 *
	 * @return array
	 */
	public function getBehaviors() {
		return [
			CMessageBehavior::class,
			CTableBehavior::class
		];
	}

	/**
	 * Create data for autotests which use WebWidget.
	 *
	 * @return array
	 */
	/**
	 * Create the initial data and set static variables.
	 */
	public function prepareData() {
		// Create a Dashboard and pages for widgets.
		$dashboards = CDataHelper::call('dashboard.create', [
				[
				'name' => self::DEFAULT_DASHBOARD,
				'auto_start' => 0,
				'pages' => [
						[
						'name' => 'Layout'
						]
					]
				],
				[
				'name' => self::DASHBOARD_FOR_WIDGET_ACTIONS,
				'auto_start' => 0,
				'pages' => [
						[
					'name' => 'Actions',
						'widgets' => [
								[
									'name' => 'Update Web monitoring widget',
									'type' => 'web',
									'x' => 0,
									'y' => 0,
									'width' => 18,
									'height' => 4
								],
								[
									'name' => 'WebMonitoring for delete',
									'type' => 'web',
									'x' => 18,
									'y' => 0,
									'width' => 18,
									'height' => 4
								],
								[
									'name' => 'WebMonitoring for cancel',
									'type' => 'web',
									'x' => 36,
									'y' => 0,
									'width' => 18,
									'height' => 4
								]
							]
						]
					]
				]
		]);
		self::$dashboardid = CDataHelper::getIds('name');

		// Create hostgroups for hosts.
		CDataHelper::call('hostgroup.create', [
			['name' => 'First Group for Web Monitoring check'],
			['name' => 'Second Group for Web Monitoring check']
		]);
		self::$groupids = CDataHelper::getIds('name');

		// Create hosts.
		CDataHelper::createHosts([
			[
				'host' => 'First host for Web Monitoring widget',
				'groups' => [
					'groupid' => self::$groupids['First Group for Web Monitoring check']
				]
			],
			[
				'host' => 'Second host for Web Monitoring widget',
				'groups' => [
					'groupid' => self::$groupids['Second Group for Web Monitoring check']
				]
			]
		]);
	}

	public static function getWidgetData() {
		return [
			//#0 Name and show header
			[
				[
					'check_dialog_properties' => true,
					'expected' => TEST_GOOD,
					'fields' => [
						'Show header' => true,
						'Name' => 'Name and show header name'
					]
				]
			],
			//#1 Empty name added
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => ''
					]
				]
			],
			//#2 Empty fields
			[
				[
					'expected' => TEST_GOOD,
					'fields' => []
				]
			],
			//#3 Non English language in name
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Кириллица'
					]
				]
			],
			//#4
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Scenario tags' => 'Or',
						'Refresh interval' => '2 minutes'
					]
				]
			],
			//#5 All fields filled in
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Show header' => false,
						'Name' => 'Happy case',
						'Refresh interval' => '10 minutes',
					]
				]
			],
			//#6 Special symbols in name
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => '!@#$%^&*()1234567890-=',
						'Refresh interval' => '1 minute',
					]
				]
			],
			//#7 No refresh
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Show header' => false,
						'Refresh interval' => 'No refresh'
					]
				]
			],
			//#8 Host groups
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Host groups' => [
							'First Group for Web Monitoring check',
							'Second Group for Web Monitoring check'
							],
						'Refresh interval' => '30 seconds'
					]
				]
			],
			//#9 Host group and exclude host group
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Host groups' => 'First Group for Web Monitoring check',
						'Exclude host groups' => 'Second Group for Web Monitoring check',
						'Refresh interval' => '30 seconds'
					]
				]
			],
			//#10 Hosts
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Hosts' => [
							'First host for Web Monitoring widget',
							'Second host for Web Monitoring widget'
							],
						'Refresh interval' => '30 seconds'
					]
				]
			],
			//#11 Hosts and host groups
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => STRING_255,
						'Show header' => true,
						'Host groups' => [
							'First Group for Web Monitoring check',
							'Second Group for Web Monitoring check'
						],
						'Hosts' => [
							'First host for Web Monitoring widget',
							'Second host for Web Monitoring widget'
						],
						'Refresh interval' => '10 minutes'
					]
				]
			],

			//#12 Tags
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Check tags table',
						'Refresh interval' => '1 minute'
					],
					'Scenario tags' => [
							['name' => 'empty value', 'operator' => 'Equals', 'value' => ''],
							['name' => '', 'operator' => 'Does not contain', 'value' => 'empty tag'],
							['name' => 'Check host tag with operator - Equals ⚠️', 'operator' => 'Equals', 'value' => 'Warning ⚠️'],
							['name' => 'Check host tag with operator - Exists', 'operator' => 'Exists'],
							['name' => 'Check host tag with operator - Contains ❌', 'operator' => 'Contains', 'value' =>
								'tag value ❌'],
							['name' => 'Check host tag with operator - Does not exist', 'operator' => 'Does not exist'],
							['name' => 'Check host tag with operator - Does not equal', 'operator' => 'Does not equal',
								'value' => 'Average'],
							['name' => 'Check host tag with operator - Does not contain', 'operator' => 'Does not contain',
								'value' => 'Disaster']
						]
				]
			],
		];
	}

	public static function getCancelData() {
		return [
			// #0 Cancel creating widget with saving the dashboard.
			[
				[
					'cancel_form' => true,
					'create_widget' => true,
					'save_dashboard' => true
				]
			],
			// #1 Cancel updating widget with saving the dashboard.
			[
				[
					'cancel_form' => true,
					'create_widget' => false,
					'save_dashboard' => true
				]
			],
			// #2 Create widget without saving the dashboard.
			[
				[
					'cancel_form' => false,
					'create_widget' => true,
					'save_dashboard' => false
				]
			],
			// #3 Update widget without saving the dashboard.
			[
				[
					'cancel_form' => false,
					'create_widget' => false,
					'save_dashboard' => false
				]
			]
		];
	}

	/**
	 * Perform Web Monitoring widget creation or update and verify the result.
	 *
	 * @param boolean $update	updating is performed
	 */
	protected function checkWidgetForm($data, $update = false) {
		if ($data['expected'] === TEST_BAD) {
			$old_hash = CDBHelper::getHash(self::SQL);
		}

		$data['fields']['Name'] = ($data['fields'] === [])
			? ''
			: CTestArrayHelper::get($data, 'fields.Name', 'Web monitoring '.microtime());
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
					self::$dashboardid[self::DASHBOARD_FOR_WIDGET_ACTIONS])->waitUntilReady();
		$dashboard = CDashboardElement::find()->one();
		$old_widget_count = $dashboard->getWidgets()->count();

		$form = $update
			? $dashboard->getWidget(self::$update_widget)->edit()->asForm()
			: $dashboard->edit()->addWidget()->asForm();

		$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Web monitoring')]);

		if (array_key_exists('tags', $data)) {
			foreach ($data['tags'] as $entity => $values) {
				$this->setTagSelector('id:tags_table_'.$entity.'_tags');
				$this->setTags($values);
			}
		}

		$form->fill($data['fields']);

		if (array_key_exists('group_by', $data)) {
			$this->getGroupByTable()->fill($data['group_by']);
		}

		if ($data['expected'] === TEST_GOOD) {
			$values = $form->getFields()->filter(CElementFilter::VISIBLE)->asValues();
		}

		$form->submit();

		// Trim leading and trailing spaces from expected results if necessary.
		if (CTestArrayHelper::get($data, 'trim', false)) {
			$data = CTestArrayHelper::trim($data);
		}

		if ($data['expected'] === TEST_BAD) {
			$this->assertMessage($data['expected'], null, $data['error']);
			$this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
			COverlayDialogElement::find()->one()->close();
		}
		else {
			// If name is empty string it is replaced by default widget name "Web monitoring".
			$header = ($data['fields']['Name'] === '') ? 'Web monitoring' : $data['fields']['Name'];
			if ($update) {
				self::$update_widget = $header;
			}

			COverlayDialogElement::ensureNotPresent();
			$widget = $dashboard->getWidget($header);

			// Save Dashboard to ensure that widget is correctly saved.
			$dashboard->save()->waitUntilReady();
			$this->assertMessage(TEST_GOOD, 'Dashboard updated');

			// Check widgets count.
			$this->assertEquals($old_widget_count + ($update ? 0 : 1), $dashboard->getWidgets()->count());

			// Check new widget update interval.
			$refresh = (CTestArrayHelper::get($data['fields'], 'Refresh interval') === 'Default (1 minute)')
				? '1 minute'
				: (CTestArrayHelper::get($data['fields'], 'Refresh interval', '1 minute'));
			$this->assertEquals($refresh, $widget->getRefreshInterval());

			// Check new widget form fields and values in frontend.
			$saved_form = $widget->edit();
			$this->assertEquals($values, $saved_form->getFields()->filter(CElementFilter::VISIBLE)->asValues());
			$saved_form->checkValue($data['fields']);

			if (array_key_exists('tags', $data)) {
				foreach ($data['tags'] as $entity => $values) {
					$this->setTagSelector('id:tags_table_'.$entity.'_tags');
					$this->assertTags($values);
				}
			}

			// Close widget window and cancel editing the dashboard.
			COverlayDialogElement::find()->one()->close();
			$dashboard->cancelEditing();
		}
	}

	/**
	 * Function for checking cancelling form or submitting without any changes.
	 *
	 * @param boolean $cancel            true if cancel scenario, false if form is submitted
	 * @param boolean $create            true if create scenario, false if update
	 * @param boolean $save_dashboard    true if dashboard will be saved, false if not
	 */
	protected function checkNoChanges($cancel = false, $create = false, $save_dashboard = true) {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='
				.self::$dashboardid[self::DASHBOARD_FOR_WIDGET_ACTIONS])->waitUntilReady();;
		$dashboard = CDashboardElement::find()->one()->waitUntilReady();
		$dashboard->invalidate();
		$dashboard = CDashboardElement::find()->one();
		$old_widget_count = $dashboard->getWidgets()->count();
		$dashboard->edit();

		$form = $create
			? $dashboard->addWidget()->asForm()
			: $dashboard->getWidget(self::$update_widget)->edit();

		$dialog = COverlayDialogElement::find()->one()->waitUntilReady();

		if ($create) {
			$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Web monitoring')]);
		}
		else {
			$values = $form->getValues();
		}

		if ($cancel || !$save_dashboard) {
			$form->fill(
				[
					'Name' => 'Update Web monitoring widget',
					'Refresh interval' => '10 minutes',
				]
			);
		}

		if ($cancel) {
			$dialog->query('button:Cancel')->one()->click();
		}
		else {
			$form->submit();
		}

		COverlayDialogElement::ensureNotPresent();

		if (!$cancel) {
			$dashboard->getWidget($save_dashboard ? self::$update_widget : 'Update Web monitoring widget')->waitUntilReady();
		}

		if ($save_dashboard) {
			$dashboard->save();
			$this->assertMessage(TEST_GOOD, 'Dashboard updated');
		}
		else {
			$dashboard->cancelEditing();
		}

		$this->assertEquals($old_widget_count, $dashboard->getWidgets()->count());

		// Check that updating widget form values did not change in frontend.
		if (!$create && !$save_dashboard) {
			$this->assertEquals($values, $dashboard->getWidget(self::$update_widget)->edit()->getValues());
			COverlayDialogElement::find()->one()->close();
		}
	}

	/**
	 * Check Web monitoring widget layout.
	 */
	public function testDashboardWebMonitoringWidget_Layout() {
		// Open the create form.
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid[self::DEFAULT_DASHBOARD])->waitUntilReady();
		$dashboard = CDashboardElement::find()->one();
		$dialog = $dashboard->edit()->addWidget();
		$this->assertEquals('Add widget', $dialog->getTitle());
		$form = $dialog->asForm();
		$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Web monitoring')]);
	//Check fields presence and default values
		$form->checkValue([
			'Name' => '',
			'Refresh interval' => 'Default (1 minute)',
			'Show header' => true,
			'Host groups'=> '',
			'Exclude host groups'=> '',
			'Hosts'=> '',
			'Scenario tags' => 'And/Or',
			'Show hosts in maintenance' => true,
		]);
		$this->assertTrue($form->getField('Name')->isAttributePresent(['maxlength' => '255', 'placeholder' => 'default']));
	// Check dropdown options.
		$this->assertEquals($form->getField('Refresh interval')->getOptions()->asText(), [
				'Default (1 minute)',
				'No refresh',
				'10 seconds',
				'30 seconds',
				'1 minute',
				'2 minutes',
				'10 minutes',
				'15 minutes'
		]);
		$popup_menu_selector = 'xpath:.//button[contains(@class, "zi-chevron-down")]';
		$host_groups = ['Host groups', 'Widget'];
		$hosts = ['Hosts', 'Widget', 'Dashboard'];
		foreach (['Host groups', 'Hosts'] as $label) {
			$field = $form->getField($label);
		// Check Select dropdown menu button.
			$menu_button = $field->query($popup_menu_selector)->asPopupButton()->one();
			$this->assertEquals(($label === 'Host groups') ? $host_groups : $hosts,
					$menu_button->getMenu()->getItems()->asText()
			);
		// After selecting Dashboard from dropdown menu, check hint and field value.
			if ($label === 'Hosts') {
				$menu_button->select('Dashboard');
				$form->checkValue(['Hosts' => 'Dashboard']);
				$this->assertTrue($field->query('xpath:.//span[@data-hintbox-contents="Dashboard is used as data source."]')
						->one()->isVisible()
				);
			}
		// After selecting Widget from dropdown menu, check overlay dialog appearance and title.
			$menu_button->select('Widget');
			$dialogs = COverlayDialogElement::find()->all();
			$this->assertEquals('Widget', $dialogs->last()->waitUntilReady()->getTitle());
			$dialogs->last()->close(true);
		}
		// After clicking on Select button, check overlay dialog appearance and title.
		foreach (['Host groups','Hosts'] as $label) {
			$field = $form->getField($label);
			$field->query('button:Select')->waitUntilCLickable()->one()->click();
			$dialogs = COverlayDialogElement::find()->all();
			$label = ($label === 'Item patterns') ? 'Items' : $label;
			$this->assertEquals($label, $dialogs->last()->waitUntilReady()->getTitle());
			$dialogs->last()->close(true);
		}
		// Check that close button is present and clickable
		$this->assertTrue($dialog->query('class:btn-overlay-close')->one()->isClickable());

		// Check if footer buttons are present and clickable.
		$this->assertEquals(['Add', 'Cancel'], $dialog->getFooter()->query('button')->all()
				->filter(CElementFilter::CLICKABLE)->asText()
		);
	}

	/**
	 * Check Web monitoring widget layout.
	 *
	 * @dataProvider getWidgetData
	 *
	 */
	public function testDashboardWebMonitoringWidget_Create($data) {
		$this->checkWidgetForm($data);
	}

	/**
	 * @dataProvider getWidgetData
	 */
	public function testDashboardWebMonitoringWidget_Update($data) {
		$this->checkWidgetForm($data, true);
	}

	/**
	 * Test opening Web monitoring form and saving with no changes made.
	 */
	public function testDashboardItemNavigatorWidget_SimpleUpdate() {
		$this->checkNoChanges();
		}

	/**
	 * @dataProvider getCancelData
	 */
	public function testDashboardGaugeWidget_Cancel($data) {
		$this->checkNoChanges($data['cancel_form'], $data['create_widget'], $data['save_dashboard']);
		}
	/**
	 * Delete Web monitoring widget check
	 */
	public function testDashboardWebMonitoringWidget_Delete() {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid[self::DASHBOARD_FOR_WIDGET_ACTIONS])->waitUntilReady();
		$dashboard = CDashboardElement::find()->one()->edit();
		$widget = $dashboard->getWidget('WebMonitoring for delete');
		$dashboard->deleteWidget('WebMonitoring for delete');
		$widget->waitUntilNotPresent();
		$dashboard->save();
		$this->assertMessage(TEST_GOOD, 'Dashboard updated');

		// Check that widget is not present on dashboard.
		$this->assertFalse($dashboard->getWidget('WebMonitoring for delete', false)->isValid());
		$this->assertEquals(0, CDBHelper::getCount('SELECT * FROM widget_field wf'.
				' LEFT JOIN widget w'.
					' ON w.widgetid=wf.widgetid'.
					' WHERE w.name='.zbx_dbstr('WebMonitoring for delete')
		));
	}
}//Main class close