<?php

namespace LAM\CONFIG;

use baseType;
use htmlTable;
use htmlSubTitle;
use htmlImage;
use htmlOutputText;
use htmlSpacer;
use htmlButton;
use htmlGroup;
use htmlDiv;
use htmlResponsiveInputCheckbox;
use LAM\TYPES\TypeManager;
use LAMConfig;
use htmlResponsiveRow;
use htmlResponsiveInputField;
use function LAM\TYPES\getTypes;

/*
  This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
  Copyright (C) 2004 - 2026  Roland Gruber

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/


/**
 * Here the user can select the account types.
 *
 * @package configuration
 * @author Roland Gruber
 */


/** Access to config functions */
include_once __DIR__ . '/../../lib/config.inc';
/** Access to account types */
include_once __DIR__ . '/../../lib/types.inc';
/** common functions */
include_once __DIR__ . '/../../lib/configPages.inc';

// start session
if (isFileBasedSession()) {
	session_save_path("../../sess");
}
lam_start_session();

setlanguage();

// check if config is set
// if not: load login page
if (!isset($_SESSION['conf_config'])) {
	/** go back to login if password is invalid */
	require(__DIR__ . '/conflogin.php');
	exit;
}

// check if user canceled editing
if (isset($_POST['cancelSettings'])) {
	metaRefresh("../login.php");
	exit;
}

$conf = &$_SESSION['conf_config'];
if (!($conf instanceof LAMConfig)) {
	die();
}

$errorsToDisplay = checkTypeInput($conf);

// check if button was pressed and if we have to save the settings or go to another tab
if ((isset($_POST['saveSettings']) || isset($_POST['editmodules'])
		|| isset($_POST['edittypes']) || isset($_POST['generalSettingsButton'])
		|| isset($_POST['moduleSettings']) || isset($_POST['jobs']))
	&& (count($errorsToDisplay) === 0)) {
	// check if all types have modules
	$activeTypes = $conf->get_ActiveTypes();
	foreach ($activeTypes as $activeType) {
		$selectedModules = $conf->get_AccountModules($activeType);
		if (count($selectedModules) === 0) {
			// go to module selection
			metaRefresh("confmodules.php");
			exit;
		}
	}
	// go to final page
	if (isset($_POST['saveSettings'])) {
		metaRefresh("confsave.php");
		exit;
	}
	// go to modules page
	elseif (isset($_POST['editmodules'])) {
		metaRefresh("confmodules.php");
		exit;
	}
	// go to general page
	elseif (isset($_POST['generalSettingsButton'])) {
		metaRefresh("confmain.php");
		exit;
	}
	// go to module settings page
	elseif (isset($_POST['moduleSettings'])) {
		metaRefresh("moduleSettings.php");
		exit;
	}
	// go to jobs page
	elseif (isset($_POST['jobs'])) {
		metaRefresh("jobs.php");
		exit;
	}
}

$typeSettings = $conf->get_typeSettings();
$allScopes = getTypes();
$typeManager = new TypeManager($conf);
$activeTypes = $typeManager->getConfiguredTypes();
$activeScopes = [];
foreach ($activeTypes as $activeType) {
	$activeScopes[] = $activeType->getScope();
}
$activeScopes = array_unique($activeScopes);
$availableScopes = [];
foreach ($allScopes as $scope) {
	$scopeObj = new $scope(null);
	if (!in_array($scope, $activeScopes) || $scopeObj->supportsMultipleConfigs()) {
		$availableScopes[] = $scopeObj;
	}
}
usort($availableScopes, \LAM\CONFIG\compareTypesByAlias(...));

echo $_SESSION['header'];
printHeaderContents(_("LDAP Account Manager Configuration"), '../..');
echo "</head><body>\n";
// include all JavaScript files
printJsIncludes('../..');
printConfigurationPageHeaderBar($conf);

// print error messages
foreach ($errorsToDisplay as $errorToDisplay) {
	call_user_func_array(StatusMessage(...), $errorToDisplay);
}

echo "<form action=\"conftypes.php\" method=\"post\" novalidate=\"novalidate\">\n";

printConfigurationPageTabs(ConfigurationPageTab::TYPES);

$row = new htmlResponsiveRow();

