<?php

namespace ElementPack\Modules\TextGradientBackground;

use Elementor\Controls_Manager;
use Elementor\Group_Control_Background;
use ElementPack\Base\Element_Pack_Module_Base;
use ElementPack\Modules\TextGradientBackground\Atomic\Support as Atomic_Support;
use ElementPack\Modules\TextGradientBackground\Normalizers\Settings_Normalizer;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // Exit if accessed directly

class Module extends Element_Pack_Module_Base {

	private $atomic_support = null;
	private $settings_normalizer = null;

	public function __construct() {
		parent::__construct();
		$this->add_actions();
	}

	public function get_name() {
		return 'bdt-text-gradient-background';
	}

	public function register_section( $element ) {
		$pro_lock = bdt_get_widget_badge( str_replace( 'bdt-', '', $this->get_name() ) );
		$element->start_controls_section(
			'section_element_pack_tgb_controls',
			[
				'tab'   => Controls_Manager::TAB_ADVANCED,
				'label' => BDTEP_CP . esc_html__( 'Text Gradient Background', 'bdthemes-element-pack' ) . $pro_lock . BDTEP_NC,
			]
		);
		$element->end_controls_section();
	}

	public function register_controls( $widget, $args ) {

		$widget->add_control(
			'element_pack_tgb_enable',
			[
				'label'              => esc_html__( 'Use Text Gradient Background?', 'bdthemes-element-pack' ),
				'type'               => Controls_Manager::SWITCHER,
				'label_on'           => esc_html__( 'Yes', 'bdthemes-element-pack' ),
				'label_off'          => esc_html__( 'No', 'bdthemes-element-pack' ),
				'render_type'        => 'template',
				'frontend_available' => true,
			]
		);

		$widget->add_control(
			'element_pack_tgb_selector',
			[
				'label'              => esc_html__( 'Class & ID Selector', 'bdthemes-element-pack' ),
				'placeholder'        => esc_html__( '.class or #id', 'bdthemes-element-pack' ),
				'description'        => esc_html__( 'Enter your class or id selector of your text parent tag. e.g: .my-class, #my-id', 'bdthemes-element-pack' ),
				'type'               => Controls_Manager::TEXT,
				'frontend_available' => true,
				'condition'          => [
					'element_pack_tgb_enable' => 'yes',
				],
			]
		);

		$widget->add_group_control(
			Group_Control_Background::get_type(),
			[
				'name'           => 'element_pack_tgb_background',
				'types'          => [ 'gradient' ],
				'selector'       => '{{WRAPPER}} .element-pack-tgb-background',
				'fields_options' => [
					'background' => [
						'default' => 'gradient',
					],
					'color' => [
						'default' => '#08aeec',
					],
					'color_b' => [
						'default' => '#2af598',
					],
					'gradient_angle' => [
						'default' => [
							'unit' => 'deg',
							'size' => 90,
						],
					],
				],
				'frontend_available' => true,
				'condition'          => [
					'element_pack_tgb_enable' => 'yes',
				],
			]
		);
	}

	public function ensure_frontend_script_deps() {
		$scripts = wp_scripts();

		if ( empty( $scripts->registered['ep-text-gradient-background'] ) ) {
			return;
		}

		$deps = $scripts->registered['ep-text-gradient-background']->deps;

		foreach ( [ 'elementor-frontend', 'elementor-frontend-modules' ] as $handle ) {
			if ( wp_script_is( $handle, 'registered' ) && ! in_array( $handle, $deps, true ) ) {
				$deps[] = $handle;
			}
		}

		$scripts->registered['ep-text-gradient-background']->deps = $deps;
	}

	public function enqueue_scripts() {
		$this->ensure_frontend_script_deps();
	}

	public function enqueue_editor_scripts() {
		wp_enqueue_script(
			'ep-text-gradient-background-editor',
			BDTEP_ASSETS_URL . 'js/modules/ep-text-gradient-background-editor.min.js',
			[ 'elementor-editor' ],
			BDTEP_VER,
			true
		);
	}

	public function should_script_enqueue( $widget ) {
		if ( $this->get_settings_normalizer()->normalize( $widget ) ) {
			$this->enqueue_scripts();
			wp_enqueue_script( 'ep-text-gradient-background' );
		}
	}

	public function register_atomic_schema( $schema ) {
		return $this->get_atomic_support()->register_schema( $schema );
	}

	public function register_atomic_controls( $controls, $element ) {
		$element_type = method_exists( $element, 'get_element_type' ) ? $element::get_element_type() : '';

		if ( 'e-heading' !== $element_type ) {
			return $controls;
		}

		return $this->get_atomic_support()->register_controls( $controls );
	}

