<?php

namespace Planaday\Api\V1;

use ArrayAccess;
use Countable;
use Planaday\Api\ApiException\ApiErrorResponseException;
use Planaday\Api\ApiException\ClientNotInitializedException;
use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Entities\Booking\BookingApi;
use Planaday\Entities\Booking\DTO\BookingForm;
use Planaday\Entities\Booking\DTO\BookingResponse;
use Planaday\Entities\Booking\DTO\ExtraField;
use Planaday\Platform\PlatformAbstraction\DataHandler;
use Planaday\Utility\DataFilters\InvalidException;
use Planaday\Utility\DataFilters\TypeValidator;
use Planaday\Utility\DataGrouping\Map;
use Planaday\Utility\DateAndTimeUtility;
use Planaday\Utility\Debug\Log\Logging;
use Traversable;

class BookingApiV1Controller implements BookingApi {
	use Logging;

	public function __construct(
		protected ClientV1 $client,
		protected DateAndTimeUtility $dateAndTimeUtility,
		protected DataHandler $dataHandler,
		protected TypeValidator $typeValidator
	) {}

	/**
	 * @param string $bookingId
	 * @param string $transactionId
	 *
	 * @return bool if the payment data was received
	 */
	public function postPaymentDataBooking(string $bookingId, string $transactionId): bool {
		try {
			$result = $this->client->callEndpoint(
				'booking/payed/' . $bookingId,
				[],
				'POST',
				[
					'is_payed' => true,
					'payment'  => [
						'party'          => 'Mollie',
						'transaction_id' => $transactionId
					]
				]
			);
		} catch ( ApiErrorResponseException|ClientNotInitializedException ) {}

		return isset($result) && $result->getStatus() === 200;
	}

	/**
	 * @param string $bookingId
	 * @param string $transactionId
	 */
	public function deleteBookingAfterNoPayment(string $bookingId, string $transactionId): void{
		try{
			$this->client->callEndpoint(
				'booking/' . $bookingId,
				[],
				'DELETE',
				[
					'is_payed' => false,
					'payment' => [
						'party' => 'Mollie',
						'transaction_id' => $transactionId
					]
				]
			);
		} catch ( ApiErrorResponseException|ClientNotInitializedException ) {
		}
	}

	/**
	 * @return Countable&Traversable&ArrayAccess
	 * @throws InvalidResponseException
	 */
	public function getExtraFieldsList(): ArrayAccess&Traversable&Countable {
		try {
			$response = $this->client->callEndpoint('extrafields/list');
		} catch (ApiErrorResponseException|ClientNotInitializedException $e){
			throw new InvalidResponseException($e->getMessage(), $e->getCode(), $e);
		}

		$typeIs = $this->typeValidator;

		try{
			$extraFieldList = [];

			$extraFields = $typeIs->array(
				$typeIs->array(
					$typeIs->array(
						$response->getData()
					)['extrafields'] ?? []
				)['student'] ?? []
			);

			foreach ($extraFields as $extraField){
				$extraFieldList[] = (new ExtraField($typeIs->int($extraField['id'] ?? null) ?? throw new InvalidException("id is not set")))
					->setName($typeIs->str($extraField['name'] ?? ''))
					->setType($typeIs->str($extraField['type'] ?? ''))
					->setIsRequired($typeIs->bool($extraField['is_required'] ?? false))
					->setDescription($typeIs->str($extraField['description'] ?? ''))
					->setOptions($typeIs->array($extraField['options'] ?? []))
					->setDefaultValue($extraField['default_value'] ?? null);
			}

			return Map::fromArray($extraFieldList);
		} catch (InvalidException){
			$this->logError("could not parse response from server to extrafields", $response);
			throw new InvalidResponseException();
		}
	}

	/**
	 * @param BookingForm $bookingForm
	 *
	 * @return BookingResponse
	 * @throws InvalidResponseException
	 */
	public function doBooking( BookingForm $bookingForm ): BookingResponse {
		try {
			$response = $this->client->callEndpoint(
				'booking/' . $bookingForm->getCourseId(),
				[],
				'POST',
				$this->bookingObjectToArray($bookingForm)
			);

			$this->logInfo("Booking Request send successfully, response: (data)", $response);
		} catch ( ApiErrorResponseException|ClientNotInitializedException $e ) {
			throw new InvalidResponseException("something went wrong and the request could not be handled", 0, $e);
		}

		$response = $response->getData();

		if (!isset($response['result'])) {
			$this->logError("The request was not handled as expected, the following was returned:", $response);
			throw new InvalidResponseException("The request was not handled as expected");
		}

		try{
			return (new BookingResponse())
				->setResult($this->typeValidator->str($response['result']))
				->setBookingId($this->typeValidator->str($response['booking_id']));
		} catch (InvalidException){
			$this->logError("could not parse response from server to booking", $response);
			throw new InvalidResponseException();
		}
	}