// show available types
if ($availableScopes !== []) {
	$row->add(new htmlSubTitle(_("Available account types")));
	foreach ($availableScopes as $availableScope) {
		$availableLabelGroup = new htmlGroup();
		$availableLabelGroup->addElement(new htmlImage('../../graphics/' . $availableScope->getIcon(), '16px', '16px'));
		$availableLabelGroup->addElement(new htmlSpacer('0.5rem', null));
		$availableLabelGroup->addElement(new htmlOutputText($availableScope->getAlias()));
		$row->addField($availableLabelGroup);
		$availableDescriptionRow = new htmlResponsiveRow();
		$availableDescriptionRow->add(new htmlOutputText($availableScope->getDescription()), 10, 10, 10, 'responsiveField');
		$button = new htmlButton('add_' . $availableScope->getScope(), 'add.svg', true);
		$button->setTitle(_("Add"));
		$button->setCSSClasses(['size16']);
		$availableDescriptionRow->add($button, 2, 2, 2, 'responsiveField');
		$row->addField($availableDescriptionRow);
		$row->addVerticalSpacer('1rem');
	}
	$row->addVerticalSpacer('2rem');
}
parseHtml(null, $row, [], false);

$container = new htmlResponsiveRow();

$_SESSION['conftypes_optionTypes'] = [];
// show active types
if (count($activeTypes) > 0) {
	$container->add(new htmlSubTitle(_("Active account types")));
	$index = 0;
	foreach ($activeTypes as $activeType) {
		// title
		$titleGroup = new htmlGroup();
		$titleGroup->addElement(new htmlImage('../../graphics/' . $activeType->getIcon(), '16px', '16px'));
		$titleGroup->addElement(new htmlSpacer('0.5rem', null));
		$titleText = new htmlOutputText($activeType->getAlias());
		$titleText->setIsBold(true);
		$titleGroup->addElement($titleText);
		$container->addField($titleGroup);
		$descriptionRow = new htmlResponsiveRow();
		$descriptionRow->add(new htmlOutputText($activeType->getBaseType()->getDescription()), 12, 12, 9, 'responsiveField');
		$buttons = new htmlGroup();
		// move buttons
		if ($index > 0) {
			$upButton = new htmlButton('moveup_' . $activeType->getId(), 'up.svg', true);
			$upButton->setTitle(_("Up"));
			$upButton->setCSSClasses(['size16']);
			$buttons->addElement($upButton);
		}
		if ($index < (count($activeTypes) - 1)) {
			$upButton = new htmlButton('movedown_' . $activeType->getId(), 'down.svg', true);
			$upButton->setTitle(_("Down"));
			$upButton->setCSSClasses(['size16']);
			$buttons->addElement($upButton);
		}
		// delete button
		$delButton = new htmlButton('rem_' . $activeType->getId(), 'del.svg', true);
		$delButton->setTitle(_("Remove this account type"));
		$delButton->setCSSClasses(['size16']);
		$buttons->addElement($delButton);
		$buttonsDiv = new htmlDiv(null, $buttons, ['text-right']);
		$descriptionRow->add($buttonsDiv, 12, 12, 3, 'responsiveLabel');
		$container->addField($descriptionRow);
		$container->addVerticalSpacer('0.5rem');
		// LDAP suffix
		$suffix = '';
		if (!empty($typeSettings['suffix_' . $activeType->getId()])) {
			$suffix = $typeSettings['suffix_' . $activeType->getId()];
		}
		$suffixInput = new htmlResponsiveInputField(_("LDAP suffix"), 'suffix_' . $activeType->getId(), $suffix, '202', true);
		$container->add($suffixInput);
		// list attributes
		$attributes = $typeSettings['attr_' . $activeType->getId()] ?? $activeType->getBaseType()->getDefaultListAttributes();
		$attrsInput = new htmlResponsiveInputField(_("List attributes"), 'attr_' . $activeType->getId(), $attributes, '206');
		$attrsInput->setFieldMaxLength(1000);
		$container->add($attrsInput);
		// custom label
		$customLabel = '';
		if (isset($typeSettings['customLabel_' . $activeType->getId()])) {
			$customLabel = $typeSettings['customLabel_' . $activeType->getId()];
		}
		$customLabelInput = new htmlResponsiveInputField(_('Custom label'), 'customLabel_' . $activeType->getId(), $customLabel, '264');
		$container->add($customLabelInput);
		// LDAP filter
		$filter = '';
		if (isset($typeSettings['filter_' . $activeType->getId()])) {
			$filter = $typeSettings['filter_' . $activeType->getId()];
		}
		$filterInput = new htmlResponsiveInputField(_("Additional LDAP filter"), 'filter_' . $activeType->getId(), $filter, '260');
		$container->add($filterInput);
		// type options
		$typeConfigOptions = $activeType->getBaseType()->get_configOptions();
		if (!empty($typeConfigOptions)) {
			$container->add($typeConfigOptions);
			// save option types to session
			ob_start();
			$typeConfigOptionTypes = parseHtml(null, $typeConfigOptions, [], true);
			ob_end_clean();
			$_SESSION['conftypes_optionTypes'] = array_merge($_SESSION['conftypes_optionTypes'], $typeConfigOptionTypes);
		}
		// advanced options
		$advancedOptions = new htmlResponsiveRow();
		// read-only
		if (isLAMProVersion() && ($conf->getAccessLevel() == LAMConfig::ACCESS_ALL)) {
			$isReadOnly = false;
			if (isset($typeSettings['readOnly_' . $activeType->getId()])) {
				$isReadOnly = $typeSettings['readOnly_' . $activeType->getId()];
			}
			$readOnly = new htmlResponsiveInputCheckbox('readOnly_' . $activeType->getId(), $isReadOnly, _('Read-only'), '265');
			$readOnly->setElementsToDisable(['hideNewButton_' . $activeType->getId(), 'hideDeleteButton_' . $activeType->getId()]);
			$advancedOptions->add($readOnly);
		}
		// hidden type
		$hidden = false;
		if (isset($typeSettings['hidden_' . $activeType->getId()])) {
			$hidden = $typeSettings['hidden_' . $activeType->getId()];
		}
		$advancedOptions->add(new htmlResponsiveInputCheckbox('hidden_' . $activeType->getId(), $hidden, _('Hidden'), '261'));
		if (isLAMProVersion() && ($conf->getAccessLevel() == LAMConfig::ACCESS_ALL)) {
			// hide button to create new accounts
			$hideNewButton = false;
			if (isset($typeSettings['hideNewButton_' . $activeType->getId()])) {
				$hideNewButton = $typeSettings['hideNewButton_' . $activeType->getId()];
			}
			$advancedOptions->add(new htmlResponsiveInputCheckbox('hideNewButton_' . $activeType->getId(), $hideNewButton, _('No new entries'), '262'));
			// hide button to delete accounts
			$hideDeleteButton = false;
			if (isset($typeSettings['hideDeleteButton_' . $activeType->getId()])) {
				$hideDeleteButton = $typeSettings['hideDeleteButton_' . $activeType->getId()];
			}
			$advancedOptions->add(new htmlResponsiveInputCheckbox('hideDeleteButton_' . $activeType->getId(), $hideDeleteButton, _('Disallow delete'), '263'));
		}
		$container->add($advancedOptions);

		$container->addVerticalSpacer('2rem');
		$index++;
	}
}