	public function maybe_register_atomic_hooks() {
		if ( ! class_exists( '\Elementor\Plugin' ) ) {
			return;
		}

		if ( has_filter( 'elementor/atomic-widgets/props-schema', [ $this, 'register_atomic_schema' ] ) ) {
			return;
		}

		add_filter( 'elementor/atomic-widgets/props-schema', [ $this, 'register_atomic_schema' ], 20 );
		add_filter( 'elementor/atomic-widgets/controls', [ $this, 'register_atomic_controls' ], 30, 2 );
	}

	public function inject_atomic_text_gradient_background_data( $widget_content, $widget ) {
		if ( ! $this->should_inject_text_gradient_data( $widget ) ) {
			return $widget_content;
		}

		if ( ! is_string( $widget_content ) || '' === $widget_content ) {
			return $widget_content;
		}

		$settings = $this->get_settings_normalizer()->normalize( $widget );

		if ( ! $settings ) {
			return $widget_content;
		}

		if ( false !== strpos( $widget_content, 'data-ep-text-gradient-background=' ) ) {
			return $widget_content;
		}

		$encoded_settings = wp_json_encode( $settings );

		if ( ! is_string( $encoded_settings ) || '' === $encoded_settings ) {
			return $widget_content;
		}

		$atomic_classes = [ 'bdt-tgb-yes', 'element-pack-tgb-background' ];
		$inline_style   = $this->get_inline_style( $settings );
		$target_class   = $this->get_injection_target_class( $widget );

		if ( class_exists( '\WP_HTML_Tag_Processor' ) ) {
			$processor = new \WP_HTML_Tag_Processor( $widget_content );
			$updated   = false;

			while ( $processor->next_tag() ) {
				$class_attr = $processor->get_attribute( 'class' );

				if ( ! is_string( $class_attr ) || false === strpos( $class_attr, $target_class ) ) {
					continue;
				}

				$processor->set_attribute( 'data-ep-text-gradient-background', $encoded_settings );
				$processor->set_attribute( 'class', $this->merge_atomic_classes( $class_attr, $atomic_classes ) );

				if ( '' !== $inline_style ) {
					$processor->set_attribute( 'style', $this->merge_atomic_style( $processor->get_attribute( 'style' ), $inline_style ) );
				}

				$updated = true;
				break;
			}

			if ( $updated ) {
				return $processor->get_updated_html();
			}
		}

		$target_pattern = '/<([a-zA-Z][^\\s>]*)([^>]*\bclass=(["\'])(?=[^"\']*' . preg_quote( $target_class, '/' ) . ')[^"\']*\3[^>]*)>/';

		return preg_replace_callback(
			$target_pattern,
			function ( $matches ) use ( $encoded_settings, $atomic_classes, $inline_style ) {
				$tag_name   = $matches[1];
				$attributes = $matches[2];

				if ( preg_match( '/\sclass=(["\'])(.*?)\1/', $attributes, $class_matches ) ) {
					$merged_classes = $this->merge_atomic_classes( $class_matches[2], $atomic_classes );
					$attributes     = preg_replace(
						'/\sclass=(["\'])(.*?)\1/',
						' class="' . esc_attr( $merged_classes ) . '"',
						$attributes,
						1
					);
				} else {
					$attributes .= ' class="' . esc_attr( implode( ' ', $atomic_classes ) ) . '"';
				}

				if ( '' !== $inline_style ) {
					if ( preg_match( '/\sstyle=(["\'])(.*?)\1/', $attributes, $style_matches ) ) {
						$merged_style = $this->merge_atomic_style( $style_matches[2], $inline_style );
						$attributes   = preg_replace(
							'/\sstyle=(["\'])(.*?)\1/',
							' style="' . esc_attr( $merged_style ) . '"',
							$attributes,
							1
						);
					} else {
						$attributes .= ' style="' . esc_attr( $inline_style ) . '"';
					}
				}

				return '<' . $tag_name . $attributes . ' data-ep-text-gradient-background="' . esc_attr( $encoded_settings ) . '">';
			},
			$widget_content,
			1
		) ?: $this->inject_into_first_tag( $widget_content, $encoded_settings, $atomic_classes, $inline_style );
	}

	private function get_injection_target_class( $widget ): string {
		if ( method_exists( $widget, 'get_name' ) && 'heading' === $widget->get_name() ) {
			return 'elementor-heading-title';
		}

		return 'e-heading-base';
	}

