<?php
/**
 * Facebook Feed module (Divi 5).
 *
 * @package DiviSupreme
 */

declare(strict_types=1);

namespace DiviSupreme\Modules;

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

use ET\Builder\Framework\DependencyManagement\Interfaces\DependencyInterface;
use ET\Builder\Framework\Utility\HTMLUtility;
use ET\Builder\FrontEnd\Module\Style;
use ET\Builder\Packages\Module\Module;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use ET\Builder\Packages\Module\Options\Element\ElementClassnames;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;

/**
 * Supreme Facebook Page Feed (D4 parity).
 */
class FacebookSimpleFeed implements DependencyInterface
{

	/**
	 * Register module.
	 */
	public function load(): void
	{
		add_action('init', array(self::class, 'register_module'));
	}

	/**
	 * Initialise module dependency.
	 */
	public static function dsm_initialise(): void
	{
		add_action(
			'divi_module_library_modules_dependency_tree',
			static function ($dependency_tree): void {
				$dependency_tree->add_dependency(new self());
			}
		);
	}

	/**
	 * Register module with Divi.
	 */
	public static function register_module(): void
	{
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/facebook-simple-feed';

		ModuleRegistration::register_module(
			$module_json_path,
			array(
				'render_callback' => array(self::class, 'render_callback'),
			)
		);
	}

