<?php
/**
 * KooKoa Folders
 *
 * @package KookoaWPF
 * @license GPL-2.0-or-later
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Registry of available migration importers.
 *
 * Acts as the central directory the Import & Export tab queries to render
 * its "Migrate from other plugins" section. New importers (Real Media
 * Library, HappyFiles, …) are added here and become detectable
 * automatically — no other file in the codebase needs to know about
 * them.
 *
 * Third-party plugins may add their own importer instances via the
 * "kookoawpf_register_importers" action.
 *
 * @since 1.0.5
 */
class KookoaWPF_Importer_Registry {

	/**
	 * Singleton instance.
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Registered importer instances, keyed by ID.
	 *
	 * @var array<string, KookoaWPF_Importer_Abstract>
	 */
	private $importers = array();

	/**
	 * Whether default importers have been registered.
	 *
	 * @var bool
	 */
	private $bootstrapped = false;

	/**
	 * Return the singleton instance.
	 *
	 * @return self
	 */
	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Private constructor — use ::instance().
	 */
	private function __construct() {}

	/**
	 * Lazily register the built-in importers plus anything third-party
	 * code hooked onto kookoawpf_register_importers.
	 */
	private function bootstrap(): void {
		if ( $this->bootstrapped ) {
			return;
		}
		$this->bootstrapped = true;

		// Built-in: FileBird.
		$this->register( new KookoaWPF_Importer_FileBird_Media() );

		$this->register(
			new KookoaWPF_Importer_Taxonomy(
				array(
					'id'               => 'filebird-post',
					'name'             => __( 'FileBird — Posts', 'kookoa-folders' ),
					'author'           => 'Ninja Team',
					'source_taxonomy'  => 'fbv_pt_tax_post',
					'target_post_type' => 'post',
				)
			)
		);

		$this->register(
			new KookoaWPF_Importer_Taxonomy(
				array(
					'id'               => 'filebird-page',
					'name'             => __( 'FileBird — Pages', 'kookoa-folders' ),
					'author'           => 'Ninja Team',
					'source_taxonomy'  => 'fbv_pt_tax_page',
					'target_post_type' => 'page',
				)
			)
		);

		// Built-in: Folders by Premio (taxonomy slugs from
		// Folders::get_custom_post_type() — note that the page-target
		// taxonomy is "folder" alone, not "page_folder").
		$this->register(
			new KookoaWPF_Importer_Taxonomy(
				array(
					'id'               => 'premio-folders-media',
					'name'             => __( 'Folders (Premio) — Media', 'kookoa-folders' ),
					'author'           => 'Premio',
					'source_taxonomy'  => 'media_folder',
					'target_post_type' => 'attachment',
				)
			)
		);

		$this->register(
			new KookoaWPF_Importer_Taxonomy(
				array(
					'id'               => 'premio-folders-post',
					'name'             => __( 'Folders (Premio) — Posts', 'kookoa-folders' ),
					'author'           => 'Premio',
					'source_taxonomy'  => 'post_folder',
					'target_post_type' => 'post',
				)
			)
		);

		$this->register(
			new KookoaWPF_Importer_Taxonomy(
				array(
					'id'               => 'premio-folders-page',
					'name'             => __( 'Folders (Premio) — Pages', 'kookoa-folders' ),
					'author'           => 'Premio',
					'source_taxonomy'  => 'folder',
					'target_post_type' => 'page',
				)
			)
		);

		// Built-in: Real Media Library (devowl.io). Custom-table source,
		// dedicated importer class — see class-kookoawpf-importer-real-media-library.php.
		$this->register( new KookoaWPF_Importer_RealMediaLibrary() );

		/**
		 * Allow third-party plugins (or our future PRO modules) to
		 * register additional migration importers.
		 *
		 * @param KookoaWPF_Importer_Registry $registry The registry instance.
		 */
		do_action( 'kookoawpf_register_importers', $this );
	}

	/**
	 * Register an importer. Last-write-wins on duplicate IDs.
	 *
	 * @param KookoaWPF_Importer_Abstract $importer The importer instance.
	 */
	public function register( KookoaWPF_Importer_Abstract $importer ): void {
		$this->importers[ $importer->get_id() ] = $importer;
	}

	/**
	 * Get all registered importers (regardless of whether they have
	 * data to show).
	 *
	 * @return array<string, KookoaWPF_Importer_Abstract>
	 */
	public function get_all(): array {
		$this->bootstrap();
		return $this->importers;
	}

