<?php
/**
 * Banner template class
 *
 * @link       https://www.webtoffee.com/
 * @since      3.0.0
 * @package    WebToffee\CookieConsent\Lite\Admin\Modules\Banners\Includes
 */

namespace WebToffee\CookieConsent\Lite\Admin\Modules\Banners\Includes;

use DOMDocument;
use DOMXPath;
use WP_Error;
use WebToffee\CookieConsent\Lite\Includes\Cache;
use WebToffee\CookieConsent\Lite\Frontend\Modules\Iab\Iab;

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

/**
 * Handles templating of Cookie banner elements
 *
 * @version     3.0.0
 * @package     WebToffee\CookieConsent\Lite\Admin\Modules\Banners\Includes
 */
class Template {

	/**
	 * Banner properties
	 *
	 * @var array
	 */
	protected $properties;

	/**
	 * Template styles
	 *
	 * @var string
	 */
	protected $styles = '';

	/**
	 * Template HTML
	 *
	 * @var string
	 */
	protected $html = '';

	/**
	 * Template type, by deafult it will be banner
	 *
	 * @var string
	 */
	protected $type = 'banner';

	/**
	 * Theme presets to be applied on the template
	 *
	 * @var array
	 */
	protected $presets = array();

	/**
	 * Type of theme dark/light
	 *
	 * @var string
	 */
	protected $theme;

	/**
	 * Template ID
	 *
	 * @var string
	 */
	protected $id;

	/**
	 * Banner object
	 *
	 * @var object
	 */
	protected $banner;

	/**
	 * Banners array
	 *
	 * array
	 */
	protected $banners;

	/**
	 * Template config
	 *
	 * @var array
	 */
	protected $template;

	/**
	 * Object cache group
	 *
	 * @var string
	 */
	protected $cache_group = 'banner_template';

	/**
	 * Language of the template
	 *
	 * @var string
	 */
	protected $language = 'en';

	/**
	 * Instance of the current class
	 *
	 * @var object
	 */
	private static $instance;

	/**
	 * Return the current instance of the class
	 *
	 * @return object
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor function
	 *
	 * @param object $banner Banner object.
	 */
	public function __construct( $banner = false ) {
		$this->language = wcc_current_language();
		$this->banners  = \WebToffee\CookieConsent\Lite\Admin\Modules\Banners\Includes\Controller::get_instance()->get_active_banners();
		if ( $banner ) {
			$this->banner     = $banner;
			$this->properties = $banner->get_settings();
			$this->load();
		}
		add_action( 'wcc_reset_settings', array( $this, 'reset' ) );
		add_action( 'wcc_after_update_banner', array( $this, 'clear_template' ) );
		add_action( 'wcc_after_update_cookie_category', array( $this, 'clear_template' ) );
		add_action( 'wcc_after_update_cookie', array( $this, 'clear_template' ) );
		add_action( 'wcc_clear_cache', array( $this, 'clear_template' ) );
	}

	/**
	 * Get or Set templates based on the condition.
	 *
	 * @return void
	 */
	public function load() {
		if ( true === $this->is_preview() || empty( $this->get_stored() ) ) {
			$this->generate();
		} else {
			$this->set_template();
		}
	}
	/**
	 * Returns the content html template from the configs.
	 *
	 * @return void
	 */
	public function generate() {
		$settings          = isset( $this->properties['settings'] ) ? $this->properties['settings'] : array();
		$preference_center = isset( $settings['preferenceCenter'] ) ? $settings['preferenceCenter'] : 'center';
		$this->id          = isset( $settings['versionID'] ) ? $settings['versionID'] : 'default';
		$this->type        = ( $settings['type'] && 'push-down' !== $preference_center ) ? $settings['type'] : 'classic';
		$this->theme       = isset( $settings['theme'] ) ? $settings['theme'] : 'light';
		$templates         = $this->get_templates( $this->id );
		$this->presets     = $this->get_presets( $this->id );
		foreach ( $templates as $template ) {
			$type = isset( $template['type'] ) ? $template['type'] : '';
			if ( $type === $this->type ) {
				$this->template = $template;
				$this->styles   = isset( $this->template['css'] ) ? $this->template['css'] : '';
				break;
			}
		}
		new \WebToffee\CookieConsent\Lite\Frontend\Modules\Shortcodes\Shortcodes( $this->banner, $this->id );
		$this->prepare_html();

		if ( false === $this->is_preview() ) {
			$this->update();
		}
	}

