<?php

namespace Planaday\Entities\Location;

use ArrayAccess;
use Countable;
use Planaday\Api\ApiException\InvalidResponseException;
use Planaday\Config\General\Api\ApiSettings;
use Planaday\Entities\Location\DTO\Location;
use Planaday\Entities\Location\Exception\LocationNotFoundException;
use Planaday\Utility\DataGrouping\Collection;
use Planaday\Utility\DateAndTimeUtility;
use Traversable;

class LocationController {
	public function __construct(
		protected LocationApi $api,
		protected LocationDatabase $database,
		protected ApiSettings $apiSettings
	) {}

	/**
	 * @param ArrayAccess<int|string, string>&Traversable<int|string, string>&Countable $locationIds
	 * @param bool $forceCall
	 *
	 * @return ArrayAccess<int|string, Location>&Traversable<int|string, Location>&Countable
	 * @throws InvalidResponseException
	 * @throws LocationNotFoundException
	 */
	public function getLocations(ArrayAccess&Traversable&Countable $locationIds, bool $forceCall = false): ArrayAccess&Traversable&Countable {
		if (!$forceCall){
			$locations = [];
			$maxAllowedAge = $this->apiSettings->getRefreshIntervalLocations() * DateAndTimeUtility::SECONDS_IN_HOUR;

			foreach ($locationIds as $locationId) {
				$location = $this->database->getLocationById($locationId);

				if (!isset($location) || $location->getAge() > $maxAllowedAge){
					break;
				}

				$locations[] = $location;
			}

			if (count($locations) === count($locationIds)){
				return Collection::fromArray($locations);
			}
		}

		$apiLocations = $this->api->getLocations($locationIds);
		foreach ($apiLocations as $apiLocation) {
			$this->database->setLocation($apiLocation);
		}
		return $apiLocations;
	}

	/**
	 * @param string $locationId
	 * @param bool $forceCall
	 *
	 * @return Location
	 * @throws LocationNotFoundException
	 * @throws InvalidResponseException
	 */
	public function getLocation(string $locationId, bool $forceCall = false): Location {
		if (!$forceCall){
			$locationDatabase = $this->database->getLocationById($locationId);
			$maxAllowedAge = $this->apiSettings->getRefreshIntervalLocations() * DateAndTimeUtility::SECONDS_IN_HOUR;

			if (isset($locationDatabase) && $locationDatabase->getAge() < $maxAllowedAge){
				return $locationDatabase;
			}
		}

		$apiLocation = $this->api->getLocation($locationId);

		$this->database->setLocation($apiLocation);

		return $apiLocation;
	}

	/**
	 * @return Collection<string>
	 */
	public function getAllCities(): Collection {
		return $this->database->getAllCities();
	}

	/**
	 * @return ArrayAccess<int|string, Location>&Traversable<int|string, Location>&Countable
	 */
	public function getAllDB(): ArrayAccess&Traversable&Countable {
		return $this->database->getAll();
	}
}