<?php

namespace Planaday\Config;

use Planaday\Platform\PlatformAbstraction\DataHandler;
use Planaday\Utility\DataGrouping\Collection;
use Planaday\Utility\DataGrouping\Map;
use Planaday\Utility\Debug\Log\Logging;
use Planaday\Utility\DependencyInjection\Attributes\Scoped;

#[Scoped]
class SettingsLoader {
	use Logging;

	/** @var Map<OptionGroupEnum, mixed>|null  */
	protected ?Map $settingsOverride = null;

	/** @var Collection<class-string<OptionGroupEnum>> */
	protected Collection $loadedSettings;

	public function __construct(
		protected DataHandler $dataHandler
	) {
		$this->loadedSettings = new Collection();
	}

	/**
	 * @param Map<OptionGroupEnum, mixed> $settingsOverride
	 *
	 * @return void
	 */
	public function setSettingsOverride(Map $settingsOverride): void {
		$this->logDebug("Setting settings override, total override count = " . count($settingsOverride));
		$this->settingsOverride = $settingsOverride;
	}

	/**
	 * @param EnumMappedSettings $settings
	 * @param class-string<OptionGroupEnum> $enum
	 *
	 * @return void
	 */
	public function overrideSettings(EnumMappedSettings $settings, string $enum): void {
		if ( $this->settingsOverride === null) {
			$this->logDebug("No settings override set, skipping override");
			return;
		}
		$mappingLayer = $settings->getEnumMapping();
		foreach ($mappingLayer["values"] as $keyName => &$value) {
			$key = $enum::tryFrom($keyName);
			if ($key === null) {
				$this->logDebug("Key is null, skipping override");
				continue;
			}
			if ($this->settingsOverride->offsetExists( $key )) {
				$this->logDebug("Overriding setting: " . $key->value);
				$value = $this->settingsOverride->offsetGet( $key );
			}
		}
	}

	public function getSettingsOverride(): ?Map {
		return $this->settingsOverride;
	}

	/**
	 * @return Collection<class-string<OptionGroupEnum>>
	 */
	public function getLoadedSettings(): Collection {
		return $this->loadedSettings;
	}

	/**
	 * @param EnumMappedSettings $settings
	 * @param class-string<OptionGroupEnum> $baseEnum
	 *
	 * @return void
	 */
	public function reloadData(EnumMappedSettings $settings, string $baseEnum): void{
		$this->logDebug("Reloading data for: " . get_class($settings));
		$indexEnums = [];
		$indexEnumValues = [];

		$enum = $baseEnum;

		while ($enum::getParentEnum() !== null) {
			$parentEnum = $enum::getParentEnum();
			array_unshift($indexEnums, $parentEnum::class);
			array_unshift($indexEnumValues, $parentEnum->value);
			$enum = $parentEnum;
		}

		$parentSettingClass = $enum::getClassName();
		$enum = is_string($enum) ? $enum : $enum::class;
		if ( !$this->loadedSettings->contains($enum) )
			$this->loadedSettings->append($enum);


		if (!is_a($parentSettingClass, GlobalSetting::class, true)) {
			$this->overrideSettings($settings, $baseEnum);
			return;
		}

		$databaseData = $this->dataHandler->getOption($parentSettingClass::getIndex());
		if (!is_array($databaseData)) {
			$databaseData = [];
		}

		foreach ( $indexEnums as $index => $enumClass ) {
			$databaseData = $databaseData[$enumClass] ?? $databaseData[$indexEnumValues[$index]] ?? [];
		}

		$mappingLayer = $settings->getEnumMapping();
		$this->setDatabaseData($databaseData, $mappingLayer);
		$this->overrideSettings($settings, $baseEnum);
	}


	public function saveData(EnumMappedSettings&GlobalSetting $objectToSave): void{
		$mappingLayer = $objectToSave->getEnumMapping();
		$databaseData = $this->getDatabaseDataLayer($mappingLayer);
		$this->dataHandler->setOption($objectToSave::getIndex(), $databaseData);
	}

	/**
	 * @param mixed[] $databaseData
	 * @param array{values:mixed[], subGroups:array[], subGroupAliases:string[]} $mappingLayer
	 *
	 * @return void
	 */
	protected function setDatabaseData(array $databaseData, array &$mappingLayer): void {
		foreach ($mappingLayer["values"] as $key => &$value) {
			if (isset($databaseData[$key])) {
				$value = $databaseData[$key];
			}
		}

		foreach ($mappingLayer["subGroupAliases"] as $alias => $destinationKey) {
			if (!isset($databaseData[$destinationKey]) && isset($databaseData[$alias])) {
				$databaseData[$destinationKey] = $databaseData[$alias];
			}
		}

		foreach ($mappingLayer["subGroups"] as $key => &$subGroup) {
			if (isset($databaseData[$key])) {
				$this->setDatabaseData($databaseData[$key], $subGroup);
			}
		}
	}

	/**
	 * @param array{values:mixed[], subGroups:array[], subGroupAliases:string[]} $layer
	 *
	 * @return array
	 */
	protected function getDatabaseDataLayer(array $layer): array {
		$databaseData = array_map( function ( $value ) {
			return $value;
		}, $layer["values"] );

		foreach ($layer["subGroups"] as $key => $subGroup) {
			$databaseData[$key] = $this->getDatabaseDataLayer($subGroup);
		}

		return $databaseData;
	}
}