	/**
	 * Get presets by template version
	 *
	 * @param integer $id Template version.
	 * @return array
	 */
	public function get_presets( $id ) {
		$this->id = isset( $id ) ? $id : 0;
		$key      = '_preset_' . $id;
		$presets  = Cache::get( $key, $this->cache_group );
		$presets  = ( isset( $presets ) && is_array( $presets ) ) ? $presets : array();
		if ( empty( $presets ) ) {
			$presets = $this->load_presets();
			Cache::set( $key, $this->cache_group, $presets, false );
		}
		return $presets;
	}

	/**
	 * Get templates by template version
	 *
	 * @param integer $id Template version.
	 * @return array
	 */
	public function get_templates( $id ) {
		$this->id  = isset( $id ) ? $id : 0;
		$key       = '_template_' . $this->id;
		$templates = Cache::get( $key, $this->cache_group );
		$templates = ( isset( $templates ) && is_array( $templates ) ) ? $templates : array();

		if ( empty( $templates ) ) {
			$templates = $this->load_templates();
			Cache::set( $key, $this->cache_group, $templates, false );
		}
		return $templates;
	}
	/**
	 * Returns the template styles
	 *
	 * @return string
	 */
	public function get_styles() {
		if ( ! $this->styles ) {
			return '';
		}

		return $this->styles;
	}
	/**
	 * Get template HTML
	 *
	 * @return string
	 */
	public function get_html() {
		if ( ! $this->html ) {
			return '';
		}
		return wp_kses( $this->html, wcc_allowed_html() );
	}

	/**
	 * Get the template config and presets
	 *
	 * @return array
	 */
	private function load_templates() {
		$template    = 'template';
		$settings    = $this->banner->get_settings();
		$iab_enabled = $settings['settings']['iab_enabled'];
		if ( $iab_enabled ) {
			$template = 'iab-template';
		}
		return wcc_read_json_file( __DIR__ . '/templates/' . esc_html( $this->id ) . '/' . $template . '.json' );
	}

	/**
	 * Load presets from plugin itself.
	 *
	 * @return array
	 */
	private function load_presets() {
		return wcc_read_json_file( __DIR__ . '/templates/' . esc_html( $this->id ) . '/theme.json' );
	}

	/**
	 * Clear templates and preset from transient.
	 *
	 * @return void
	 */
	public function reset() {
		Cache::delete( $this->cache_group );
	}
	/**
	 * Publicly available function clear template cache.
	 *
	 * @return void
	 */
	public function delete_cache() {
		if ( wcc_is_admin_request() ) {
			$this->reset();
		}
	}
	/**
	 * Returns the template HTML after processing the shortcodes
	 *
	 * @return string
	 */
	private function prepare_html() {
		$html     = '';
		$colors   = array();
		$template = isset( $this->template['html'] ) ? $this->template['html'] : '';
		if ( '' === $template ) {
			return $html;
		}
		$html = do_shortcode( $template );
		if ( ! class_exists( 'DOMDocument' ) || ! class_exists( 'DOMXPath' ) ) {
			return $html;
		}
		try {
			$dom         = new DOMDocument();
			$used_errors = libxml_use_internal_errors( true );
			if ( function_exists( 'mb_encode_numericentity' ) ) {
				$html = mb_encode_numericentity( $html, array( 0x80, 0x10FFFF, 0, ~0 ), 'UTF-8' );
			}
			$dom->loadHTML( $html, LIBXML_HTML_NODEFDTD );
			$used_errors || libxml_use_internal_errors( false );

			$finder     = new DOMXPath( $dom );
			$elements   = $finder->query( '//*[@data-tag]' );
			$properties = $this->properties;
			$configs    = isset( $properties['config'] ) ? $properties['config'] : array();

			if ( ! empty( $this->presets ) ) {
				foreach ( $this->presets as $preset ) {
					$theme = isset( $preset['name'] ) ? $preset['name'] : '';
					if ( $theme === $this->theme ) {
						$colors = ( isset( $preset['settings'] ) && is_array( $preset['settings'] ) ) ? $preset['settings'] : array();
						break;
					}
				}
			}

			if ( ! empty( $colors ) ) {
				$configs = array_replace_recursive( $configs, $colors );
			}

			foreach ( $elements as $element ) {
				$tag = $element->getAttribute( 'data-tag' );
				if ( empty( $tag ) ) {
					continue;
				}
				if ( in_array( $tag, $this->image_tags(), true ) ) {
					$img_tags = $element->getELementsByTagName( 'img' );
					foreach ( $img_tags as $img ) {
						$src = $this->get_assets_path( $img->getAttribute( 'src' ) );
						$img->setAttribute( 'src', $src );
					}
				}
				$config  = wcc_array_search( $configs, 'tag', $tag );
				$preview = $this->is_preview();
				$enabled = isset( $config['status'] ) && false === $preview ? $config['status'] : true;
				// Initialize powered_by_style for each element.
				$powered_by_style = '';

				if ( false === $enabled ) {
					if ( ! in_array( $tag, array( 'detail-powered-by', 'optout-powered-by' ), true ) ) {
						$element->parentNode->removeChild( $element );  //phpcs:ignore WordPress.NamingConventions.ValidVariableName
						continue;
					} else {
						$powered_by_style = 'display:none;';
					}
				}

				$styles = isset( $config['styles'] ) ? $config['styles'] : array();

				$existing = $element->getAttribute( 'style' );
				$style    = isset( $existing ) ? $existing : '';
				if ( ! empty( $styles ) ) {
					foreach ( $styles as $property => $value ) {
						if ( '' !== $value ) {
							$style .= $property . ':' . $value . ';';
						}
					}
				}
				if ( '' !== $style ) {
					// Append powered_by_style to the style attribute.
					if ( ! empty( $powered_by_style ) ) {
						$style .= $powered_by_style;
					}
					$element->setAttribute( 'style', esc_attr( $style ) );
				}
			}

			$this->html = $dom->saveHTML( $dom->documentElement ); //phpcs:ignore WordPress.NamingConventions.ValidVariableName
		} catch ( \Exception $e ) {
			// Could not generate the template.
			$this->html = $html;
		}
		return $this->html;
	}

