<?php

namespace Planaday\Entities\Course\DTO;

use ArrayAccess;
use Countable;
use DateTime;
use DateTimeZone;
use Planaday\Entities\CostsData;
use Planaday\Entities\DayPart\DTO\DayPart;
use Planaday\Utility\DataGrouping\Collection;
use Traversable;

class Course {
	protected string $code = '';
	protected string $name = '';
	protected string $description = '';
	protected string $type = '';
	protected string $status = '';
	protected int $dayPartAmount = 0;
	protected ?CourseUsersData $usersData = null;
	protected ?CostsData $costsData = null;
	protected ?int $templateId = null;
	protected string $level = '';
	protected string $levelDescription = '';
	protected bool $startGuaranteed = false;
	protected bool $moneyBackGuaranteed = false;
	protected bool $hasELearning = false;
	protected bool $hasCode95 = false;
	protected bool $hasSoob = false;
	protected bool $hasStap = false;
	protected bool $stapOnly = false;
	/** @var string[] */
	protected array $labels = [];
	protected string $language = '';
	/** @var ApiAttribute[] */
	protected array $attributes = [];
	protected DateTime $lastUpdate;

	/** @var null|int[] */
	protected ?array $dayPartIds = null;

	/** @var ArrayAccess<int, DayPart>&Traversable<int, DayPart>&Countable */
	protected ArrayAccess&Traversable&Countable $dayParts;

	/** @var ArrayAccess<int, CourseMaterial>&Traversable<int, CourseMaterial>&Countable */
	protected ArrayAccess&Traversable&Countable $materials;

	/** @var Collection<int>|null */
	protected ?Collection $companies = null;

	protected DayPartDisplayTypes $dayPartDisplayType = DayPartDisplayTypes::AllSelectedImmutable;
	protected bool $priceFromDayParts = false;

	public function __construct(
		protected int $id
	) {
		$this->dayParts = new Collection();
		$this->materials = new Collection();
		$this->lastUpdate = (new DateTime())->setTimezone(new DateTimeZone("Europe/Amsterdam"));
	}

	public function setCode( ?string $code ): self {
		$this->code = $code ?? $this->code;

		return $this;
	}

	public function setName( ?string $name ): self {
		$this->name = $name ?? $this->name;

		return $this;
	}

	public function setDescription( ?string $description ): self {
		$this->description = $description ?? $this->description;

		return $this;
	}

	public function setType( ?string $type ): self {
		$this->type = $type ?? $this->type;

		return $this;
	}

	public function setStatus( ?string $status ): self {
		$this->status = $status ?? $this->status;

		return $this;
	}

	public function setDayPartAmount( ?int $dayPartAmount ): self {
		$this->dayPartAmount = $dayPartAmount ?? $this->dayPartAmount;

		return $this;
	}

	public function setUsersData( ?CourseUsersData $usersData ): self {
		$this->usersData = $usersData;

		return $this;
	}

	public function setCostsData( ?CostsData $costsData ): self {
		$this->costsData = $costsData;

		return $this;
	}

	public function setTemplateId( ?int $templateId ): self {
		$this->templateId = $templateId;

		return $this;
	}

	public function setLevel( ?string $level ): self {
		$this->level = $level ?? $this->level;

		return $this;
	}

	public function setLevelDescription( ?string $levelDescription ): self {
		$this->levelDescription = $levelDescription ?? $this->levelDescription;

		return $this;
	}

	public function setStartGuaranteed( ?bool $startGuaranteed ): self {
		$this->startGuaranteed = $startGuaranteed ?? $this->startGuaranteed;

		return $this;
	}

	public function setMoneyBackGuaranteed( ?bool $moneyBackGuaranteed ): self {
		$this->moneyBackGuaranteed = $moneyBackGuaranteed ?? $this->moneyBackGuaranteed;

		return $this;
	}

	public function setHasELearning( ?bool $hasELearning ): self {
		$this->hasELearning = $hasELearning ?? $this->hasELearning;

		return $this;
	}

	public function setHasCode95( ?bool $hasCode95 ): self {
		$this->hasCode95 = $hasCode95 ?? $this->hasCode95;

		return $this;
	}

	public function setHasSoob( ?bool $hasSoob ): self {
		$this->hasSoob = $hasSoob ?? $this->hasSoob;

		return $this;
	}

	public function setHasStap( ?bool $hasStap ): self {
		$this->hasStap = $hasStap ?? $this->hasStap;

		return $this;
	}

	public function setStapOnly( ?bool $stapOnly ): self {
		$this->stapOnly = $stapOnly ?? $this->stapOnly;

		return $this;
	}

	/**
	 * @param string[] $labels
	 *
	 * @return $this
	 */
	public function setLabels( ?array $labels ): self {
		$this->labels = $labels ?? $this->labels;

		return $this;
	}

	public function setLanguage( ?string $language ): self {
		$this->language = $language ?? $this->language;

		return $this;
	}

	/**
	 * @param ApiAttribute[] $attributes
	 *
	 * @return $this
	 */
	public function setAttributes( ?array $attributes ): self {
		$this->attributes = $attributes ?? [];

		return $this;
	}

	public function setLastUpdate( DateTime $lastUpdate ): self {
		$this->lastUpdate = $lastUpdate;

		return $this;
	}