	private function inject_into_first_tag( string $widget_content, string $encoded_settings, array $atomic_classes, string $inline_style ): string {
		return preg_replace_callback(
			'/<([a-zA-Z][^\\s>]*)([^>]*)>/',
			function ( $matches ) use ( $encoded_settings, $atomic_classes, $inline_style ) {
				$tag_name   = $matches[1];
				$attributes = $matches[2];

				if ( preg_match( '/\sclass=(["\'])(.*?)\1/', $attributes, $class_matches ) ) {
					$merged_classes = $this->merge_atomic_classes( $class_matches[2], $atomic_classes );
					$attributes     = preg_replace(
						'/\sclass=(["\'])(.*?)\1/',
						' class="' . esc_attr( $merged_classes ) . '"',
						$attributes,
						1
					);
				} else {
					$attributes .= ' class="' . esc_attr( implode( ' ', $atomic_classes ) ) . '"';
				}

				if ( '' !== $inline_style ) {
					if ( preg_match( '/\sstyle=(["\'])(.*?)\1/', $attributes, $style_matches ) ) {
						$merged_style = $this->merge_atomic_style( $style_matches[2], $inline_style );
						$attributes   = preg_replace(
							'/\sstyle=(["\'])(.*?)\1/',
							' style="' . esc_attr( $merged_style ) . '"',
							$attributes,
							1
						);
					} else {
						$attributes .= ' style="' . esc_attr( $inline_style ) . '"';
					}
				}

				return '<' . $tag_name . $attributes . ' data-ep-text-gradient-background="' . esc_attr( $encoded_settings ) . '">';
			},
			$widget_content,
			1
		) ?: $widget_content;
	}

	private function get_inline_style( array $settings ): string {
		$background = $settings['background'] ?? '';

		if ( '' === $background || ! $this->is_safe_css_background( $background ) ) {
			return '';
		}

		return sprintf(
			'background-color:transparent!important;background-image:%s;background-clip:text!important;-webkit-background-clip:text!important;color:transparent!important;-webkit-text-fill-color:transparent!important;',
			$background
		);
	}

	private function is_safe_css_background( string $background ): bool {
		$background = trim( $background );

		if ( '' === $background ) {
			return false;
		}

		if ( 1 === preg_match( '/^(linear-gradient|radial-gradient|repeating-linear-gradient|repeating-radial-gradient)\(/i', $background ) ) {
			return false === strpos( $background, ';' ) && false === strpos( $background, '{' ) && false === strpos( $background, '}' );
		}

		return false;
	}

	private function merge_atomic_style( $existing_style, string $atomic_style ): string {
		if ( ! is_string( $existing_style ) || '' === trim( $existing_style ) ) {
			return $atomic_style;
		}

		return rtrim( $existing_style, " \t\n\r\0\x0B;" ) . ';' . $atomic_style;
	}

	private function merge_atomic_classes( string $existing_classes, array $atomic_classes ) {
		$classes = preg_split( '/\s+/', trim( $existing_classes ) );

		if ( ! is_array( $classes ) ) {
			$classes = [];
		}

		return implode( ' ', array_unique( array_merge( array_filter( $classes ), $atomic_classes ) ) );
	}

	private function should_inject_text_gradient_data( $widget ): bool {
		if ( ! method_exists( $widget, 'get_name' ) ) {
			return false;
		}

		if ( 'heading' === $widget->get_name() ) {
			return true;
		}

		return $this->is_atomic_heading( $widget );
	}

	private function is_atomic_heading( $widget ): bool {
		if ( ! method_exists( $widget, 'get_atomic_settings' ) ) {
			return false;
		}

		$element_type = method_exists( $widget, 'get_element_type' ) ? $widget::get_element_type() : '';

		return 'e-heading' === $element_type;
	}

	private function get_atomic_support() {
		if ( null === $this->atomic_support ) {
			$this->atomic_support = new Atomic_Support();
		}

		return $this->atomic_support;
	}

	private function get_settings_normalizer() {
		if ( null === $this->settings_normalizer ) {
			$this->settings_normalizer = new Settings_Normalizer();
		}

		return $this->settings_normalizer;
	}

	protected function add_actions() {

		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );

		add_action( 'elementor/element/common/_section_style/after_section_end', [ $this, 'register_section' ] );
		add_action( 'elementor/element/common/section_element_pack_tgb_controls/before_section_end', [ $this, 'register_controls' ], 10, 2 );

		add_action( 'elementor/frontend/widget/before_render', [ $this, 'should_script_enqueue' ] );
		add_action( 'elementor/preview/enqueue_scripts', [ $this, 'enqueue_scripts' ] );
		add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_editor_scripts' ] );
		add_filter( 'elementor/widget/render_content', [ $this, 'inject_atomic_text_gradient_background_data' ], 10, 2 );
		add_action( 'elementor/frontend/after_register_scripts', [ $this, 'ensure_frontend_script_deps' ] );

		if ( did_action( 'elementor/init' ) ) {
			$this->maybe_register_atomic_hooks();
		} else {
			add_action( 'elementor/init', [ $this, 'maybe_register_atomic_hooks' ] );
		}
	}
}
