<?php

namespace Planaday\Html\NodeTags\Course;

use DOMNode;
use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Entities\Course\CourseController;
use Planaday\Entities\Course\DTO\Course;
use Planaday\Entities\Course\Exception\CourseNotFoundException;
use Planaday\Html\Nodes\ParserNode;
use Planaday\Html\NodeTags\InnerContentControl;
use Planaday\Html\NodeTags\Tag;
use Planaday\Html\NodeTags\TagCore;
use Planaday\Html\NodeTags\TagNotFullyInitializedException;
use Planaday\Platform\PlatformAbstraction\UrlHandler;
use Planaday\Utility\DependencyInjection\Attributes\Transient;
use Planaday\Utility\GeneralTermEnum;
use Planaday\Utility\PluginData;

#[Transient]
class Injection implements Tag {
	use TagCore;
	use InnerContentControl;

	public function __construct(
		protected CourseController $courseController,
		protected UrlHandler $urlHandler,
		protected PluginData $pluginData,
		protected InjectionSettings $settings,
	) {
		$this->config = [
			"selectedCourseId" => function () { return $this->settings->getSelectedCourseId();},
			"noCourseSelectedText" => function () { return $this->settings->getNoSelectedCourseText();},
			"courseNotFoundText" => function () { return $this->settings->getCourseNotFoundText();},
			"apiErrorText" => function () { return $this->settings->getApiErrorText();},
		];
	}

	public function getNewNode(): DOMNode&ParserNode {
		if ( !isset( $this->originalNode ) ){
			throw new TagNotFullyInitializedException("The OriginalNode is not set");
		}

		$innerContent = $this->getInnerContentFragment($this->originalNode);
		$nodeData = $innerContent->getExtendedNodeData() ?? (clone $this->nodeData);

		$course = $this->getCourse();
		if ( $course instanceof Course ){
			$nodeData->data["course"] = $course;
		} else {
			$nodeData->data["courseFetchError"] = $course;
		}

		$innerContent->setExtendedNodeData($nodeData);
		return $innerContent;
	}

	public function getTagDir(): string {
		return '';
	}

	/**
	 * @return Course|string
	 */
	protected function getCourse(): Course|string {
		$course = $this->attributes["course"] ?? null;
		if ( $course instanceof Course){
			return $course;
		}

		$course = $this->nodeData->data["course"] ?? null;
		if ( $course instanceof Course){
			return $course;
		}

		$courseCouldNotBeFoundText = $this->settings->getCourseNotFoundText();
		$apiErrorText = $this->settings->getApiErrorText();

		$courseIdString = $this->attributes["course-id"] ??
		                  $this->nodeData->data["course-id"] ??
		                  null;

		try {
			if ( $courseIdString !== null ){
				return $this->fetchCourseById($courseIdString);
			}

			$configId = $this->settings->getSelectedCourseId();
			if ( $configId > 0 ){
				return $this->fetchCourseById($configId);
			}

			$courseIdString = $this->urlHandler->getUrlQueryField(
				$this->pluginData->getShortPrefix() . GeneralTermEnum::COURSE_ID_URL_QUERY
			);

			$noCourseSelectedText = $this->settings->getNoSelectedCourseText();

			if ( $courseIdString !== null ){
				return $this->fetchCourseById($courseIdString);
			}

			$this->logWarning("no valid course id found");
			return $noCourseSelectedText;
		} catch (CourseNotFoundException) {
			return $courseCouldNotBeFoundText;
		} catch (InvalidResponseException) {
			return $apiErrorText;
		}
	}

	/**
	 * @param string $courseId
	 *
	 * @return Course|string
	 * @throws CourseNotFoundException
	 * @throws InvalidResponseException
	 */
	protected function fetchCourseById(string $courseId): Course|string {
		$courseId = filter_var($courseId, FILTER_VALIDATE_INT);

		if ( $courseId === false ){
			$this->logWarning("course id is not a valid integer");
			throw new CourseNotFoundException();
		}

		try {
			return $this->courseController->getCourse($courseId, true);
		} catch (CourseNotFoundException $e) {
			$this->logWarning("course not found");
			throw $e;
		} catch ( InvalidResponseException $e ) {
			$this->logWarning("error while fetching course: " . $e->getMessage());
			throw $e;
		}
	}
}