<?php
/**
 * Admin data registry.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Admin;

use ScheduledChanges\Extension\BundledModuleIds;
use ScheduledChanges\Extension\PublicHooks;
use ScheduledChanges\Templates\LaunchTemplatesModule;

/** Prevents omitted modules from loading or localizing their read models. */
final class AdminDataRegistry {
	/** @var string[] */
	private array $keys;
	/** @var array<string,string[]> */
	private array $screen_keys;

	/**
	 * @param string[]               $keys Registered data keys.
	 * @param array<string,string[]> $screen_keys Screen-to-data key map.
	 */
	public function __construct( array $keys, array $screen_keys = array() ) {
		$this->keys        = self::normalizeKeyList( $keys );
		$this->screen_keys = self::normalizeScreenKeys( array() === $screen_keys ? self::defaultScreenKeys() : $screen_keys );
	}

	/**
	 * @param string[] $module_ids Selected module IDs.
	 */
	public static function forModules( array $module_ids ): self {
		$keys = array();
		$map  = array(
			BundledModuleIds::ADMIN_READ_ONLY   => array( 'dashboardSummary', 'launches', 'scheduleDefaults' ),
			BundledModuleIds::ADMIN_SCHEDULE    => array( 'dashboardSummary', 'calendar', 'fieldCatalog', 'launchDetails', 'launches', 'scheduleDefaults', 'supportedAreas', 'targets' ),
			BundledModuleIds::ADMIN_RECORDING   => array( 'recordableTargets', 'recordings', 'scheduleDefaults' ),
			BundledModuleIds::ADMIN_RUN_HISTORY => array( 'notifications', 'runs', 'scheduleDefaults' ),
			BundledModuleIds::ADMIN_MODULES     => array( 'adapters', 'system' ),
			LaunchTemplatesModule::MODULE_ID    => array( 'scheduleDefaults' ),
		);
		foreach ( $map as $module_id => $module_keys ) {
			if ( in_array( $module_id, $module_ids, true ) ) {
				$keys = array_merge( $keys, $module_keys );
			}
		}

		$keys        = PublicHooks::adminDataKeys( $keys, $module_ids );
		$screen_keys = PublicHooks::adminScreenDataKeys( self::defaultScreenKeys(), $module_ids, $keys );

		return new self( $keys, $screen_keys );
	}

	/** Returns whether active modules own one localized data key. */
	public function includes( string $key ): bool {
		return in_array( $key, $this->keys, true );
	}

	/**
	 * Returns selected localized data keys.
	 *
	 * @return string[]
	 */
	public function keys(): array {
		return $this->keys;
	}

	/**
	 * Returns screen-specific localized data key ownership.
	 *
	 * @return array<string,string[]>
	 */
	public function screenKeys(): array {
		return $this->screen_keys;
	}

	/** Returns whether the current screen should receive one localized data key. */
	public function includesForScreen( string $key, string $screen ): bool {
		$screen_keys = $this->screen_keys[ $screen ] ?? array();

		return $this->includes( $key ) && in_array( $key, $screen_keys, true );
	}

	/**
	 * Returns built-in screen data ownership.
	 *
	 * @return array<string,string[]>
	 */
	private static function defaultScreenKeys(): array {
		return array(
			'schedule-center' => array( 'dashboardSummary', 'calendar', 'fieldCatalog', 'launches', 'recordableTargets', 'recordings', 'scheduleDefaults', 'supportedAreas', 'targets' ),
			'calendar'        => array( 'calendar', 'launches', 'scheduleDefaults' ),
			'all-changes'     => array( 'launches', 'scheduleDefaults' ),
			'recordings'      => array( 'recordableTargets', 'recordings', 'scheduleDefaults' ),
			'runs-logs'       => array( 'notifications', 'runs', 'scheduleDefaults' ),
			'modules'         => array( 'adapters', 'system' ),
		);
	}

	/**
	 * @param array<string,mixed> $screen_keys Screen data map.
	 * @return array<string,string[]>
	 */
	private static function normalizeScreenKeys( array $screen_keys ): array {
		$normalized = array();

		foreach ( $screen_keys as $screen => $keys ) {
			if ( ! is_string( $screen ) || ! is_array( $keys ) ) {
				continue;
			}

			$normalized[ $screen ] = self::normalizeKeyList( $keys );
		}

		return $normalized;
	}

	/**
	 * @param mixed[] $keys Data keys.
	 * @return string[]
	 */
	private static function normalizeKeyList( array $keys ): array {
		return array_values(
			array_unique(
				array_filter(
					array_map(
						'strval',
						array_filter(
							$keys,
							static fn ( $key ): bool => is_scalar( $key )
						)
					)
				)
			)
		);
	}
}
