<?php
/**
 * Pluggable registry for webfont icon libraries.
 *
 * @package DiviSupreme
 */

declare(strict_types=1);

namespace DiviSupreme\Extensions\IconLibraries;

use DiviSupreme\Extensions\IconLibraries\Libraries\HeroiconsLibrary;
use DiviSupreme\Extensions\IconLibraries\Libraries\LucideLibrary;
use DiviSupreme\Extensions\IconLibraries\Libraries\PhosphorLibrary;
use DiviSupreme\Extensions\IconLibraries\Libraries\TablerLibrary;

if ( ! defined( 'ABSPATH' ) ) {
	die( 'Direct access forbidden.' );
}

/**
 * Register and resolve icon libraries by id.
 */
final class IconLibraryRegistry {

	/**
	 * @var array<string, IconLibraryInterface>
	 */
	private static array $libraries = array();

	/**
	 * @var bool
	 */
	private static bool $booted = false;

	/**
	 * Boot default libraries once.
	 *
	 * @return void
	 */
	public static function boot(): void {
		if ( self::$booted ) {
			return;
		}

		self::$booted = true;

		self::register( new TablerLibrary() );
		self::register( new PhosphorLibrary() );
		self::register( new LucideLibrary() );
		self::register( new HeroiconsLibrary() );

		/**
		 * Register additional webfont icon libraries.
		 *
		 * @param IconLibraryRegistry $registry Registry instance.
		 */
		do_action( 'dsm_icon_libraries_register', self::class );
	}

	/**
	 * @param IconLibraryInterface $library Library instance.
	 * @return void
	 */
	public static function register( IconLibraryInterface $library ): void {
		self::$libraries[ $library->get_id() ] = $library;
	}

	/**
	 * @param string $library_id Library slug.
	 * @return IconLibraryInterface|null
	 */
	public static function get( string $library_id ): ?IconLibraryInterface {
		self::boot();

		return self::$libraries[ $library_id ] ?? null;
	}

	/**
	 * All registered libraries (enabled flag not applied).
	 *
	 * @return array<string, IconLibraryInterface>
	 */
	public static function all(): array {
		self::boot();

		return self::$libraries;
	}

	/**
	 * Libraries enabled in plugin settings.
	 *
	 * @return array<string, IconLibraryInterface>
	 */
	public static function enabled(): array {
		self::boot();

		$enabled = array();

		foreach ( self::$libraries as $id => $library ) {
			if ( ExtensionSettings::is_library_enabled( $id ) ) {
				$enabled[ $id ] = $library;
			}
		}

		return $enabled;
	}

	/**
	 * Map library metadata for REST / VB bootstrap (manifest omitted — lazy loaded).
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_public_catalog(): array {
		$catalog = array();

		foreach ( self::all() as $library ) {
			$id      = $library->get_id();
			$enabled = ExtensionSettings::is_library_enabled( $id );

			$sample_icon_id = $library->get_sample_icon_id();
			$default_variant = $library->get_default_variant();

			$catalog[] = array(
				'id'             => $id,
				'label'          => $library->get_label(),
				'enabled'        => $enabled,
				'iconCount'      => $library->get_icon_count(),
				'sampleIconId'   => $sample_icon_id,
				'licenseNote'    => $library->get_license_note(),
				'variants'       => $library->get_variants(),
				'defaultVariant' => $default_variant,
				'pickerMode'     => $library->get_picker_render_mode(),
				'frontendMode'   => $library->get_frontend_render_mode(),
				'webfontFamily'  => $library->get_picker_webfont_family( $sample_icon_id, $default_variant ),
				'webfontStylesheets' => self::map_webfont_stylesheets_for_js(
					$library->get_webfont_stylesheets()
				),
			);
		}

		return $catalog;
	}

	/**
	 * Convert manifest entry to Divi iconList row shape.
	 *
	 * @param IconLibraryInterface         $library         Library.
	 * @param array<string, mixed>         $entry           Manifest row.
	 * @return array<string, mixed>
	 */
	public static function to_divi_icon_list_row(
		IconLibraryInterface $library,
		array $entry
	): array {
		$icon_id = sanitize_key( (string) ( $entry['id'] ?? '' ) );
		$variant = (string) ( $entry['variant'] ?? $library->get_default_variant() );

		$row = array(
			'searchTerms'  => (string) ( $entry['searchTerms'] ?? $icon_id ),
			'unicode'      => $icon_id,
			'name'         => (string) ( $entry['name'] ?? $icon_id ),
			'styles'       => array( $library->get_id() ),
			'fontWeight'   => 400,
			'dsmVariant'   => $variant,
			'dsmLibraryId' => $library->get_id(),
		);

		$row['dsmPickerMode']      = $library->get_picker_render_mode();
		$row['dsmPickerFontClass'] = $library->get_picker_font_class( $icon_id, $variant );
		$row['dsmWebfontFamily']   = $library->get_picker_webfont_family( $icon_id, $variant );

		return $row;
	}

