<?php

namespace Planaday\Entities\Booking\Validators;

use DateTime;
use Exception;
use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Config\Booking\Form\Student\StudentSettings;
use Planaday\Entities\Booking\BookingController;
use Planaday\Entities\Booking\DTO\Address;
use Planaday\Entities\Booking\DTO\BookingStudent;
use Planaday\Entities\Booking\PostRequest\BookingStudentFieldEnum;
use Planaday\Platform\PlatformAbstraction\LocalizationController;
use Planaday\Utility\DataFilters\AdvancedValidatorFactory;
use Planaday\Utility\DataFilters\InvalidException;
use Planaday\Utility\DataFilters\TypeValidator;
use Planaday\Utility\DataGrouping\Map;

class BookingStudentValidator {
	public function __construct(
		protected LocalizationController $localisationService,
		protected TypeValidator $typeValidator,
		protected AdvancedValidatorFactory $advancedValidatorFactory,
		protected BookingController $bookingController,
		protected BookingAddressValidator $addressValidator,
		protected StudentSettings $studentSettings
	) {}

	/**
	 * @param array<mixed> $postData
	 * @param int $studentIndex
	 *
	 * @return BookingStudent|array<string,mixed>
	 */
	public function getFromPost(array $postData, int $studentIndex): BookingStudent|array {
		$t      = $this->localisationService;
		$type   = $this->typeValidator;
		$errors = [];

		try {
			$gender = $type->str($postData[BookingStudentFieldEnum::GENDER] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::GENDER] = $t->translate('Geslacht is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$initials = $type->str($postData[BookingStudentFieldEnum::INITIALS] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::INITIALS] = $t->translate('Initialen is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$firstName = $type->str($postData[BookingStudentFieldEnum::FIRST_NAME] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::FIRST_NAME] = $t->translate('Voornaam is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$lastName = $type->str($postData[BookingStudentFieldEnum::LAST_NAME] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::LAST_NAME] = $t->translate('Achternaam is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$nickName = $type->str($postData[BookingStudentFieldEnum::NICK_NAME] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::NICK_NAME] = $t->translate('Bijnaam is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$maidenName = $type->str($postData[BookingStudentFieldEnum::MAIDEN_NAME] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::MAIDEN_NAME] = $t->translate('Meisjesnaam is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$prefix = $type->str($postData[BookingStudentFieldEnum::PREFIX] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::PREFIX] = $t->translate('Tussenvoegsel is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$dateOfBirth = $type->time($postData[BookingStudentFieldEnum::DATE_OF_BIRTH] ?? null);
			if ($dateOfBirth !== null) {
				$dateOfBirth = new DateTime( '@' . $dateOfBirth );
			}
		} catch (InvalidException|Exception ) {
			$errors[BookingStudentFieldEnum::DATE_OF_BIRTH] = $t->translate('Geboortedatum is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$placeOfBirth = $type->str($postData[BookingStudentFieldEnum::PLACE_OF_BIRTH] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::PLACE_OF_BIRTH] = $t->translate('Geboorteplaats is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$email = $type->str($postData[BookingStudentFieldEnum::EMAIL] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::EMAIL] = $t->translate('Email is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$phoneNumber = $type->str($postData[BookingStudentFieldEnum::PHONE_NUMBER] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::PHONE_NUMBER] = $t->translate('Telefoonnummer is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$companyPosition = $type->str($postData[BookingStudentFieldEnum::COMPANY_POSITION] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::COMPANY_POSITION] = $t->translate('Functie is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$employeeNumber = $type->str($postData[BookingStudentFieldEnum::EMPLOYEE_NUMBER] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::EMPLOYEE_NUMBER] = $t->translate('Personeelsnummer is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$employeeNumber = $type->str($postData[BookingStudentFieldEnum::INTERNAL_REFERENCE] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::INTERNAL_REFERENCE] = $t->translate('Interne referentie is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$costCenterCode = $type->str($postData[BookingStudentFieldEnum::COST_CENTER_CODE] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::COST_CENTER_CODE] = $t->translate('Kostenplaats is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$stapRegulation = $type->bool($postData[BookingStudentFieldEnum::STAP_REGULATION] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::STAP_REGULATION] = $t->translate('Stap is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$isContact = $type->bool($postData[BookingStudentFieldEnum::IS_CONTACT] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::IS_CONTACT] = $t->translate('Contactpersoon is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$code95 = $type->bool($postData[BookingStudentFieldEnum::PROCESS_CODE95] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::PROCESS_CODE95] = $t->translate('Code 95 is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$soob = $type->bool($postData[BookingStudentFieldEnum::PROCESS_SOOB] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::PROCESS_SOOB] = $t->translate('SOOB is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}
		try {
			$remark = $type->str($postData[BookingStudentFieldEnum::REMARK] ?? null);
		} catch (InvalidException ) {
			$errors[BookingStudentFieldEnum::REMARK] = $t->translate('Opmerking is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}

		$extraFields = [];
		try {
			$extraFieldsPost = $type->array($postData[BookingStudentFieldEnum::EXTRA_FIELDS] ?? null);
			$extraFieldsApi = $this->bookingController->getExtraFields();
			$extraFieldErrors = [];
			foreach ($extraFieldsApi as $extraField) {
				$postValue = $extraFieldsPost[$extraField->getId()] ?? null;

				if ($extraField->isRequired() && $postValue === null) {
					$extraFieldErrors[$extraField->getId()] = sprintf($t->translate("Extra vraag %s is verplicht bij deelnemer "), $extraField->getName()) . ($studentIndex + 1);
					continue;
				}

				if (!is_array($postValue)){
					$extraFields[$extraField->getId()] = $type->str($postValue);
				} else {
					$extraFields[$extraField->getId()] = [];
					foreach ($postValue as $subValue) {
						$extraFields[$extraField->getId()][] = $type->str($subValue);
					}
				}
			}

			if (count($extraFieldErrors) > 0) {
				$errors[BookingStudentFieldEnum::EXTRA_FIELDS] = $extraFieldErrors;
			}
		} catch ( InvalidResponseException ) {
			$errors[BookingStudentFieldEnum::EXTRA_FIELDS] = $t->translate('server error');
		} catch ( InvalidException ) {
			$errors[BookingStudentFieldEnum::EXTRA_FIELDS] = $t->translate('Extra vragen zijn niet geldig bij deelnemer ') . ($studentIndex + 1);
		}

		if (empty($firstName)) $errors[BookingStudentFieldEnum::FIRST_NAME] = $t->translate('Voornaam is verplicht bij deelnemer ') . ($studentIndex + 1);

		if (empty($lastName)) $errors[BookingStudentFieldEnum::LAST_NAME] = $t->translate('Achternaam is verplicht bij deelnemer ') . ($studentIndex + 1);

		if (empty($email)) $errors[BookingStudentFieldEnum::EMAIL] = $t->translate('Email is verplicht bij deelnemer ') . ($studentIndex + 1);
		if (!$this->advancedValidatorFactory->isEmail($email ?? null))
			$errors[BookingStudentFieldEnum::EMAIL] = $t->translate('Email is geen geldig email adres bij deelnemer ') . ($studentIndex + 1);

		$studentSettings = $this->studentSettings;

		if ($studentSettings->isAskGender() && $studentSettings->isGenderRequired() && empty($gender)) {
			$errors[BookingStudentFieldEnum::GENDER] = $t->translate('Geslacht is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ($studentSettings->isAskInitials() && $studentSettings->isInitialsRequired() && empty($initials)) {
			$errors[BookingStudentFieldEnum::INITIALS] = $t->translate('Initialen is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ($studentSettings->isAskPhoneNumber() && $studentSettings->isPhoneNumberRequired() && empty($phoneNumber)) {
			$errors[BookingStudentFieldEnum::PHONE_NUMBER] = $t->translate('Telefoonnummer is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ($studentSettings->isAskCostCenterCode() && $studentSettings->isCostCenterCodeRequired()){
			$errors[BookingStudentFieldEnum::COST_CENTER_CODE] = $t->translate('Kostenplaats is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ((($studentSettings->isAskDateOfBirth() && $studentSettings->isDateOfBirthRequired()) || ($stapRegulation ?? false)) && empty($dateOfBirth)){
			$errors[BookingStudentFieldEnum::DATE_OF_BIRTH] = $t->translate('Geboortedatum is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ($studentSettings->isAskPlaceOfBirth() && $studentSettings->isPlaceOfBirthRequired() && empty($placeOfBirth)){
			$errors[BookingStudentFieldEnum::PLACE_OF_BIRTH] = $t->translate('Geboorteplaats is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		if ($studentSettings->isAskCompanyPosition() && $studentSettings->isCompanyPositionRequired() && empty($companyPosition)){
			$errors[BookingStudentFieldEnum::COMPANY_POSITION] = $t->translate('Functie opgeven is verplicht bij deelnemer ') . ($studentIndex + 1);
		}

		try{
			$address = $this->addressValidator->getFromPost( $type->array($postData[BookingStudentFieldEnum::ADDRESS] ?? null) ?? [], 'deelnemer ' . (1 + $studentIndex) . ' ' , $studentSettings->isAskAddress());
			if ( !$address instanceof Address ) {
				$errors[BookingStudentFieldEnum::ADDRESS] = $address;
			}
		} catch ( InvalidException ) {
			$errors[BookingStudentFieldEnum::ADDRESS] = $t->translate('Adres is niet geldig bij deelnemer ') . ($studentIndex + 1);
		}

		if ( count( $errors ) > 0 || !isset($firstName, $lastName, $email, $address)) {
			return $errors;
		}

		return (new BookingStudent())
			->setGender($gender ?? null)
			->setInitials($initials ?? null)
			->setFirstName($firstName)
			->setLastName($lastName)
			->setNickName($nickName ?? null)
			->setMaidenName($maidenName ?? null)
			->setPrefix($prefix ?? null)
			->setDateOfBirth($dateOfBirth ?? null)
			->setPlaceOfBirth($placeOfBirth ?? null)
			->setCompanyPosition($companyPosition ?? null)
			->setCostCenterCode($costCenterCode ?? null)
			->setEmployeeNumber($employeeNumber ?? null)
			->setInternalReference($internalReference ?? null)
			->setRemark($remark ?? null)
			->setStapRegulation($stapRegulation ?? null)
			->setIsContact($isContact ?? null)
			->setAddress($address)
			->setEmail($email)
			->setPhoneNumber($phoneNumber ?? null)
			->setProcessCode95($code95 ?? null)
			->setProcessSoob($soob ?? null)
			->setExtraFields(Map::fromArray($extraFields));
	}
}