	/**
	 * @param int[]|null $dayPartIds
	 */
	public function setDayPartIds( ?array $dayPartIds ): self {
		$this->dayPartIds = $dayPartIds;
		return $this;
	}

	/**
	 * @param ArrayAccess<int, DayPart>&Traversable<int, DayPart>&Countable $dayParts
	 *
	 * @return $this
	 */
	public function setDayParts(ArrayAccess&Traversable&Countable $dayParts): self {
		$this->dayParts = $dayParts;
		return $this;
	}

	/**
	 * @param ArrayAccess<int, CourseMaterial>&Traversable<int, CourseMaterial>&Countable $materials
	 *
	 * @return $this
	 */
	public function setMaterials(ArrayAccess&Traversable&Countable $materials): self {
		$this->materials = $materials;
		return $this;
	}

	/**
	 * @param Collection<int> $companies
	 *
	 * @return $this
	 */
	public function setCompanies(Collection $companies): self {
		$this->companies = $companies;
		return $this;
	}

	public function setDayPartDisplayType(DayPartDisplayTypes $dayPartDisplayType): self {
		$this->dayPartDisplayType = $dayPartDisplayType;
		return $this;
	}

	public function setPriceFromDayParts(?bool $priceFromDayParts): self {
		$this->priceFromDayParts = $priceFromDayParts ?? $this->priceFromDayParts;
		return $this;
	}

	public function getId(): int {
		return $this->id;
	}

	public function getCode(): string {
		return $this->code;
	}

	public function getName(): string {
		return $this->name;
	}

	public function getDescription(): string {
		return $this->description;
	}

	public function getType(): string {
		return $this->type;
	}

	public function getStatus(): string {
		return $this->status;
	}

	public function getDayPartAmount(): int {
		return $this->dayPartAmount;
	}

	public function getUsersData(): CourseUsersData {
		if ( $this->usersData === null ) {
			$this->usersData = new CourseUsersData();
		}

		return $this->usersData;
	}

	public function getCostsData(): CostsData {
		if ( $this->costsData === null ) {
			$this->costsData = new CostsData();
		}

		return $this->costsData;
	}

	public function getTemplateId(): ?int {
		return $this->templateId;
	}

	public function getLevel(): string {
		return $this->level;
	}

	public function getLevelDescription(): string {
		return $this->levelDescription;
	}

	public function isStartGuaranteed(): bool {
		return $this->startGuaranteed;
	}

	public function isMoneyBackGuaranteed(): bool {
		return $this->moneyBackGuaranteed;
	}

	public function isHasELearning(): bool {
		return $this->hasELearning;
	}

	public function isHasCode95(): bool {
		return $this->hasCode95;
	}

	public function isHasSoob(): bool {
		return $this->hasSoob;
	}

	public function isHasStap(): bool {
		return $this->hasStap;
	}

	public function isStapOnly(): bool {
		return $this->stapOnly;
	}

	/**
	 * @return string[]
	 */
	public function getLabels(): array {
		return $this->labels;
	}

	public function getLanguage(): string {
		return $this->language;
	}

	/**
	 * @return ApiAttribute[]
	 */
	public function getAttributes():array {
		return $this->attributes;
	}

	public function getLastUpdate():DateTime {
		return $this->lastUpdate;
	}

	/**
	 * @return int[]|null
	 */
	public function getDayPartIds(): ?array {
		return $this->dayPartIds;
	}

	/**
	 * @return ArrayAccess<int, DayPart>&Traversable<int, DayPart>&Countable
	 */
	public function getDayParts(): ArrayAccess&Traversable&Countable {
		return $this->dayParts;
	}

	/**
	 * @return ArrayAccess<int, CourseMaterial>&Traversable<int, CourseMaterial>&Countable
	 */
	public function getMaterials(): ArrayAccess&Traversable&Countable {
		return $this->materials;
	}

	/**
	 * @return Collection<int>
	 */
	public function getCompanies(): Collection {
		if ( $this->companies === null ) {
			$this->companies = new Collection();
		}
		return $this->companies;
	}

	/**
	 * @param bool $hasELearning is the daypart allowed to be e-learning?
	 *
	 * @return DayPart|null
	 */
	public function getEarliestDayPart(bool $hasELearning = false): ?DayPart {
		$earliestDayPart = null;
		foreach ( $this->dayParts as $dayPart ) {
			if ( $dayPart->isELearning() && !$hasELearning ) continue;

			if (
				$earliestDayPart === null ||
				$dayPart->getStartTime() < $earliestDayPart->getStartTime()
			) {
				$earliestDayPart = $dayPart;
			}
		}
		return $earliestDayPart;
	}

	public function hasPastDayParts(bool $eLearningIncluded = true): bool {
		$now = time();
		foreach ( $this->dayParts as $dayPart ) {
			if (
				$dayPart->getStartTime()->getTimestamp() < $now &&
				(!$dayPart->isELearning() || $eLearningIncluded) &&
				$dayPart->getStartTime()->format('Y-m-d') !== date( 'Y-m-d', $now )
			) {
				return true;
			}
		}
		return false;
	}

	public function getDayPartDisplayType(): DayPartDisplayTypes {
		return $this->dayPartDisplayType;
	}

	public function isPriceFromDayParts(): bool {
		return $this->priceFromDayParts;
	}

	public function getAge(): int {
		return time() - $this->lastUpdate->getTimestamp();
	}
}