	/**
	 * Whether stored icon settings reference an enabled DSM icon library.
	 *
	 * @param array<string, mixed>|null $icon Icon settings.
	 * @return bool
	 */
	public static function is_dsm_library_icon( ?array $icon ): bool {
		if ( null === $icon || ! isset( $icon['type'], $icon['unicode'] ) ) {
			return false;
		}

		$type = sanitize_key( (string) $icon['type'] );

		return null !== self::get( $type ) && ExtensionSettings::is_library_enabled( $type );
	}

	/**
	 * Resolve variant from stored icon settings (Phosphor weights).
	 *
	 * @param array<string, mixed> $icon Icon settings.
	 * @return string
	 */
	public static function resolve_variant_from_icon( array $icon ): string {
		$library_id = sanitize_key( (string) ( $icon['type'] ?? '' ) );
		$variant    = '';

		if ( isset( $icon['variant'] ) && is_string( $icon['variant'] ) && '' !== $icon['variant'] ) {
			$variant = sanitize_key( $icon['variant'] );
		} else {
			$library = self::get( $library_id );
			$variant = $library ? $library->get_default_variant() : '';
		}

		return WebfontAssetLoader::normalize_variant( $library_id, $variant );
	}

	/**
	 * Enqueue webfont CSS for icons used on a page (conditional per variant).
	 *
	 * @param array<string, array<string, mixed>> $icons Unique icon settings.
	 * @return void
	 */
	public static function enqueue_webfonts_for_icons( array $icons ): void {
		WebfontAssetLoader::enqueue_for_icons( $icons );
	}

	/**
	 * VB bootstrap includes the full manifest (limit 0) — same model as Font Awesome in iconList.
	 */
	private const BOOTSTRAP_MANIFEST_LIMIT = 0;

	/**
	 * Build Divi iconList rows for all enabled libraries (VB bootstrap — sync before memoization).
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_divi_icon_list_rows_for_enabled(): array {
		return self::get_bootstrap_manifest_rows();
	}

	/**
	 * Sample manifest rows for VB bootstrap.
	 *
	 * @param int $limit Per-library cap; `0` means all rows.
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_bootstrap_manifest_rows( int $limit = self::BOOTSTRAP_MANIFEST_LIMIT ): array {
		$rows = array();

		foreach ( self::enabled() as $library ) {
			$count = 0;

			foreach ( $library->get_manifest() as $entry ) {
				if ( ! is_array( $entry ) ) {
					continue;
				}

				$rows[] = self::to_divi_icon_list_row( $library, $entry );
				++$count;

				if ( $limit > 0 && $count >= $limit ) {
					break;
				}
			}
		}

		return $rows;
	}

	/**
	 * Stylesheet descriptors for VB picker webfonts (enabled libraries only).
	 *
	 * @return array<int, array{libraryId: string, variant: string, handle: string, url: string, path: string}>
	 */
	public static function get_vb_picker_webfont_stylesheets(): array {
		$stylesheets = array();

		foreach ( self::enabled() as $library_id => $library ) {
			foreach ( $library->get_webfont_stylesheets() as $sheet ) {
				if ( ! is_readable( $sheet['path'] ?? '' ) ) {
					continue;
				}

				$stylesheets[] = array(
					'libraryId' => $library_id,
					'variant'   => (string) ( $sheet['variant'] ?? '' ),
					'handle'    => (string) ( $sheet['handle'] ?? '' ),
					'url'       => (string) ( $sheet['url'] ?? '' ),
					'path'      => (string) ( $sheet['path'] ?? '' ),
				);
			}
		}

		return $stylesheets;
	}

	/**
	 * Strip server paths from webfont descriptors for JS bootstrap.
	 *
	 * @param array<int, array{variant: string, handle: string, path: string, url: string}> $sheets Stylesheets.
	 * @return array<int, array{variant: string, handle: string, url: string}>
	 */
	private static function map_webfont_stylesheets_for_js( array $sheets ): array {
		$mapped = array();

		foreach ( $sheets as $sheet ) {
			$mapped[] = array(
				'variant' => (string) ( $sheet['variant'] ?? '' ),
				'handle'  => (string) ( $sheet['handle'] ?? '' ),
				'url'     => (string) ( $sheet['url'] ?? '' ),
			);
		}

		return $mapped;
	}
}
