<?php

namespace Planaday\Api\Mock;

use ArrayAccess;
use Countable;
use Planaday\Entities\CostsData;
use Planaday\Entities\Course\CourseApi;
use Planaday\Entities\Course\DTO\ApiAttribute;
use Planaday\Entities\Course\DTO\ApiAttributeValue;
use Planaday\Entities\Course\DTO\Course;
use Planaday\Entities\Course\DTO\CourseMaterial;
use Planaday\Entities\Course\DTO\CourseTemplate;
use Planaday\Entities\Course\DTO\CourseUsersData;
use Planaday\Entities\Course\DTO\DayPartDisplayTypes;
use Planaday\Entities\Course\Exception\CourseNotFoundException;
use Planaday\Utility\DataGrouping\Collection;
use Planaday\Utility\Debug\Log\Logging;
use Traversable;

class MockCourseApi implements CourseApi {
	use Logging;
	public CourseApi|null $courseApiMock = null;

	/** @var Collection<Course> */
	protected Collection $courses;

	/** @var Collection<CourseMaterial> */
	protected Collection $courseMaterials;

	/** @var Collection<CourseTemplate> */
	protected Collection $courseTemplates;

	public function __construct() {
		$this->logInfo("MockCourseApi constructed");
		$this->courses = new Collection();
		$courses = range(1, 10);
		foreach ($courses as $courseId) {
			$this->courses->append($this->getTestCourse($courseId));
		}
		$this->logInfo("Courses added, count: " . count($this->courses));

		$this->courseMaterials = new Collection();
		$courseMaterials = range(1, 10);
		foreach ($courseMaterials as $matId) {
			$this->courseMaterials->append($this->getTestMaterial($matId));
		}
		$this->logInfo("Course materials added, count: " . count($this->courseMaterials));

		$this->courseTemplates = new Collection();
		$courseTemplates = range(1, 4);
		foreach ($courseTemplates as $courseTemplateId) {
			$this->courseTemplates->append($this->getTestCourseTemplate($courseTemplateId));
		}
		$this->logInfo("Course templates added, count: " . count($this->courseTemplates));
	}

	/**
	 * @inheritDoc
	 */
	public function getCourse( int $courseId ): Course {
		if ( $this->courseApiMock !== null ) return $this->courseApiMock->getCourse( $courseId );

		$this->logInfo("MockCourseApi getting course $courseId");
		if ( isset( $this->courses[ $courseId - 1 ] ) ) {
			return $this->courses[ $courseId - 1 ];
		}

		throw new CourseNotFoundException( "Course with ID $courseId not found" );
	}

	/**
	 * @inheritDoc
	 */
	public function getMaterialsCourse( int $courseId, int $offset = 0, int $limit = 100 ): ArrayAccess&Traversable&Countable {
		if ( $this->courseApiMock !== null ) return $this->courseApiMock->getMaterialsCourse( $courseId, $offset, $limit );

		$this->logInfo("MockCourseApi getting materials for course $courseId");
		$items = new Collection();
		foreach ( $this->courseMaterials as $courseMaterial ) {
			if ( $courseMaterial->getId() === $courseId ) {
				$items[] = $courseMaterial;
			}
		}

		return $items;
	}

	/**
	 * @inheritDoc
	 */
	public function getCoursesTimeSlot( int $startDate, int $endDate, int $offset = 0, int $limit = 100 ): ArrayAccess&Traversable&Countable {
		if ( $this->courseApiMock !== null ) return $this->courseApiMock->getCoursesTimeSlot( $startDate, $endDate, $offset, $limit );

		$this->logInfo("MockCourseApi getting courses for time slot $startDate - $endDate");
		return $this->courses;
	}

	/**
	 * @inheritDoc
	 */
	public function getCourseTemplateList(): ArrayAccess&Traversable&Countable {
		if ( $this->courseApiMock !== null ) return $this->courseApiMock->getCourseTemplateList();

		$this->logInfo("MockCourseApi getting course template list");
		return $this->courseTemplates;
	}