	/**
	 * Render module style.
	 *
	 * @param array<string, mixed> $args Arguments.
	 */
	public static function module_styles(array $args): void
	{
		$attrs = $args['attrs'] ?? array();
		$elements = $args['elements'];

		Style::add(
			array(
				'id' => $args['id'],
				'name' => $args['name'],
				'orderIndex' => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles' => array(
					$elements->style(
						array(
							'attrName' => 'module',
							'styleProps' => array(
								'disabledOn' => array(
									'disabledModuleVisibility' => $args['settings']['disabledModuleVisibility'] ?? null,
								),
							),
						)
					),
					CssStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr' => $attrs['css'] ?? array(),
						)
					),
				),
			)
		);
	}

	/**
	 * Render module script data.
	 *
	 * @param array<string, mixed> $args Arguments.
	 */
	public static function module_script_data(array $args): void
	{
		$args['elements']->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

	/**
	 * Render module classnames.
	 *
	 * @param array<string, mixed> $args Arguments.
	 */
	public static function module_classnames(array $args): void
	{
		$classnames_instance = $args['classnamesInstance'];
		$attrs = $args['attrs'];

		$classnames_instance->add(
			ElementClassnames::classnames(
				array(
					'attrs' => $attrs['module']['decoration'] ?? array(),
				)
			)
		);

		$classnames_instance->add(self::get_text_align_class($attrs));
	}

	/**
	 * Text alignment utility class (D4 parity: et_pb_text_align_* on the module).
	 *
	 * @param array<string, mixed> $attrs Block attributes.
	 */
	private static function get_text_align_class(array $attrs): string
	{
		$branch = $attrs['fb_alignment'] ?? null;
		$raw = 'center';
		if (is_array($branch) && isset($branch['innerContent']['desktop']['value'])) {
			$v = $branch['innerContent']['desktop']['value'];
			$raw = is_string($v) && '' !== $v ? $v : 'center';
		}
		$allowed = array('left', 'center', 'right', 'justified');
		if (! in_array($raw, $allowed, true)) {
			$raw = 'center';
		}

		return 'et_pb_text_align_' . $raw;
	}

	/**
	 * @param array<int|string, mixed> $tabs Tabs attribute (checkbox list).
	 * @return list<string>
	 */
	private static function normalize_tabs(array $tabs): array
	{
		$out = array();
		foreach ($tabs as $item) {
			if (is_string($item) && '' !== $item) {
				$out[] = $item;
			}
		}

		return $out !== array() ? $out : array('timeline');
	}

	/**
	 * Pixel width/height for Page plugin data-width / data-height (integers only; see Meta Page Plugin docs).
	 *
	 * @param mixed    $raw     Stored value (may include "px").
	 * @param int      $default Fallback.
	 * @param int      $min     Minimum.
	 * @param int|null $max     Maximum or null.
	 */
	private static function normalize_fb_pixel_dimension($raw, int $default, int $min, ?int $max = null): int
	{
		if (is_numeric($raw)) {
			$n = (int) round((float) $raw);
		} else {
			$s = preg_replace('/[^0-9.]/', '', (string) $raw);
			$n = '' === $s ? $default : (int) round((float) $s);
		}

		$n = max($min, $n);
		if (null !== $max) {
			$n = min($max, $n);
		}

		return $n;
	}

	/**
	 * Read RangeContainer value for FE (desktop first, then tablet / phone).
	 *
	 * @param array<string, mixed> $attrs Full block attrs.
	 * @param string               $key   e.g. fb_width.
	 * @param string               $default When all breakpoints are empty.
	 * @return mixed Raw value for {@see self::normalize_fb_pixel_dimension()}.
	 */
	private static function get_fb_range_inner_value(array $attrs, string $key, string $default)
	{
		$inner = $attrs[ $key ]['innerContent'] ?? null;
		if (! is_array($inner)) {
			return $default;
		}

		foreach (array('desktop', 'tablet', 'phone') as $bp) {
			if (! isset($inner[ $bp ]) || ! is_array($inner[ $bp ])) {
				continue;
			}

			if (! array_key_exists('value', $inner[ $bp ])) {
				continue;
			}

			$val = $inner[ $bp ]['value'];

			if (is_string($val) && '' !== trim($val)) {
				return $val;
			}

			if (is_numeric($val)) {
				return $val;
			}
		}

		return $default;
	}

	/**
	 * Normalize innerContent yes/no to lowercase "true"|"false" for XFBML data-* attributes.
	 *
	 * @param mixed $raw Raw attr value.
	 */
	private static function inner_value_to_fb_bool_string($raw, bool $default): string
	{
		if (is_bool($raw)) {
			return $raw ? 'true' : 'false';
		}
		if (is_string($raw)) {
			$v = strtolower(trim($raw));
			if (in_array($v, array('true', 'on', 'yes', '1'), true)) {
				return 'true';
			}
			if (in_array($v, array('false', 'off', 'no', '0', ''), true)) {
				return 'false';
			}
		}

		return $default ? 'true' : 'false';
	}

	/**
	 * Render module HTML output.
	 *
	 * @param array<string, mixed> $attrs    Module attributes.
	 * @param string                 $content  Inner content.
	 * @param \WP_Block              $block    Block instance.
	 * @param mixed                  $elements Style/element helpers.
	 */
	public static function render_callback($attrs, $content, $block, $elements): string
	{
		$fb_url = $attrs['fb_page_url']['innerContent']['desktop']['value'] ?? 'https://www.facebook.com/divisupreme/';
		$fb_hide_cover = self::inner_value_to_fb_bool_string(
			$attrs['fb_hide_cover']['innerContent']['desktop']['value'] ?? null,
			false
		);
		$fb_small_header = self::inner_value_to_fb_bool_string(
			$attrs['fb_small_header']['innerContent']['desktop']['value'] ?? null,
			false
		);
		$fb_show_facepile = self::inner_value_to_fb_bool_string(
			$attrs['fb_show_facepile']['innerContent']['desktop']['value'] ?? null,
			true
		);
		$fb_adapt_container = self::inner_value_to_fb_bool_string(
			$attrs['fb_adapt_container_width']['innerContent']['desktop']['value'] ?? null,
			true
		);
		$fb_hide_cta = self::inner_value_to_fb_bool_string(
			$attrs['fb_hide_cta']['innerContent']['desktop']['value'] ?? null,
			false
		);
		$fb_lazy = self::inner_value_to_fb_bool_string(
			$attrs['fb_lazy']['innerContent']['desktop']['value'] ?? null,
			false
		);
		$fb_tabs_raw = $attrs['fb_tabs']['innerContent']['desktop']['value'] ?? array('timeline');
		$fb_tabs = is_array($fb_tabs_raw) ? self::normalize_tabs($fb_tabs_raw) : array('timeline');
		$fb_width_raw  = self::get_fb_range_inner_value(is_array($attrs) ? $attrs : array(), 'fb_width', '340');
		$fb_height_raw = self::get_fb_range_inner_value(is_array($attrs) ? $attrs : array(), 'fb_height', '500');
		$fb_width = self::normalize_fb_pixel_dimension($fb_width_raw, 340, 180, 500);
		$fb_height = self::normalize_fb_pixel_dimension($fb_height_raw, 500, 70, null);

		wp_enqueue_script('dsm-facebook');

		$container = sprintf(
			'<div class="dsm-facebook-feed">
				<div class="fb-page" data-href="%1$s" data-tabs="%5$s" data-width="%6$d" data-height="%7$d" data-small-header="%4$s" data-adapt-container-width="%8$s" data-hide-cover="%2$s" data-show-facepile="%3$s" data-hide-cta="%9$s" data-lazy="%10$s"></div>
			</div>',
			esc_url($fb_url),
			esc_attr($fb_hide_cover),
			esc_attr($fb_show_facepile),
			esc_attr($fb_small_header),
			esc_attr(implode(',', $fb_tabs)),
			$fb_width,
			$fb_height,
			esc_attr($fb_adapt_container),
			esc_attr($fb_hide_cta),
			esc_attr($fb_lazy)
		);

		$module_inner = HTMLUtility::render(
			array(
				'tag' => 'div',
				'attributes' => array(
					'class' => 'et_pb_module_inner',
				),
				'childrenSanitizer' => 'et_core_esc_previously',
				'children' => $container,
			)
		);

		$module_elements = $elements->style_components(
			array(
				'attrName' => 'module',
			)
		);

		$module_container_children = $module_elements . $module_inner;

		return Module::render(
			array(
				'orderIndex' => $block->parsed_block['orderIndex'],
				'storeInstance' => $block->parsed_block['storeInstance'],
				'attrs' => $attrs,
				'elements' => $elements,
				'id' => $block->parsed_block['id'],
				'moduleClassName' => 'dsm_facebook_feed',
				'name' => $block->block_type->name,
				'classnamesFunction' => array(self::class, 'module_classnames'),
				'moduleCategory' => $block->block_type->category,
				'stylesComponent' => array(self::class, 'module_styles'),
				'scriptDataComponent' => array(self::class, 'module_script_data'),
				'children' => $module_container_children,
			)
		);
	}
}
