<?php

namespace StevenBuehner\sbChurchtoolsGrouphomepage\Controller;

use StevenBuehner\sbChurchtoolsGrouphomepage\Always\MyContainer;
use StevenBuehner\sbChurchtoolsGrouphomepage\Helper\ArrayHelper;
use StevenBuehner\sbChurchtoolsGrouphomepage\Service\CtPersonService;
use StevenBuehner\sbChurchtoolsGrouphomepage\Service\Models\PersonCollection;
use StevenBuehner\sbChurchtoolsGrouphomepage\Service\Models\PersonData;
use WP_REST_Request;

class PersonsFrontendController extends AbstractController {


	protected function init(): void {

		$this->setUpHooks(
			FALSE,
			FALSE,
			FALSE,
			TRUE,
			FALSE,
			TRUE,
			TRUE
		);

		$this->domain = 'sb-ct-blocks';

	}


	public function registerGutenbergBlocksAndCategories(): void {
		parent::registerGutenbergBlocksAndCategories();

		$this->registerBlockJson(__DIR__ . '/../../build/Persons', TRUE, TRUE);

	}

	public function registerRestRequests() {

		parent::registerRestRequests();

		register_rest_route('churchtools/persons/v1', '/get', [
			'methods'             => \WP_REST_Server::READABLE,
			'callback'            => [$this, 'action_get_ct_persons_json'],
			'args'                => array(
				'groupSelection' => [
					'description'       => 'JSON Data of configured groups',
					'type'              => 'string',
					'validate_callback' => '__return_true',
					'required'          => FALSE // Werden schon gebraucht ... aber das wird bei der RÜckgabe der Daten angegeben, damit es keinen Error wirft
				],
				'values'         => [
					'description'       => 'Array of values to include in the result per person',
					'type'              => 'array',
					'validate_callback' => function ($param, $request, $key) {
						if (!is_array($param)) {
							return FALSE;
						}

						// Entweder 'firstname', 'lastname', 'fullname', 'picture', 'gender', oder <ID Gruppe>:<Feldname>
						foreach ($param as $val) {
							if (preg_match('~^(' . join('|', [PersonData::FIRSTNAME, PersonData::LASTNAME, PersonData::FULLNAME, PersonData::PHONE, PersonData::PHONE, PersonData::ICON, PersonData::GENDER]) . '|[0-9]+:[a-zA-Z_ -]+)$~', $val) !== 1) {
								return FALSE;
							}
						}

						return TRUE;
					},
					'required'          => FALSE // Werden schon gebraucht ... aber das wird der RÜckgabe der Daten angegeben, damit es keinen Error wirft
				],
			),
			'permission_callback' => '__return_true',
		]);

	}

	public function action_get_ct_persons_json(WP_REST_Request $request): array {

		$groupSelection = $request->get_param('groupSelection');
		$props          = $request->get_params();

		if ($groupSelection === NULL) {
			return ['success' => FALSE,
			        'message' => 'Missing configuration'];
		}

		try {

			$parsedGroupSelection = $this->parseGroupSelection($groupSelection);
			$requiredProps        = $this->extractRequiredProps($props, $parsedGroupSelection);
			$geladeneDaten        = $this->loadChurchToolsValues($parsedGroupSelection, $requiredProps);

		} catch (\Exception $e) {
			error_log('Class: ' . self::class . ':' . __FUNCTION__ . ' -> ' . $e->getMessage());
			return ['success' => FALSE,
			        'message' => 'Error while retrieving data. See Logfile'];
		}

		return ['success' => TRUE,
		        'data'    => [
			        'persons' => $geladeneDaten->serializeOnly($requiredProps)
		        ]
		];

	}


	public function render($attributes = [], $content = '') {

		$parsedGroupSelection = $this->parseGroupSelection($attributes['groupSelection']);
		$requiredProps        = $this->extractRequiredProps($attributes, $parsedGroupSelection);
		$geladeneDaten        = $this->loadChurchToolsValues($parsedGroupSelection, $requiredProps);
		$responsePersonsData  = $geladeneDaten->serializeOnly($requiredProps);

		$componentProps = [
			'data' => ArrayHelper::onlyKeys($attributes, ['enableContactFormular', 'groupBy', 'showPicture', 'showHeadline', 'showSubHeadline', 'showDescription', 'headlineFormat', 'subHeadlineFormat', 'descriptionFormat', 'descriptionSeparator', 'displaySize', 'borderColor', 'borderWidth', 'maxImageWidth'])
		];

		// $componentProps['class'] = 'sb-ct-persons';
		$componentProps['data']['persons'] = $responsePersonsData;

		return $this->modifyHtmlElement($content, $componentProps);

	}

