<?php
if (!defined('ABSPATH')) {
  exit;
}

// Check if class exists
if (!class_exists('Ucla_Responsive_Block_Controls')) {
  class Ucla_Responsive_Block_Controls
  {
    // The instance of this class
    private static $instance = null;

    /**
     * An array of styles.
     * 
     * @access protected
     * @var array
     */
    protected $styles = [];
    protected $class_names = [];

    // Returns the instance of this class.
    public static function get_instance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function __construct()
    {

        // Load plugin textdomain
        add_action('init', array($this, 'load_textdomain'));

        // Render block
        add_filter('render_block', array($this, 'render_block'), 10, 2);

        // Print all block styles in the head.
        add_action( 'wp_footer', [ $this, 'print_styles' ] );

        // Enqueue block editor assets
        add_action('enqueue_block_editor_assets', array($this, 'enqueue_editor_assets'));

        // Enqueue both backend + frontend block assets
        add_action('enqueue_block_assets', array($this, 'enqueue_block_assets'), 100);

        // Add the responsive controls attribute to blocks.
        add_action('wp_loaded', array($this, 'add_attribute_to_blocks'), 100);

    }

    /**
     * Load plugin textdomain
     */
    public function load_textdomain()
    {
      load_plugin_textdomain(
          'ucla-wordpress-plugin',
          false,
          dirname(plugin_basename(__FILE__)) . '/languages/'
      );
    }

    /**
     * @return array Blocks known to not work properly.
     */
    public function get_unsupported_responsive_blocks() {
      return apply_filters('ucla_unsupported_unresponsive_blocks', [
        'core/freeform',
        'core/html',
        'core/shortcode',
        'core/legacy-widget',
        'core/more',
        'core/nextpage',
        'core/query',
        'core/page-list',
        'core/post-title',
        'core/post-template',
        'core/post-date',
        'core/post-featured-image',
        'core/post-author-name',
        'core/post-excerpt',
        'core/spacer',
        'ucla-wordpress-plugin/accordion-item',
        'ucla-wordpress-plugin/carousel-slide',
        'ucla-wordpress-plugin/icons',
        'ucla-wordpress-plugin/tab-item',
      ]);
    }

    /**
     * Determine if block is supported to have an animation.
     *
     * @param string $block_name
     * @return boolean
     */
    public function is_supported($block_name) {

      static $not_supported;
      if(!is_array($not_supported)) {
        $not_supported = $this->get_unsupported_responsive_blocks();
      }

      $is_allowed_namespace = (
        str_starts_with($block_name, 'core/') || str_starts_with($block_name, 'ucla-wordpress-plugin/')
      );

      if (!$is_allowed_namespace) {
        return false;
      }

      return !in_array($block_name, $not_supported, true);
    }

    /**
     * Add the `responsiveControls` attribute to all server-side registered blocks.
     *
     * @hooked wp_loaded,100    This might not be late enough for all blocks, I don't know when blocks are supposed to be registered.
     */
    public function add_attribute_to_blocks()
    {
        $registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();

        foreach ($registered_blocks as $name => $block) {
          if ($this->is_supported($name)) {
            $block->attributes['responsiveControls'] = array(
                'type' => 'object'
            );
          }
        }
    }

    public function skip_important_css_prop($deviceStyles, $wpBlock) {
      if (strpos($deviceStyles, ';') !== false) {
        $css_prop = explode(';', $deviceStyles);
        $importantProperties = array_map(function($property) {
            // Trim property to check if empty
            $property = trim($property);
            if ($property === '') {
              return null;
            }
            // Extract the property name before the colon
            $parts = explode(':', $property, 2);
            $propertyName = trim ($parts[0]);
            // Skip adding !important to margin-left and margin-right
            if (in_array($propertyName, ['margin-left', 'margin-right', 'flex-basis'])) {
              return $property;
            }
            return rtrim($property, ';') . '!important';
          }, $css_prop);
          $formattedCss = implode(';', $importantProperties);
      }
      return $formattedCss;
    }

    /**
     * Enqueue block editor assets
     */
    public function enqueue_editor_assets()
    {
      $editor_assets = include(plugin_dir_path(__FILE__) . 'responsive-controls.asset.php');

      wp_enqueue_style(
        'ucla-responsive-block-controls-editor',
        plugins_url('/responsive-controls.css', __FILE__),
        array('wp-editor'),
        $editor_assets['version']
      );
      wp_enqueue_script(
          'ucla-responsive-block-controls-editor',
          plugins_url('/responsive-controls.js', __FILE__),
          $editor_assets['dependencies'],
          $editor_assets['version'],
          array('in_footer' => true)
      );
      wp_localize_script(
        'ucla-responsive-block-controls-editor',
        'uclaResponsiveSupport',
        [
          'responsiveUnsupportedBlocks' => $this->get_unsupported_responsive_blocks(),
        ]
      );
    }

    /**
     * Enqueue frontend block assets
     */
    public function enqueue_block_assets() {
      $frontend_assets = include(plugin_dir_path(__FILE__) . 'responsive-controls.asset.php');

      wp_register_style(
        'ucla-responsive-block-controls-styles',
        plugins_url('/style-responsive-controls.css', __FILE__),
        is_admin() ? array('wp-editor') : array(),
        $frontend_assets['version']
      );
      wp_enqueue_style('ucla-responsive-block-controls-styles');
    }

    public function add_custom_attribute($block_content, $attribute_name, $attribute_value) {
      // Search the first tag HTML in the block content
      $pos = strpos($block_content, '>');
  
      if ($pos !== false) {
        // Insert the custom attribute after the first HTML tag
        $block_content = substr_replace($block_content, ' ' . $attribute_name . '="' . esc_attr($attribute_value) . '"', $pos, 0);
      }
  
      return $block_content;
    }

    /**
     * Render block
     */
    public function render_block($block_content, $block) {
      if (
        empty($block['attrs']['responsiveControls']) ||
        !$this->is_supported($block['blockName'])
      ) {
        return $block_content;
      }

      $responsive_controls = $block['attrs']['responsiveControls'];
      $desktop_attr = $responsive_controls;
      // Bail early if there's no valid root opening tag.
      if (!preg_match('/^\s*<[^>]+>/s', $block_content, $root_match)) {
        return $block_content;
      }

      // Handle WP_HTML_Tag_Processor in one context
      $tag_processor = new WP_HTML_Tag_Processor($block_content);
      $tag_found = false;

      switch ($block['blockName']) {
        case 'core/image':
          $tag_found = $tag_processor->next_tag('img');
          break;
        case 'core/table':
          $tag_found = $tag_processor->next_tag('table');
          break;
        default:
          $tag_found = $tag_processor->next_tag();
      }

      if (!$tag_found) {
        return $block_content;
      }

      // adding width to column block sets flex-basis property.
      if (!empty($block['attrs']['width'])) {
        $tag_processor->add_class('ucla-has-custom-width');
      }
      // inline order
      if (!empty($block['attrs']['responsiveControls']['order'])) {
        $existing_style = $tag_processor->get_attribute('style');
        $tag_processor->set_attribute(
          'style',
          'order:' . $block['attrs']['responsiveControls']['order'] . ';' . $existing_style
        );
      }

      // overlay color
      if (isset($block['attrs']['isUserOverlayColor'])) {
        if ($block['attrs']['isUserOverlayColor'] && !empty($block['attrs']['customOverlayColor'])) {
          
          $tag_processor->next_tag(array('class_name'=> 'has-background-dim'));
          
          $tag_processor->set_attribute(
            'style',
            'background-color:' . $block['attrs']['customOverlayColor'] . ';'
          );
        }
      }

      // Handle tablet/mobile breakpoint class additions
      foreach (['tablet' => 'md', 'mobile' => 'sm'] as $device => $suffix) {
        if (!empty($responsive_controls[$device]['borderColor'])) {
          $class = 'has-' . $responsive_controls[$device]['borderColor'] . "-border-color-{$suffix}";
          $tag_processor->add_class($class);
        }
      }

      // Handle desktop border color override
      if (!empty($responsive_controls['borderColor']) && !empty($block['attrs']['borderColor'])) {
        $original = 'has-' . $block['attrs']['borderColor'] . '-border-color';
        $actual = 'has-' . $responsive_controls['borderColor'] . '-border-color';
        $tag_processor->remove_class($original);
        $tag_processor->add_class($actual);
      }

      // Finalize content from processor
      $block_content = $tag_processor->get_updated_html();

      // Add unique responsive ID
      $responsive_control_id = wp_unique_id('ucla-responsive-');
      $block_content = $this->add_custom_attribute($block_content, 'data-responsive-id', $responsive_control_id);

      preg_match('/data-responsive-id="([^"]*)"/', $block_content, $matches);
      if (!isset($matches[1])) {
        return $block_content;
      }

      // Extract ID and build key
      $styles_id = $matches[1];
      $key = hash('crc32', $styles_id);
      $block_content = str_replace($matches[0], 'ucla-block-id="' . $key . '"', $block_content);

      // Track classes and inline styles
      preg_match('/class="([^"]*)"/', $block_content, $class_match);
      preg_match('/style="([^"]*)"/', $block_content, $style_match);

      if (!empty($class_match[1])) {
        $classes = explode(' ', trim($class_match[1]));
        $this->class_names[$key] = '.' . implode('.', $classes);
      }

      if (!empty($style_match[1])) {
        // Clean out non-desktop keys before generating styles
        unset($desktop_attr['tablet'], $desktop_attr['mobile']);
        
        $desktopMinHeight = $desktop_attr['minHeight'] ?? null;
        $desktopMinHeightUnit = $desktop_attr['minHeightUnit'] ?? null;
        $desktopOrder = $desktop_attr['order'] ?? null;

        // Compile desktop styles with !important if needed
        $desktop_styles = wp_style_engine_get_styles($desktop_attr);

        if (!empty($block['attrs']['width'])) {
          $desktop_styles['css'] = ($desktop_styles['css'] ?? '') . "flex-basis:{$block['attrs']['width']};";
        }

        if ($desktopMinHeight) {
          $desktop_styles['css'] = ($desktop_styles['css'] ?? '') . "min-height:{$desktopMinHeight}" . ($desktopMinHeightUnit ? $desktopMinHeightUnit : 'px') . ';';
        }
        if ($desktopOrder) {
          $desktop_styles['css'] = ($desktop_styles['css'] ?? '') . "order:{$desktopOrder};";
        }
        if (!empty($desktop_styles['css'])) {
          $this->styles[$key]['desktop'] = $this->skip_important_css_prop($desktop_styles['css'], $block['blockName']);
        }

        // Strip original inline style
        $block_content = str_replace($style_match[0], '', $block_content);
      }

      // Tablet/Mobile styles
      foreach (['tablet', 'mobile'] as $device) {
        if (empty($responsive_controls[$device])) {
          continue;
        }

        $styles = $responsive_controls[$device];

        $order = $styles['order'] ?? null;
        $responsiveMinHeight = $styles['minHeight'] ?? null;
        $responsiveMinHeightUnit = $styles['minHeightUnit'] ?? null;

        unset($styles['order'], $styles['minHeight'], $styles['minHeightUnit']);

        $compiled = wp_style_engine_get_styles($styles);
        if ($order) {
          $compiled['css'] = ($compiled['css'] ?? '') . "order:{$order};";
        }
        if ($responsiveMinHeight) {
          $compiled['css'] = ($compiled['css'] ?? '') . "min-height:{$responsiveMinHeight}" . ($responsiveMinHeightUnit ? $responsiveMinHeightUnit : 'px') . ';';
        }

        if (!empty($compiled['css'])) {
          $css = $this->skip_important_css_prop($compiled['css'], $block['blockName']);
          $this->styles[$key][$device] = $css;
        }
      }

      return $block_content;
    }

    /**
     * Helper function to build the full CSS selector based on block ID and class name
     * 
     * @access public
     * @return string
     */
    public function build_selector( string $key, string $class_name = '' ): string {
      $selector = '';

      if ( $class_name ) {
          $selector .= $class_name;
      }

      $selector .= '[ucla-block-id="' . $key . '"]';

      if ( $class_name ) {
          if ( str_contains($class_name, 'wp-block-image') ) {
              $selector .= '>img';
          }

          if ( str_contains($class_name, 'wp-block-table') ) {
              $selector .= '>table,' . $class_name . '>table td,' . $class_name . '>table th';
          }

          if (
              preg_match('/\bwp-block-button\b/', $class_name) &&
              !preg_match('/\bwp-block-buttons\b/', $class_name)
          ) {
              $selector .= '>.wp-block-button__link';
          }
      }

      return $selector;
    }

    /**
     * Remove redundant responsive styles that duplicate desktop/tablet.
     *
     * @param array $styles
     * @return array
     */
    private function dedupe_responsive_styles(array $styles): array {
      // echo '<pre id="dedupe_responsive_styles">';
      // var_dump($styles);
      // echo '</pre>';

      $normalize = function (?string $css): ?string {
          if (!$css) {
              return null;
          }
          $rules = array_filter(array_map('trim', explode(';', $css)));
          sort($rules); // ensure consistent comparison regardless of order
          return implode(';', $rules) . ';';
      };

      foreach ($styles as $key => &$style) {
          $desktop = $normalize($style['desktop'] ?? null);
          $tablet  = $normalize($style['tablet'] ?? null);
          $mobile  = $normalize($style['mobile'] ?? null);

          // Collapse tablet if identical to desktop
          if ($tablet && $desktop && $tablet === $desktop) {
              unset($style['tablet']);
          }

          // Collapse mobile if identical to tablet (after collapse) or desktop
          if ($mobile) {
              if (($tablet && $mobile === $tablet) || ($desktop && $mobile === $desktop)) {
                  unset($style['mobile']);
              }
          }
      }

      return $styles;
    }

    /**
     * Print block styles.
     * 
     * @access public
     * @return void
     */
    public function print_styles() {
      // Early exit if there are no inline styles.

      // echo '<pre>';
      // var_dump($this->styles);
      // echo '</pre>';
      
      if ( empty( $this->styles ) ) {
          return;
      }

      $this->styles = $this->dedupe_responsive_styles($this->styles);

      $breakpoints = [
        'desktop' => null,
        'tablet'  => '@media screen and (max-width:781px)',
        'mobile'  => '@media screen and (max-width:600px)',
      ];

      echo '<style id="block-inline-styles">';

      foreach ($this->styles as $key => $style) {

        $selector = $this->build_selector($key, $this->class_names[$key] ?? '');

        foreach($breakpoints as $device => $mediaQuery) {
          if (!isset($style[$device])) {
            continue;
          }

          // Add default border style if border-width is set without border-style
          $css = $style[$device];
            if (str_contains($css, 'border-width') && !str_contains($css, 'border-style')) {
                $css .= 'border-style:solid;';
            }

            if ($mediaQuery) {
                echo $mediaQuery . '{';
            }
            echo $selector . '{' . $css . '}';
            if ($mediaQuery) {
                echo '}';
            }
        }
      }

      echo '</style>';
    }
  }
  Ucla_Responsive_Block_Controls::get_instance();
}