	/**
	 * Check if banner is in preview mode.
	 *
	 * @return boolean
	 */
	public function is_preview() {
		return defined( 'WCC_PREVIEW_REQUEST' ) && WCC_PREVIEW_REQUEST;
	}
	/**
	 * Retrieve stored template.
	 *
	 * @return string
	 */
	public function get_stored() {
		$stored = get_option( 'wcc_banner_template_' . $this->banner->get_slug(), array() );
		return isset( $stored[ $this->language ] ) ? $stored[ $this->language ] : array();
	}

	/**
	 * Store templates to options table
	 *
	 * @return void
	 */
	public function update() {
		$stored = get_option( 'wcc_banner_template_' . $this->banner->get_slug(), array() );
		$stored = is_array( $stored ) && ! empty( $stored ) ? $stored : array();

		$stored[ $this->language ] = array(
			'html'   => wp_kses( $this->html, wcc_allowed_html() ),
			'styles' => wp_kses(
				$this->styles,
				wcc_allowed_html()
			),
		);
		update_option(
			'wcc_banner_template_' . $this->banner->get_slug(),
			$stored
		);
	}

	/**
	 * Set templates from the stored
	 *
	 * @return void
	 */
	public function set_template() {
		$template     = $this->get_stored();
		$this->styles = isset( $template['styles'] ) ? $template['styles'] : '';
		$this->html   = isset( $template['html'] ) ? $template['html'] : '';
	}

	/**
	 * Reset banner template
	 *
	 * @return void
	 */
	public function clear_template() {
		foreach ( $this->banners as $key => $banner ) {
			update_option( 'wcc_banner_template_' . $banner->get_slug(), '' );
		}
		$banner_controller = new \WebToffee\CookieConsent\Lite\Admin\Modules\Banners\Includes\Controller();
		$banner_controller->delete_cache();
	}

	/**
	 * Return the asset path
	 *
	 * @param string $path Template path.
	 * @return string
	 */
	public function get_assets_path( $path ) {
		$base_name = wp_basename( $path );
		return WCC_APP_ASSETS_URL . $base_name;
	}
	/**
	 * Elements contain image tags
	 *
	 * @return array
	 */
	public function image_tags() {
		return array(
			'revisit-consent',
			'close-button',
			'detail-close',
			'detail-powered-by',
			'optout-close',
			'optout-powered-by',
		);
	}
	/**
	 * This function will return the IAB vendor files.
	 *
	 * @since 3.0.2
	 *
	 * @param WP_REST_Request $request Request data.
	 * @return WP_Error|WP_REST_Response
	 */
	public function fetch_json( $request ) {
		// Get language from request content, similar to update_vendor_data
		$language = isset( $request['content'] ) ? sanitize_text_field( wp_unslash( $request['content'] ) ) : 'en';
		return rest_ensure_response( Iab::get_instance()->get_vendor_list( $language ) );
	}
}