	protected function loadChurchToolsValues(array &$parsedGroupSelection, &$angeforderteWerte): PersonCollection {

		$resultCollection = new PersonCollection();
		$groupApi         = MyContainer::getCtGroupService();

		foreach ($parsedGroupSelection as $item) {

			$groupId   = $item['group']['value'];
			$groupName = empty($item['replaceGroupname']) ? $item['group']['label'] : $item['replaceGroupname'];

			$groupRolesFilter = [];
			foreach ($item['groupRoles'] as $groupRoleEntry) {
				$groupRolesFilter[] = (int)$groupRoleEntry['value'];
			}

			$pps = $groupApi->getGroupWithFields($groupId, $groupRolesFilter);

			foreach ($pps as $p) {

				$person = new PersonData($p->getPersonId(), $p->getPerson()->getDomainAttributes()->getFirstName(), $p->getPerson()->getDomainAttributes()->getLastName());

				$person->addGroupName($groupId, $groupName);

				// Füge die Url hinzu, falls vorhanden
				$url = $p->getPerson()->getImageUrl();
				if (!empty($url)) {
					$person->setIconUrl($url);
				}

				// Füge den Namen der Gruppenrolle zu den Daten hinzu
				$personsRoleId = $p->getGroupTypeRoleId();
				if (!empty($personsRoleId)) {
					foreach ($item['groupRoles'] as $groupRoleEntry) {
						if ($groupRoleEntry['value'] == $personsRoleId && !empty($groupRoleEntry['label'])) {
							$person->addGroupField($groupId, PersonData::GROUP_ROLE, $groupRoleEntry['label']);
						}
					}
				}

				$resultCollection->addPerson($person);

			}


		}

		// Lade noch die E-Mails oder Telefonnummern nach, falls diese gefordert sind
		if (isset($angeforderteWerte['hasMail'])) {
			$personService = MyContainer::getCtPersonService();

			foreach ($resultCollection->getPersons() as $person) {

				try {
 					$ctPerson = $personService->getPerson($person->getId());
				} catch (\Exception $e) {
					$ctPerson = NULL;
				} catch (\Error $e) {
					$ctPerson = NULL;
				}

			}
		}

		return $resultCollection;

	}


	protected function parseGroupSelection(string $groupSelectionJson): array {

		// Wenn der Wert eine leere Zeichenkette ist, gib ein leeres Array zurück
		if ($groupSelectionJson === "") {
			return [];
		}

		// Versuche den Wert als JSON zu parsen
		$parsed = json_decode($groupSelectionJson, TRUE);

		// Überprüfen, ob json_decode einen Fehler zurückgegeben hat
		if (json_last_error() !== JSON_ERROR_NONE) {
			return [];
		}

		if (!is_array($parsed)) {
			return [];
		}

		// Check basic existance of variables
		/**
		 * @var array <int, array{
		 *     group: array{
		 *         label: string,
		 *          value: int,
		 *          groupTypeId: int
		 *     },
		 *      groupRoles: array <int, array{
		 *         value: int,
		 *         label: string
		 *      }>,
		 *      replaceGroupname: string
		 * }> $parsed
		 */
		foreach ($parsed as $index => $groupSelectionItem) {

			if (!isset($groupSelectionItem['group']) || !isset($groupSelectionItem['groupRoles']) || !is_array($groupSelectionItem['group']) || !is_array($groupSelectionItem['groupRoles'])) {
				throw new \Exception('Invalid group selection');
			} else {

				foreach ($groupSelectionItem["group"] as $groupKey => $value) {
					switch ($groupKey) {
						case 'label':
							break;
						case 'value':
						case 'groupTypeId':
							$parsed[$index]['group'][$groupKey] = (int)$value;
							break;
						default:
							unset($parsed[$index]['group'][$groupKey]);
					};
				}

				foreach ($groupSelectionItem["groupRoles"] as $groupRoleKey => $value) {

					if (!isset($value['value']) || !isset($value['label'])) {
						throw new \Exception('Invalid groupRoles selection');
					}

					$parsed[$index]['groupRoles'][$groupRoleKey] = [
						'value' => (int)$value['value'],
						'label' => $value['label']
					];

				}

				$parsed[$index]['replaceGroupname'] = !empty($parsed[$index]['replaceGroupname']) ? $parsed[$index]['replaceGroupname'] : '';

				if (in_array($groupSelectionItem["group"]['value'], [0, NULL])) {
					// Gruppen mit ID =  NULL oder 0 sind nicht erlaubt und Fehler der UI
					unset($parsed[$index]);
				}

			}
		}

		return $parsed;
	}