	/**
	 * Get one importer by ID.
	 *
	 * @param string $id The importer ID.
	 * @return KookoaWPF_Importer_Abstract|null
	 */
	public function get( string $id ): ?KookoaWPF_Importer_Abstract {
		$this->bootstrap();
		return isset( $this->importers[ $id ] ) ? $this->importers[ $id ] : null;
	}

	/**
	 * Get only the importers whose card should currently be visible
	 * (data present, not dismissed, not already imported).
	 *
	 * @return array<string, KookoaWPF_Importer_Abstract>
	 */
	public function get_visible(): array {
		$visible = array();
		foreach ( $this->get_all() as $id => $importer ) {
			if ( $importer->is_visible() ) {
				$visible[ $id ] = $importer;
			}
		}
		return $visible;
	}

	/**
	 * Whether at least one importer for a specific target post type
	 * still has data to import. Used by per-post-type surfaces (e.g.
	 * the Media sidebar hint) so they hide as soon as their own scope
	 * is done, regardless of unrelated PRO-locked sources.
	 *
	 * @param string $post_type Target post type (e.g. "attachment").
	 * @return bool
	 */
	public function has_visible_for_post_type( string $post_type ): bool {
		foreach ( $this->get_visible() as $importer ) {
			if ( $importer->get_target_post_type() === $post_type ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Distinct source author names for the importers still visible for
	 * the given post type. The sidebar hint uses this to render either a
	 * specific brand ("FileBird detected") when there is exactly one
	 * source, or a generic message ("Other folder plugins detected")
	 * when there are two or more.
	 *
	 * @param string $post_type Target post type.
	 * @return array<int, string> Author names in registration order, deduped.
	 */
	public function get_visible_source_brands( string $post_type ): array {
		$seen   = array();
		$brands = array();
		foreach ( $this->get_visible() as $importer ) {
			if ( $importer->get_target_post_type() !== $post_type ) {
				continue;
			}
			// Use the source NAME stripped of its post-type suffix when
			// present (e.g. "Folders (Premio) — Media" → "Folders
			// (Premio)") so multiple per-post-type importers from the
			// same plugin collapse to one brand line.
			$name = preg_replace( '/\s+—\s+(Media|Posts|Pages)$/', '', (string) $importer->get_name() );
			$key  = strtolower( (string) $name );
			if ( isset( $seen[ $key ] ) ) {
				continue;
			}
			$seen[ $key ] = true;
			$brands[]     = (string) $name;
		}
		return $brands;
	}

	/**
	 * Whether any importer has been completed or dismissed — used by the
	 * Settings tab to decide whether to render the "Reset import
	 * detection" link.
	 *
	 * @return bool
	 */
	public function has_hidden_cards(): bool {
		foreach ( $this->get_all() as $importer ) {
			if ( $importer->is_imported() || $importer->is_dismissed() ) {
				if ( $importer->count_folders() > 0 ) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Clear imported/dismissed flags on every registered importer, and
	 * also clear the permanent-hide flag so the section can fully repopulate.
	 */
	public function reset_all(): void {
		foreach ( $this->get_all() as $importer ) {
			$importer->reset();
		}
		delete_option( self::section_hidden_option() );
	}

	/**
	 * Option key for the permanent-hide flag (set-only, irreversible from
	 * the UI — clearing it requires either Reset or DB edit).
	 */
	private static function section_hidden_option(): string {
		return 'kookoawpf_migrate_section_hidden_forever';
	}

	/**
	 * Whether the user has chosen to permanently hide the Migrate
	 * section. Affects rendering only — does not delete any imported
	 * data.
	 */
	public function is_section_hidden_forever(): bool {
		return '1' === (string) get_option( self::section_hidden_option(), '' );
	}

	/**
	 * Set the permanent-hide flag.
	 */
	public function hide_section_forever(): void {
		update_option( self::section_hidden_option(), '1', false );
	}

	/**
	 * Decide whether the Migrate from Other Plugins section should
	 * render at all.
	 *
	 * Logic:
	 * - If there is an unimported / undismissed source with data → render.
	 *   (This intentionally overrides the permanent-hide flag, so a brand-
	 *   new competing plugin installed after the user opted out still
	 *   surfaces its migration card.)
	 * - Else if the permanent-hide flag is set → hide.
	 * - Else if anything has been imported or dismissed → render (so the
	 *   user can find Reset / Hide Forever).
	 * - Otherwise → hide.
	 */
	public function should_show_section(): bool {
		if ( ! empty( $this->get_visible() ) ) {
			return true;
		}
		if ( $this->is_section_hidden_forever() ) {
			return false;
		}
		return $this->has_hidden_cards();
	}
}