	public function getTestCourse(int $courseId): Course {
		$course = (new Course($courseId))
			->setName("Test cursus $courseId")
			->setCode("Test$courseId")
			->setDescription("Dit is een test cursus (id: $courseId), deze wordt weergeven omdat er nog geen koppeling is gemaakt met planaday.")
			->setStatus("open")
			->setType("open")
			->setDayPartAmount($courseId === 1 ? 2 : 3)
			->setUsersData(
				(new CourseUsersData())
					->setMax(10)
					->setAvailable(10)
			)
			->setLabels(["test label", "test label 2"])
			->setTemplateId(($courseId % 3) + 1)
			->setHasCode95($courseId % 2 == 1)
			->setHasSoob($courseId % 2 == 1)
			->setHasELearning($courseId % 2 == 1)
			->setStartGuaranteed($courseId % 4 == 1)
			->setMoneybackGuaranteed($courseId % 3 == 1)
			->setDayPartIds(
				range(
					(($courseId-1) * 3 == 0 ? 1 : ($courseId-1) * 3),
					($courseId-1) * 3 + 2)
			);

		$course->setAttributes([
			(new ApiAttribute())
				->setName("test attribute")
				->setCode("test")
				->setIsRequired(false)
				->setForBooking(true)
				->setMultipleValuesAllowed(true)
				->setDescription("test description")
				->setValues([
					(new ApiAttributeValue())
						->setName("test name")
						->setValue("test value")
						->setDescription("test description"),
					(new ApiAttributeValue())
						->setName("test name 2")
						->setValue("test value 2")
						->setDescription("test description 2")
				])
		]);

		$course->getCostsData()
		       ->setRemark("test financiële toelichting")
		       ->setCost(1.23 * ($courseId - 1) * ($courseId - 2));

		switch ($courseId % 4) {
			case 0:
				$course->setDayPartDisplayType(DayPartDisplayTypes::AllSelectedImmutable);
				break;
			case 1:
				$course->setDayPartDisplayType(DayPartDisplayTypes::NoneSelected);
				break;
			case 2:
				$course->setDayPartDisplayType(DayPartDisplayTypes::AllSelected);
				break;
			case 3:
				$course->setDayPartDisplayType(DayPartDisplayTypes::MaxOneSelected);
				break;
		}

		switch ($courseId % 3) {
			case 0:
				$course->setPriceFromDayParts(false);
				break;
			case 1:
			case 2:
				$course->setPriceFromDayParts(true);
				break;
		}

		if ($courseId % 2 == 1) {
			$course->setCompanies(new Collection(1, 2, 3));
		}

		return $course;
	}

	public function getTestMaterial(int $matId): CourseMaterial {
		return (new CourseMaterial($matId))
			->setCode("TestMat$matId")
			->setName("Test materiaal $matId")
			->setCostsData(
				(new CostsData())
					->setCost(1.23 * $matId)
					->setVat(21)
			)
			->setAmount($amount = $matId % 2 == 1 ? 2 : 0)
			->setInvoicedPerStudent( $invoicedPerStudent = $matId % 3 == 1 )
			->setInvoicedPerCompany( $invoicedPerCompany = $matId % 3 == 2 )
			->setDescription(
				"Dit is een test materiaal" .
				" (id: $matId, amount: $amount, invoiced per student: $invoicedPerStudent, invoiced per company: $invoicedPerCompany)," .
				" deze wordt weergeven omdat er nog geen koppeling is gemaakt met planaday."
			);
	}

	public function getTestCourseTemplate(int $courseTemplateId): CourseTemplate {
		return (new CourseTemplate($courseTemplateId))
			->setCode("TestCursusTemplate$courseTemplateId")
			->setName("Test cursus template $courseTemplateId")
			->setDescription("Dit is een test cursus template (id: $courseTemplateId), deze wordt weergeven omdat er nog geen koppeling is gemaakt met planaday.")
			->setDayPartAmount(3)
			->setLabels(["test label", "test label 2"])
			->setLanguage("NL");
	}
}