	/**
	 * Die Funktion extrahiert aus den attributen und den $parsedGroupSelection alle Werte, die aus den Personendaten herausgeladen werden sollen/dürfen.
	 * Das Ergebnis ist eine Key-Liste, die auf PersonData::serializeOnly angewandt werden kann um nur die benötigten Daten zu extrahieren und zu senden
	 * @param mixed $attributes
	 * @return string[]
	 */
	protected function extractRequiredProps(mixed $attributes, &$parsedGroupSelection): array {

		$dataToSearch = [
			isset($attributes['showHeadline']) && $attributes['showHeadline'] === "1" && !empty($attributes['headlineFormat']) ? $attributes['headlineFormat'] : NULL,
			isset($attributes['showSubHeadline']) && $attributes['showSubHeadline'] === "1" && !empty($attributes['subHeadlineFormat']) ? $attributes['subHeadlineFormat'] : NULL,
			isset($attributes['showDescription']) && $attributes['showDescription'] === "1" && !empty($attributes['descriptionFormat']) ? $attributes['descriptionFormat'] : NULL,
		];

		// Regex-Muster zur Extraktion von Variablen in eckigen Klammern
		$pattern = '/\[(([0-9]+:)?[^\]]+)\]/';

		// Ergebnis-Array für alle gefundenen Variablen
		$extractedVariables = [];

		// Schleife durch das Array
		foreach ($dataToSearch as $item) {
			if ($item !== NULL) {
				// Führe preg_match_all aus, um alle Matches zu finden
				if (preg_match_all($pattern, $item, $matches)) {

					// Die gefundenen Variablen befinden sich in $matches[1]
					foreach ($matches[1] as $match) {
						$extractedVariables[$match] = $match;
					}

				}
			}
		}

		if (isset($attributes['showPicture']) && $attributes['showPicture'] === "1") {
			$extractedVariables[PersonData::ICON] = PersonData::ICON;
		}

		if (isset($attributes['enableContactFormular']) && $attributes['enableContactFormular'] === "1") {
			$extractedVariables['hasMail'] = 'hasMail';
		}

		$key = array_search('groups with roles', $extractedVariables);
		if ($key !== FALSE) {
			// Lösche das Element
			unset($extractedVariables[$key]);

			// Verwende statt dessen gruppen UND Rollen
			$extractedVariables['groups'] = 'groups';
			$extractedVariables['roles']  = 'roles';
		}


		// Beim Stichwort "groups" füge die Namen alle Gruppen zur Liste der benötigten Werte hinzu
		$key = array_search('groups', $extractedVariables);
		if ($key !== FALSE) {
			// Lösche das Element
			unset($extractedVariables[$key]);

			foreach ($parsedGroupSelection as $groupKey => $item) {
				$groupId = $item['group']['value'];
				if (!empty($groupId)) {
					$key                      = $groupId . ":" . PersonData::GROUP_NAME;
					$extractedVariables[$key] = $key;
				}
			}

		}

		// Beim Stichwort "role" füge die Gruppen-Rollen alle Gruppen zur Liste der benötigten Werte hinzu
		$key = array_search('roles', $extractedVariables);
		if ($key !== FALSE) {
			// Lösche das Element
			unset($extractedVariables[$key]);

			foreach ($parsedGroupSelection as $groupKey => $item) {
				$groupId = $item['group']['value'];
				if (!empty($groupId)) {
					$key                      = $groupId . ":" . PersonData::GROUP_ROLE;
					$extractedVariables[$key] = $key;
				}
			}

		}

		return $extractedVariables;

	}


}