	private function bookingObjectToArray(BookingForm $bookingForm):array {
		$booking = [
			'notes' => "Boeking via wordpress-website",
			'created_at' => $this->dateAndTimeUtility->currentDate(),
			'course_id' => $bookingForm->getCourseId(),
			'company_id' => $bookingForm->getCompanyId(),
			'dayparts' => [...$bookingForm->getDayParts()],
		];

		$students = [];
		foreach ($bookingForm->getStudents() as $index => $student){
			$students[$index] = [
				"gender" => $student->getGender(),
				"first_name" => $student->getFirstName(),
				"last_name" => $student->getLastName(),
				"maiden_name" => $student->getMaidenName(),
				"internal_reference" => $student->getInternalReference(),
				"nick_name" => $student->getNickName(),
				"initials" => $student->getInitials(),
				"prefix" => $student->getPrefix(),
				"email" => $student->getEmail(),
				"address" => $student->getAddress()->getStreet(),
				"house_number" => $student->getAddress()->getHouseNumber(),
				"house_number_extension" => $student->getAddress()->getHouseNumberExtension(),
				"postal_code" => $student->getAddress()->getPostalCode(),
				"city" => $student->getAddress()->getCity(),
				"phonenumber" => $student->getPhoneNumber(),
				"date_of_birth" => $student->getDateOfBirth()?->format( "Y-m-d" ),
				"hometown" => $student->getPlaceOfBirth(),
				"employeeId" => $student->getEmployeeNumber(),
				"company_position" => $student->getCompanyPosition(),
				"remark" => $student->getRemark(),
				"is_contact_person" => $student->isContact(),
                "is_student" => true,
				"costcentercode" => $student->getCostCenterCode(),
				"process_code95" => $student->isProcessCode95(),
				"process_soob" => $student->isProcessSoob(),
				"stap_regulation" => $student->isStapRegulation(),
			];

			foreach ($student->getExtraFields() as $field => $values){
				$students[$index]["extrafields"] = $students[$index]["extrafields"] ?? [];
				$students[$index]["extrafields"][$field] = is_array($values) ? implode('|', $values) : $values;
			}
		}

		$company = $bookingForm->getCompany();
		if ($company && empty( $bookingForm->getCompanyId() )) {
			$booking['company'] = [
				"name"                   => $company->getName(),
				"email"                  => $company->getEmail(),
				"address"                => $company->getAddress()->getStreet(),
				"house_number"           => $company->getAddress()->getHouseNumber(),
				"postal_code"            => $company->getAddress()->getPostalCode(),
				"house_number_extension" => $company->getAddress()->getHouseNumberExtension(),
				"city"                   => $company->getAddress()->getCity(),
				"phonenumber"            => $company->getPhoneNumber()
			];

			$invoice = $company->getInvoice();
			if ( $invoice ) {
				$booking['company']['invoice_email'] = $invoice->getEmail();
				if ($invoice->getAddress() !== null)
					$booking['company']['invoice']       = [
						"addressee"              => $invoice->getAddress()->getName(),
						"address"                => $invoice->getAddress()->getStreet(),
						"house_number"           => $invoice->getAddress()->getHouseNumber(),
						"postal_code"            => $invoice->getAddress()->getPostalCode(),
						"house_number_extension" => $invoice->getAddress()->getHouseNumberExtension(),
						"city"                   => $invoice->getAddress()->getCity(),
						"country"                => $invoice->getAddress()->getCountry()
					];
			}

			if ( $contactPerson = $company->getContactPerson() ) {
				$address = $contactPerson->getAddress();
				$students[] = [
					"first_name" => $contactPerson->getFirstName(),
					"last_name" => $contactPerson->getLastName(),
					"prefix" => $contactPerson->getPrefix(),
					"email" => $contactPerson->getEmail(),
					"phonenumber" => $contactPerson->getPhoneNumber(),
					"is_contact_person" => true,
					"is_student" => false,
					...($address ? [
						"address" => $address->getStreet(),
		                "house_number" => $address->getHouseNumber(),
		                "house_number_extension" => $address->getHouseNumberExtension(),
		                "postal_code" => $address->getPostalCode(),
		                "city" => $address->getCity()
					] : [])
				];
			}
		}

		$booking['students'] = $students;

		$materials = $bookingForm->getMaterials();
		if (count($materials) > 0){
			$booking['materials'] = [...$materials];
		}

		$attributes = $bookingForm->getAttributes();
		if (count($attributes) > 0){
			$booking['attributes'] = [];
			foreach ($attributes as $key => $values){
				$booking['attributes'][] = [
					"code" => $key,
					"values" => is_array($values) ? $values : [$values]
				];
			}
		}
		$booking['creating_source'] = $this->dataHandler->getServer();

		return $booking;
	}
}