<?php

namespace Planaday\Entities\Booking;

use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Config\Course\General\GeneralSettings;
use Planaday\Config\Payment\PaymentSettings;
use Planaday\Entities\Booking\DTO\Booking;
use Planaday\Entities\Booking\DTO\BookingForm;
use Planaday\Entities\Booking\Enum\BookingStatus;
use Planaday\Entities\Booking\Exception\CourseAlreadyStartedException;
use Planaday\Entities\Booking\Exception\CourseFullException;
use Planaday\Entities\Course\CourseController;
use Planaday\Entities\Course\CourseUtility;
use Planaday\Entities\Course\DTO\Course;
use Planaday\Entities\Course\Exception\CourseNotFoundException;
use Planaday\Payment\Interfaces\PaymentHandler;
use Planaday\Payment\Interfaces\TransactionCreationFailedException;
use Planaday\Utility\Debug\Log\Logging;
use Planaday\Utility\GeneralTermEnum;
use Planaday\Utility\PluginData;

class BookingSubmissionHandler {
	use Logging;

	protected string $prefix;

	public function __construct(
		protected BookingController $bookingController,
		protected BookingDatabase $bookingDatabase,
		protected CourseController $courseController,
		protected BookingEmailService $emailService,
		protected PaymentSettings $paymentSettings,
		protected PaymentHandler $paymentHandler,
		protected CourseUtility $courseUtility,
		PluginData $pluginData,
		protected BookingPriceService $bookingPriceService,
		protected GeneralSettings $generalSettings
	) {
		$this->prefix = $pluginData->getShortPrefix();
	}

	/**
	 * @param BookingForm $form
	 * @param bool $allowBookingOnPast
	 * @param bool $includeOptionsInCount
	 *
	 * @return Booking
	 * @throws CourseFullException
	 * @throws CourseNotFoundException
	 * @throws InvalidResponseException
	 * @throws CourseAlreadyStartedException
	 */
	public function checkAndHandleBooking(BookingForm $form, bool $allowBookingOnPast, bool $includeOptionsInCount):Booking {
		$alreadyCompletedBooking = $this->bookingDatabase->getAlreadyCompletedBooking($form);
		if ($alreadyCompletedBooking !== null) {
			$alreadyCompletedBooking->setBookingStatus(BookingStatus::AlreadyBooked);
			return $alreadyCompletedBooking;
		}

		$course = $this->courseController->getCourse($form->getCourseId(), true);

		if(!$allowBookingOnPast && $course->hasPastDayParts($this->generalSettings->isELearningCountsAsStart())){
			throw new CourseAlreadyStartedException();
		}

		if ($course->getUsersData()->getAvailableSpots($includeOptionsInCount) < count($form->getStudents())) {
			throw new CourseFullException();
		}

		try {
			$bookingResponse = $this->bookingController->doBooking($form);
			$status = $bookingResponse->getResult() == 'OK' ? BookingStatus::Pending : BookingStatus::Failed;
			$bookingId = $bookingResponse->getBookingId();
		} catch ( InvalidResponseException $e ) {
			$this->logError("Do booking failed: " . $e->getMessage(), $e);
			$status = BookingStatus::Failed;
		}

		$booking = (new Booking())
			->setBookingId($bookingId ?? null)
			->setBookingForm($form)
			->setBookingStatus($status);
		$this->bookingDatabase->setBooking($booking);

		if ($booking->getBookingStatus() === BookingStatus::Failed) {
			$this->emailService->sendEmails( $booking, $course );
			return $booking;
		}

		$totalCost = 0;
		if ($this->paymentSettings->isPaymentPossible()) {
			$totalCost = $this->bookingPriceService->calculatePriceBooking( $booking, $course );
		}

		if(
			$totalCost > 0 &&
			!(
				$booking->getBookingForm()->getCompany() !== null &&
				$this->paymentSettings->isAllowCompanyInvoice() &&
				$booking->getBookingForm()->getPayWithInvoice()
			)
		){
			$this->insertTransaction($booking, $totalCost, $course);
			$this->bookingDatabase->setBooking($booking);
			return $booking;
		}

		$booking->setBookingStatus(BookingStatus::Successful);
		$this->bookingDatabase->setBooking($booking);
		$this->emailService->sendEmails( $booking, $course );

		return $booking;
	}

	protected function insertTransaction(Booking $booking, float $totalCost, Course $course): void {
		$booking->setBookingStatus(BookingStatus::PaymentRequired);

		$redirectUrl = $this->courseUtility->getDetailLink($course) . '?'
		               . $this->prefix . GeneralTermEnum::BOOKING_UUID_URL_QUERY . '=' . $booking->getDatabaseId() . '&' .
		               $this->prefix . GeneralTermEnum::BOOKING_PASSCODE_URL_QUERY . '=' . $this->bookingDatabase->generatePasswordBooking($booking->getDatabaseId()) .
		               '#pad-main-booking-form';
		try {
			$transaction = $this->paymentHandler->getNewTransaction(
				$totalCost,
				$booking,
				$redirectUrl
			);

			$booking->setTransaction($transaction);
			$booking->setBookingStatus(BookingStatus::PaymentRequired);
		} catch ( TransactionCreationFailedException $e ) {
			$this->logError("Transaction creation failed: " . $e->getMessage(), $e);
			$booking->setBookingStatus(BookingStatus::Failed);
		}
	}
}