<?php

namespace Planaday\Entities\Booking;

use ArrayAccess;
use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Config\Booking\Form\FormSettings;
use Planaday\Config\Booking\Submission\Redirecting\RedirectSettings;
use Planaday\Config\Course\General\GeneralSettings;
use Planaday\Entities\Booking\DTO\Booking;
use Planaday\Entities\Booking\DTO\BookingForm;
use Planaday\Entities\Booking\Enum\BookingStatus;
use Planaday\Entities\Booking\Exception\CourseFullException;
use Planaday\Entities\Booking\Validators\BookingFormValidator;
use Planaday\Entities\Course\CourseController;
use Planaday\Entities\Course\DTO\Course;
use Planaday\Entities\Course\Exception\CourseNotFoundException;
use Planaday\Html\HtmlParserFactory;
use Planaday\Html\Tags\Mail\BookingMail\BookingMail;
use Planaday\Html\Tags\Tag;
use Planaday\Platform\PlatformAbstraction\UrlHandler;
use Planaday\Utility\DataGrouping\Map;
use Planaday\Utility\Debug\Log\Logging;
use Planaday\Utility\GeneralTermEnum;
use Planaday\Utility\PluginData;

class BookingFormPostService {
	use Logging;

	protected ?BookingForm $postEntity = null;
	protected null|InvalidResponseException|CourseNotFoundException|CourseFullException|ArrayAccess $error = null;
	protected ?string $successMessage = null;
	protected ?bool $bookingSuccess = null;
	protected bool $includeOptionsAvailability;
	protected string $prefix;

	public function __construct(
		protected BookingSubmissionHandler $bookingSubmissionHandler,
		protected CourseController $courseController,
		protected RedirectSettings $redirectSettings,
		protected BookingFormValidator $bookingFormValidator,
		protected BookingDatabase $bookingDatabase,
		protected HtmlParserFactory $htmlParserFactory,
		protected FormSettings $formSettings,
		protected UrlHandler $urlHandler,
		GeneralSettings $generalSettings,
		PluginData $pluginData,
	) {
		$this->prefix = $pluginData->getShortPrefix();
		$this->includeOptionsAvailability = $generalSettings->isIncludeOptionInCount();
	}

	/**
	 * @param array<mixed> $postData
	 *
	 * @return void
	 */
	public function checkAndHandlePostData(array $postData):void {
		$bookingFormField = $postData;
		if (empty($bookingFormField)) return;

		$bookingForm = $this->bookingFormValidator->getFromPost(Map::fromArray($bookingFormField));

		if (!$bookingForm instanceof BookingForm) {
			$this->error = Map::fromIterable($bookingForm);
			return;
		}

		$this->postEntity = $bookingForm;

		try {
			$course = $this->courseController->getCourse($bookingForm->getCourseId(), true);
			$booking = $this->bookingSubmissionHandler->checkAndHandleBooking($bookingForm, $this->formSettings->isStartedCourseBookable(), $this->includeOptionsAvailability);
		} catch ( InvalidResponseException|CourseNotFoundException|CourseFullException $e ) {
			$this->error = $e;
			return;
		} catch ( Exception\CourseAlreadyStartedException $e ) {
			$this->error = new CourseFullException($e->getMessage());
			return;
		}

		$bookingUUID = $booking->getDatabaseId();

		$this->bookingSuccess = $booking->getBookingStatus() !== BookingStatus::Failed;

		if (!$this->bookingSuccess) {
			$this->routeUnsuccessfulBooking( $booking,  $course, $bookingUUID);
			return;
		}

		$this->handleSuccessfulBooking($booking, $course, $bookingUUID);
	}

	public function routeUnsuccessfulBooking(Booking $booking, Course $course, string $bookingUUID):void {
		if ( $this->redirectSettings->isRedirectAfterFailure() && $this->redirectSettings->getFailurePage() !== 0) {
			$redirectUrl = $this->urlHandler->getPageUrl($this->redirectSettings->getFailurePage());

			if ($redirectUrl){
				$this->redirect($redirectUrl, $bookingUUID);
				return;
			}
		}

		$this->bookingSuccess = false;
		$parser = $this->htmlParserFactory->getParser();
		$parser->addContext(BookingMail::BOOKING_CONTEXT_INDEX, $booking);
		$parser->addContext(BookingMail::COURSE_CONTEXT_INDEX, $course);
		$parser->addContext(BookingMail::STUDENT_CONTEXT_INDEX, $booking->getBookingForm()->getStudents()[0]);
		$parser->addContext(BookingMail::MAIL_TEMPLATE_CONTEXT_INDEX, $this->redirectSettings->getFailureText());

		$tagString = Tag::getParsableTag(BookingMail::TAG, []);

		$this->successMessage = $parser->parse( $tagString );
	}

	public function handleSuccessfulBooking(Booking $booking, Course $course, string $bookingUUID):void {
		if (
			$booking->getBookingStatus() !== BookingStatus::AlreadyBooked &&
		    ($transaction = $booking->getTransaction()) !== null
		){
			$this->urlHandler->redirect($transaction->getCheckoutUrl());
			return;
		}

		$this->routeSuccessFulBooking($booking, $course, $bookingUUID);
	}

	public function routeSuccessFulBooking(Booking $booking, Course $course, string $bookingUUID):void {
		if ( $this->redirectSettings->isRedirectAfterSuccess() && $this->redirectSettings->getSuccessPage() !== 0 ) {
			$redirectUrl = $this->urlHandler->getPageUrl( $this->redirectSettings->getSuccessPage() );

			if ( $redirectUrl ) {
				$this->redirect( $redirectUrl, $bookingUUID );
				return;
			}
		}

		$this->bookingSuccess = true;
		$parser = $this->htmlParserFactory->getParser();
		$parser->addContext(BookingMail::BOOKING_CONTEXT_INDEX, $booking);
		$parser->addContext(BookingMail::COURSE_CONTEXT_INDEX, $course);
		$parser->addContext(BookingMail::STUDENT_CONTEXT_INDEX, $booking->getBookingForm()->getStudents()[0]);
		$parser->addContext(BookingMail::MAIL_TEMPLATE_CONTEXT_INDEX, $this->redirectSettings->getSuccessText());

		$tagString = Tag::getParsableTag(BookingMail::TAG, []);

		$this->successMessage = $parser->parse( $tagString );
	}

	public function getPostEntity(): ?BookingForm {
		return $this->postEntity;
	}

	public function getError(): null|InvalidResponseException|CourseNotFoundException|CourseFullException|ArrayAccess {
		return $this->error;
	}

	public function getSuccessMessage():?string {
		return $this->successMessage;
	}

	public function getBookingSuccess(): ?bool {
		return $this->bookingSuccess;
	}

	protected function redirect(string $redirectUrl, string $bookingUUID):void {
		(substr_count($redirectUrl, '?') !== 0) ? $redirectUrl .= '&' : $redirectUrl .= '?';
		$redirectUrl .= $this->prefix . GeneralTermEnum::BOOKING_UUID_URL_QUERY . '=' . $bookingUUID;
		$redirectUrl .= "&";
		$redirectUrl .= $this->prefix . GeneralTermEnum::BOOKING_PASSCODE_URL_QUERY . '=' . $this->bookingDatabase->generatePasswordBooking($bookingUUID);

		$this->urlHandler->redirect($redirectUrl);
	}
}