$dynamicTypeOptions = [];
foreach ($_SESSION['conftypes_optionTypes'] as $key => $value) {
	if (isset($typeSettings[$key])) {
		$dynamicTypeOptions[$key] = explode(LAMConfig::LINE_SEPARATOR, $typeSettings[$key]);
	}
}
parseHtml(null, $container, $dynamicTypeOptions, false);

echo "</div></div>";

echo "<input type=\"hidden\" name=\"postAvailable\" value=\"yes\">\n";

$buttonContainer = new htmlTable();
$buttonContainer->addElement(new htmlSpacer(null, '10px'), true);
$saveButton = new htmlButton('saveSettings', _('Save'));
$saveButton->setCSSClasses(['lam-primary']);
$buttonContainer->addElement($saveButton);
$cancelButton = new htmlButton('cancelSettings', _('Cancel'));
$buttonContainer->addElement($cancelButton, true);
$buttonContainer->addElement(new htmlSpacer(null, '10px'), true);
parseHtml(null, $buttonContainer, [], false);

echo "</form>\n";
echo "</body>\n";
echo "</html>\n";


/**
 * Checks user input and saves the entered settings.
 *
 * @param LAMConfig $conf config
 * @return array<int, string[]> list of errors
 */
function checkTypeInput(LAMConfig $conf): array {
	if (!isset($_POST['postAvailable'])) {
		return [];
	}
	$errors = [];
	$typeManager = new TypeManager($conf);
	$typeSettings = $conf->get_typeSettings();
	$accountTypes = $conf->get_ActiveTypes();
	$postKeys = array_keys($_POST);
	foreach ($postKeys as $key) {
		// check if remove button was pressed
		if (str_starts_with($key, "rem_")) {
			$type = substr($key, 4);
			$accountTypes = array_flip($accountTypes);
			unset($accountTypes[$type]);
			$accountTypes = array_flip($accountTypes);
			$accountTypes = array_values($accountTypes);
		}
		// check if up button was pressed
		elseif (str_starts_with($key, "moveup_")) {
			$type = substr($key, 7);
			$pos = array_search($type, $accountTypes);
			$temp = $accountTypes[$pos - 1];
			$accountTypes[$pos - 1] = $accountTypes[$pos];
			$accountTypes[$pos] = $temp;
		}
		// check if down button was pressed
		elseif (str_starts_with($key, "movedown_")) {
			$type = substr($key, 9);
			$pos = array_search($type, $accountTypes);
			$temp = $accountTypes[$pos + 1];
			$accountTypes[$pos + 1] = $accountTypes[$pos];
			$accountTypes[$pos] = $temp;
		}
		// set suffixes
		elseif (str_starts_with($key, "suffix_")) {
			$typeSettings[$key] = trim($_POST[$key]);
			$type = $typeManager->getConfiguredType(substr($key, 7));
			if ($type === null) {
				continue;
			}
			if (strlen($_POST[$key]) < 1) {
				$errors[] = ["ERROR", _("LDAP Suffix is invalid!"), $type->getAlias()];
			}
		}
		// set attributes
		elseif (str_starts_with($key, "attr_")) {
			$typeSettings[$key] = $_POST[$key];
			$type = $typeManager->getConfiguredType(substr($key, 5));
			if ($type === null) {
				continue;
			}
			if (!is_string($_POST[$key]) || !preg_match("/^((#[^:;]+)|([^:;]*:[^:;]+))(;((#[^:;]+)|([^:;]*:[^:;]+)))*$/", $_POST[$key])) {
				$errors[] = ["ERROR", _("List attributes are invalid!"), $type->getAlias()];
			}
		}
		// set filter
		elseif (substr($key, 0, strlen('filter_')) === "filter_") {
			$typeSettings[$key] = $_POST[$key];
		}
		// set custom label
		elseif (str_starts_with($key, 'customLabel_')) {
			$typeSettings[$key] = $_POST[$key];
		}
	}
	$typeConfigOptions = extractConfigOptionsFromPOST($_SESSION['conftypes_optionTypes']);
	foreach ($accountTypes as $accountType) {
		// set hidden
		$key = "hidden_" . $accountType;
		$typeSettings[$key] = (isset($_POST[$key]) && ($_POST[$key] == 'on'));
		if (isLAMProVersion() && ($conf->getAccessLevel() == LAMConfig::ACCESS_ALL)) {
			// set if new entries are allowed
			$key = "hideNewButton_" . $accountType;
			$typeSettings[$key] = (isset($_POST[$key]) && ($_POST[$key] == 'on'));
			// set if deletion of entries is allowed
			$key = "hideDeleteButton_" . $accountType;
			$typeSettings[$key] = (isset($_POST[$key]) && ($_POST[$key] == 'on'));
			// set if account type is read-only
			$key = "readOnly_" . $accountType;
			$typeSettings[$key] = (isset($_POST[$key]) && ($_POST[$key] == 'on'));
		}
		// check dynamic type settings
		$configuredType = $typeManager->getConfiguredType($accountType);
		if ($configuredType === null) {
			continue;
		}
		$typeObj = $configuredType->getBaseType();
		$typeMessages = $typeObj->check_configOptions($typeConfigOptions);
		if (!empty($typeMessages)) {
			$errors = array_merge($errors, $typeMessages);
		}
	}
	// new type
	foreach ($_POST as $key => $value) {
		// check if add button was pressed
		if (str_starts_with($key, "add_")) {
			$scope = substr($key, 4);
			$accountTypes[] = $typeManager->generateNewTypeId($scope);
		}
	}
	// add dynamic type settings
	foreach ($typeConfigOptions as $key => $value) {
		$typeSettings[$key] = implode(LAMConfig::LINE_SEPARATOR, $value);
	}
	// save input
	$conf->set_typeSettings($typeSettings);
	$conf->set_ActiveTypes($accountTypes);
	// check for duplicate type aliases
	$aliasNames = [];
	foreach ($typeManager->getConfiguredTypes() as $type) {
		if (in_array($type->getAlias(), $aliasNames)) {
			$errors[] = ['ERROR', _('Please set a unique label for the account types.'), htmlspecialchars($type->getAlias())];
		}
		$aliasNames[] = $type->getAlias();
	}
	return $errors;
}

/**
 * Compares types by alias for sorting.
 *
 * @param baseType $a first type
 * @param baseType $b second type
 * @return int comparison result
 */
function compareTypesByAlias(baseType $a, baseType $b): int {
	return strnatcasecmp($a->getAlias(), $b->getAlias());
}
