<?php
/**
 * ThemeRegistry
 *
 * Central registry for all theme renderers.
 * Premium themes register themselves only when a valid license exists —
 * that is the freemium gating point.
 *
 * @package Relatedly\Theming
 * @since   2.0.0
 */

namespace Relatedly\Theming;

use Relatedly\Core\Options;
use Relatedly\Labels\LabelProvider;
use Relatedly\Premium\FeatureGate;
use Relatedly\Theming\Inline\Theme1Simple;
use Relatedly\Theming\Inline\Theme2Minimal;
use Relatedly\Theming\Inline\Theme3Featured;
use Relatedly\Theming\Inline\Theme4Quote;
use Relatedly\Theming\Inline\Theme5Badge;
use Relatedly\Theming\Inline\Theme6Callout;
use Relatedly\Theming\Inline\Theme7Gradient;
use Relatedly\Theming\Inline\Theme8Card;
use Relatedly\Theming\EndContent\Theme1List;
use Relatedly\Theming\EndContent\Theme2SimpleList;
use Relatedly\Theming\EndContent\Theme3Masonry;
use Relatedly\Theming\EndContent\Theme4Carousel;
use Relatedly\Theming\EndContent\Theme5Timeline;
use Relatedly\Theming\EndContent\Theme6Compact;
use Relatedly\Theming\Floating\Variants\Variant1;
use Relatedly\Theming\Floating\Variants\Variant2;
use Relatedly\Theming\Floating\Variants\Variant3;
use Relatedly\Theming\Floating\Variants\Variant4;
use Relatedly\Theming\Floating\Variants\Variant5;

defined( 'ABSPATH' ) || exit;

/**
 * Stores theme renderers by display context and key.
 *
 * @since 2.0.0
 */
class ThemeRegistry {

	/**
	 * Theme renderers grouped by context and key.
	 *
	 * @since 2.0.0
	 * @var array<string, array<string, AbstractTheme>>
	 */
	private array $themes = array(
		'inline'      => array(),
		'end_content' => array(),
		'floating'    => array(),
	);

	/**
	 * Plugin settings repository.
	 *
	 * @since 2.0.0
	 * @var Options
	 */
	private Options $options;

	/**
	 * Create the theme registry and register default themes.
	 *
	 * @since 2.0.0
	 * @param Options $options Plugin settings repository.
	 */
	public function __construct( Options $options ) {
		$this->options = $options;
		$this->register_defaults();
	}

	/**
	 * Register a theme renderer.
	 *
	 * @param string        $context 'inline' | 'end_content' | 'floating'.
	 * @param string        $key     Theme key e.g. 'theme1'.
	 * @param AbstractTheme $theme   Renderer instance.
	 * @param bool          $premium Only registers when license is valid.
	 */
	public function register( string $context, string $key, AbstractTheme $theme, bool $premium = false ): void {
		if ( $premium && ! $this->is_premium() ) {
			return;
		}
		$this->themes[ $context ][ $key ] = $theme;
	}

	/**
	 * Render posts using the specified context + theme key.
	 * Falls back to the first registered theme if the key is not found.
	 *
	 * @param string     $context   'inline' | 'end_content' | 'floating'.
	 * @param string     $key       Theme key.
	 * @param \WP_Post[] $posts     Posts to render.
	 * @param int|null   $source_id Source post ID.
	 * @return string HTML output.
	 */
	public function render( string $context, string $key, array $posts, ?int $source_id = null ): string {
		if ( ! isset( $this->themes[ $context ] ) ) {
			return '';
		}

		if ( 'theme_random' === $key ) {
			$available = array_keys( $this->themes[ $context ] ?? array() );
			if ( ! empty( $available ) ) {
				$key = $available[ array_rand( $available ) ];
			}
		}

		$theme = $this->themes[ $context ][ $key ] ?? reset( $this->themes[ $context ] );

		return $theme instanceof AbstractTheme ? $theme->render( $posts, $source_id ) : '';
	}

	/**
	 * Get all registered theme keys for a context.
	 *
	 * @param string $context Theme context.
	 * @return string[]
	 */
	public function get_keys( string $context ): array {
		return array_keys( $this->themes[ $context ] ?? array() );
	}

	// -------------------------------------------------------------------------
	// Internal
	// -------------------------------------------------------------------------

	/**
	 * Register built-in free and premium theme renderers.
	 */
	private function register_defaults(): void {
		$labels = new LabelProvider( $this->options );

		$this->register( 'inline', 'theme1', new Theme1Simple( $this->options, $labels ) );
		$this->register( 'inline', 'theme2', new Theme2Minimal( $this->options, $labels ) );
		$this->register( 'inline', 'theme3', new Theme3Featured( $this->options, $labels ) );

		// Premium-only inline themes are tagged @fs_premium_only; this block is stripped from the free package.
		if ( \function_exists( 'relatedly_fs' ) && \relatedly_fs()->is__premium_only() ) {
			$this->register( 'inline', 'theme4', new Theme4Quote( $this->options, $labels ), true );
			$this->register( 'inline', 'theme5', new Theme5Badge( $this->options, $labels ), true );
			$this->register( 'inline', 'theme6', new Theme6Callout( $this->options, $labels ), true );
			$this->register( 'inline', 'theme7', new Theme7Gradient( $this->options, $labels ), true );
			$this->register( 'inline', 'theme8', new Theme8Card( $this->options, $labels ), true );
		}

		$this->register( 'end_content', 'theme1', new Theme1List( $this->options, $labels ) );
		$this->register( 'end_content', 'theme2', new Theme2SimpleList( $this->options, $labels ) );

		// Premium-only end-content themes are tagged @fs_premium_only; this block is stripped from the free package.
		if ( \function_exists( 'relatedly_fs' ) && \relatedly_fs()->is__premium_only() ) {
			$this->register( 'end_content', 'theme3', new Theme3Masonry( $this->options, $labels ), true );
			$this->register( 'end_content', 'theme4', new Theme4Carousel( $this->options, $labels ), true );
			$this->register( 'end_content', 'theme5', new Theme5Timeline( $this->options, $labels ), true );
		}

		$this->register( 'end_content', 'theme6', new Theme6Compact( $this->options, $labels ) );

		// Floating sidebar variants are premium only and tagged @fs_premium_only.
		if ( \function_exists( 'relatedly_fs' ) && \relatedly_fs()->is__premium_only() ) {
			$this->register( 'floating', 'variant1', new Variant1( $this->options, $labels ), true );
			$this->register( 'floating', 'variant2', new Variant2( $this->options, $labels ), true );
			$this->register( 'floating', 'variant3', new Variant3( $this->options, $labels ), true );
			$this->register( 'floating', 'variant4', new Variant4( $this->options, $labels ), true );
			$this->register( 'floating', 'variant5', new Variant5( $this->options, $labels ), true );
		}

		// Allow premium layer / third-party plugins to add more themes.
		do_action( 'relatedly_register_themes', $this );
	}

	/**
	 * Check whether premium renderers can be used.
	 *
	 * @return bool
	 */
	private function is_premium(): bool {
		return FeatureGate::is_premium();
	}
}
