<?php

/**
 * Product Modifiers Class
 *
 * @package Pixeldev\SquareWooSync\Modifiers
 */

namespace Pixeldev\SquareWooSync\Modifiers;

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

/**
 * Class ProductModifiers
 *
 * Provides a "Square-like" modifier system for WooCommerce products,
 * allowing admin-defined per-product modifier sets with optional stock and price adjustments.
 */
class ProductModifiers
{

  /**
   * Constructor.
   *
   * Hooks into various WooCommerce actions/filters to register
   * the per-product modifier sets functionality.
   */
  public function __construct()
  {
    // Admin product data tab/panel.
    add_filter('woocommerce_product_data_tabs', [$this, 'add_modifiers_product_data_tab']);
    add_action('woocommerce_product_data_panels', [$this, 'add_modifiers_product_data_fields']);

    // Admin scripts and data loading.
    add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
    add_action('admin_print_footer_scripts', [$this, 'load_modifier_sets_into_js']);

    // Save product meta (modifier sets).
    add_action('save_post', [$this, 'save_product_modifiers']);

    // Frontend display.
    add_action('woocommerce_before_add_to_cart_button', [$this, 'display_product_modifiers']);

    // Cart item data handling.
    add_filter('woocommerce_add_cart_item_data', [$this, 'add_modifiers_to_cart_item_data'], 10, 2);
    add_action('woocommerce_before_calculate_totals', [$this, 'adjust_cart_item_price'], 20);

    // Display modifiers in cart/checkout.
    add_filter('woocommerce_get_item_data', [$this, 'display_modifiers_in_cart'], 10, 2);

    // Add modifiers to order items & reduce modifier stock.
    add_action('woocommerce_checkout_create_order_line_item', [$this, 'add_modifiers_to_order_line_item'], 10, 4);
  }

  /**
   * Adds a new "Modifiers" tab in the WooCommerce product data tabs.
   *
   * @param array $tabs Existing product data tabs.
   *
   * @return array Modified product data tabs.
   */
  public function add_modifiers_product_data_tab($tabs)
  {
    $tabs['pws_modifiers'] = [
      'label'  => __('Modifiers', 'square-woo-sync-pro'),
      'target' => 'pws_modifiers_product_data',
      'class'  => [],
    ];

    return $tabs;
  }

  /**
   * Outputs the content for the custom "Modifiers" panel in the product data section.
   *
   * @return void
   */
  public function add_modifiers_product_data_fields()
  {
?>
    <div id="pws_modifiers_product_data" class="panel woocommerce_options_panel hidden">
      <div class="options_group">
        <p>
          <?php esc_html_e('Define one or more "modifier sets." Each set can have multiple options. If "Single Choice" is on, only one option can be chosen in that set.', 'square-woo-sync-pro'); ?>
        </p>

        <!-- Container for all sets -->
        <div id="pws_modifier_sets_container"></div>

        <!-- Button to add a new set -->
        <button type="button" class="button button-secondary" id="pws_add_set_btn">
          <?php esc_html_e('Add Modifier Set', 'square-woo-sync-pro'); ?>
        </button>
      </div>
    </div>
  <?php
  }

  /**
   * Enqueues the admin script and CSS for managing the nested repeater (modifier sets and options).
   *
   * @return void
   */
  public function admin_enqueue_scripts()
  {
    $screen = get_current_screen();

    if (! $screen) {
      return;
    }

    // Enqueue only on product edit/add screens.
    if ('product' === $screen->post_type && 'post' === $screen->base) {
      // Example: Enqueue a dedicated CSS file for styling if desired.
      wp_enqueue_style(
        'pws-modifiers-admin-styles',
        SQUAREWOOSYNC_URL . '/assets/styles/pws-modifiers-admin.css', // Adjust path if needed
        array(),
        '1.0'
      );

      // If you have a separate JS, enqueue here. Currently, we use inline JS below.
      // wp_enqueue_script(
      //     'pws-modifiers-admin',
      //     SQUAREWOOSYNC_URL . '/assets/js/pws-modifiers-admin.js',
      //     array( 'jquery' ),
      //     '1.0',
      //     true
      // );
    }
  }

  /**
   * Loads existing modifier sets data via inline JavaScript
   * so the nested repeater UI can be pre-populated.
   *
   * @return void
   */
  public function load_modifier_sets_into_js()
  {
    global $post;

    if (! isset($post->ID) || 'product' !== get_post_type($post->ID)) {
      return;
    }

    // Retrieve saved sets.
    $modifier_sets = get_post_meta($post->ID, '_pws_modifier_sets', true);
    if (! is_array($modifier_sets)) {
      $modifier_sets = [];
    }
  ?>
    <script type="text/javascript">
      jQuery(document).ready(function($) {

        var existingSets = <?php echo wp_json_encode($modifier_sets); ?>;
        var $container = $('#pws_modifier_sets_container');
        var setIndex = 0;

        /**
         * Render a single set row (including sub-table for options).
         */
        function renderSetRow(setData, index) {
          var setName = setData.set_name ? setData.set_name : '';
          var singleChoice = setData.single_choice ? 'checked' : '';
          var squareModListId = setData.square_mod_list_id ? setData.square_mod_list_id : '';
          var options = setData.options ? setData.options : [];

          var setHTML = `
						<div class="pws-modifier-set" data-set-index="${index}">
							<div class="pws-modifier-set-header">
								<div>
									<label><?php esc_html_e('Set Name:', 'square-woo-sync-pro'); ?></label><br/>
									<input type="text" name="pws_modifier_sets[${index}][set_name]" value="${setName}" placeholder="<?php esc_attr_e('e.g. Sauces', 'square-woo-sync-pro'); ?>"/>
								</div>
								<div>
									<label><?php esc_html_e('Square Mod List ID:', 'square-woo-sync-pro'); ?></label><br/>
									<input type="text" name="pws_modifier_sets[${index}][square_mod_list_id]" value="${squareModListId}" placeholder="<?php esc_attr_e('e.g. WEYL7DNTWBB4MQOIXMYWPK2J', 'square-woo-sync-pro'); ?>"/>
								</div>
								<div>
									<label style="display:block;"><?php esc_html_e('Single Choice?', 'square-woo-sync-pro'); ?></label>
									<input type="checkbox" name="pws_modifier_sets[${index}][single_choice]" value="1" ${singleChoice} />
								</div>
								<div>
									<button type="button" class="button link-delete pws-remove-set-btn"><?php esc_html_e('Remove Set', 'square-woo-sync-pro'); ?></button>
								</div>
							</div>

							<table class="widefat wc_input_table pws-options-table">
								<thead>
									<tr>
										<th><?php esc_html_e('Option Name', 'square-woo-sync-pro'); ?></th>
										<th><?php esc_html_e('Price', 'square-woo-sync-pro'); ?></th>
										<th><?php esc_html_e('Stock (optional)', 'square-woo-sync-pro'); ?></th>
										<th><?php esc_html_e('Square Modifier ID', 'square-woo-sync-pro'); ?></th>
										<th><?php esc_html_e('Actions', 'square-woo-sync-pro'); ?></th>
									</tr>
								</thead>
								<tbody></tbody>
							</table>
							<button type="button" class="button button-secondary pws-add-option-btn">
								<?php esc_html_e('Add Option', 'square-woo-sync-pro'); ?>
							</button>
						</div>
					`;

          $container.append(setHTML);

          var $thisSet = $container.find('.pws-modifier-set[data-set-index="' + index + '"]');
          var $tableBody = $thisSet.find('tbody');

          // Render each existing option
          $.each(options, function(optIndex, optData) {
            renderOptionRow($tableBody, index, optData);
          });
        }

        /**
         * Render a single option row in a set's table.
         */
        function renderOptionRow($tableBody, setIndex, optData) {
          var optionName = optData.name ? optData.name : '';
          var optionPrice = (typeof optData.price !== 'undefined') ? optData.price : '';
          var optionStock = (typeof optData.stock !== 'undefined') ? optData.stock : '';
          var optionSquareId = (typeof optData.square_mod_id !== 'undefined') ? optData.square_mod_id : '';

          var newRow = `
						<tr>
							<td>
								<input type="text" name="pws_modifier_sets[${setIndex}][options][name][]" value="${optionName}" placeholder="<?php esc_attr_e('e.g. Ketchup', 'square-woo-sync-pro'); ?>"/>
							</td>
							<td>
								<input type="number" step="0.01" name="pws_modifier_sets[${setIndex}][options][price][]" value="${optionPrice}" placeholder="0.00"/>
							</td>
							<td>
								<input type="number" step="1" name="pws_modifier_sets[${setIndex}][options][stock][]" value="${optionStock}" placeholder="<?php esc_attr_e('optional', 'square-woo-sync-pro'); ?>"/>
							</td>
							<td>
								<input type="text" name="pws_modifier_sets[${setIndex}][options][square_mod_id][]" value="${optionSquareId}" placeholder="<?php esc_attr_e('Square Modifier ID', 'square-woo-sync-pro'); ?>"/>
							</td>
							<td>
								<button type="button" class="button link-delete pws-remove-option-btn"><?php esc_html_e('Remove', 'square-woo-sync-pro'); ?></button>
							</td>
						</tr>
					`;
          $tableBody.append(newRow);
        }

        // Initial render of existing sets
        $.each(existingSets, function(i, setData) {
          renderSetRow(setData, setIndex);
          setIndex++;
        });

        // Add Set button
        $('#pws_add_set_btn').on('click', function() {
          var emptySetData = {
            set_name: '',
            square_mod_list_id: '',
            single_choice: false,
            options: []
          };
          renderSetRow(emptySetData, setIndex);
          setIndex++;
        });

        // Add Option button
        $(document).on('click', '.pws-add-option-btn', function() {
          var $set = $(this).closest('.pws-modifier-set');
          var sIndex = $set.data('set-index');
          var $tableBdy = $set.find('tbody');

          renderOptionRow($tableBdy, sIndex, {
            name: '',
            price: '',
            stock: '',
            square_mod_id: ''
          });
        });

        // Remove option row
        $(document).on('click', '.pws-remove-option-btn', function() {
          $(this).closest('tr').remove();
        });

        // Remove entire set
        $(document).on('click', '.pws-remove-set-btn', function() {
          $(this).closest('.pws-modifier-set').remove();
        });
      });
    </script>
<?php
  }

  /**
   * Saves product modifier sets meta when the product is saved/updated.
   *
   * @param int $post_id The product post ID.
   *
   * @return void
   */
  public function save_product_modifiers($post_id)
  {
    // Only save for products.
    if ('product' !== get_post_type($post_id)) {
      return;
    }

    // Check capabilities.
    if (! current_user_can('edit_post', $post_id)) {
      return;
    }

    $raw_data   = isset($_POST['pws_modifier_sets']) ? wp_unslash($_POST['pws_modifier_sets']) : [];
    $clean_sets = [];

    foreach ($raw_data as $set_index => $set_item) {
      // Set-level fields
      $set_name           = isset($set_item['set_name']) ? sanitize_text_field($set_item['set_name']) : '';
      $single_choice      = isset($set_item['single_choice']) ? true : false;
      $square_mod_list_id = isset($set_item['square_mod_list_id']) ? sanitize_text_field($set_item['square_mod_list_id']) : '';

      $options = [];
      if (isset($set_item['options']) && is_array($set_item['options'])) {
        $names          = isset($set_item['options']['name'])          ? $set_item['options']['name']          : [];
        $prices         = isset($set_item['options']['price'])         ? $set_item['options']['price']         : [];
        $stocks         = isset($set_item['options']['stock'])         ? $set_item['options']['stock']         : [];
        $square_mod_ids = isset($set_item['options']['square_mod_id']) ? $set_item['options']['square_mod_id'] : [];

        foreach ($names as $opt_i => $opt_name) {
          $opt_name       = sanitize_text_field($opt_name);
          $opt_price      = ('' !== $prices[$opt_i]) ? floatval($prices[$opt_i]) : 0.0;
          $opt_stock      = ('' !== $stocks[$opt_i]) ? intval($stocks[$opt_i]) : '';
          $opt_sq_mod_id  = isset($square_mod_ids[$opt_i]) ? sanitize_text_field($square_mod_ids[$opt_i]) : '';

          // If name & price are both blank, skip to avoid empty rows
          if ('' === $opt_name && 0 === $opt_price && '' === $opt_sq_mod_id) {
            continue;
          }

          $options[] = [
            'name'           => $opt_name,
            'price'          => $opt_price,
            'stock'          => $opt_stock,
            'square_mod_id'  => $opt_sq_mod_id,
          ];
        }
      }

      $clean_sets[] = [
        'set_name'           => $set_name,
        'single_choice'      => $single_choice,
        'square_mod_list_id' => $square_mod_list_id,
        'options'            => $options,
      ];
    }

    update_post_meta($post_id, '_pws_modifier_sets', $clean_sets);
  }

  /**
   * Displays the product modifier sets on the single product page.
   * Radio inputs for single-choice sets, checkboxes for multi-choice sets.
   *
   * @return void
   */
  public function display_product_modifiers()
  {
    global $product;

    if (! $product) {
      return;
    }

    $sets = get_post_meta($product->get_id(), '_pws_modifier_sets', true);
    if (! is_array($sets) || empty($sets)) {
      return;
    }

    echo '<div class="pws-modifier-sets">';
    echo '<h4>' . esc_html__('Choose Modifiers', 'square-woo-sync-pro') . '</h4>';

    foreach ($sets as $set_index => $set_data) {
      $set_name      = isset($set_data['set_name']) ? $set_data['set_name'] : '';
      $single_choice = ! empty($set_data['single_choice']);
      $options       = isset($set_data['options']) ? $set_data['options'] : [];

      if (empty($options)) {
        continue;
      }

      $input_name_base = "pws_modifier_sets[{$set_index}]";

      echo '<div class="pws-modifier-set-frontend">';
      printf('<h5>%s</h5>', esc_html($set_name));

      foreach ($options as $opt_i => $opt) {
        $opt_name  = isset($opt['name'])  ? $opt['name']  : '';
        $opt_price = isset($opt['price']) ? floatval($opt['price']) : 0;
        $opt_stock = isset($opt['stock']) ? $opt['stock'] : '';

        $disabled = (is_numeric($opt_stock) && (int) $opt_stock <= 0) ? 'disabled' : '';

        $sign       = $opt_price >= 0 ? '+' : '';
        $price_text = sprintf('%s%.2f', $sign, $opt_price);

        // Single vs multiple choice
        $type       = $single_choice ? 'radio' : 'checkbox';
        $input_name = $single_choice ? $input_name_base : $input_name_base . '[]';

        echo '<label class="pws-modifier-option-label">';
        echo '<input type="' . esc_attr($type) . '" '
          . 'value="' . esc_attr($opt_name) . '" '
          . 'data-price="' . esc_attr($opt_price) . '" '
          . $disabled
          . ' name="' . esc_attr($input_name) . '"> ';

        // e.g. "Ketchup (+1.00)"
        printf(
          '%s (%s)',
          esc_html($opt_name),
          esc_html($price_text)
        );

        // If there's a stock value, show " [In stock: X]"
        if (is_numeric($opt_stock) && (int) $opt_stock > 0) {
          echo ' <small>('
            . sprintf(
              /* translators: %d: stock quantity */
              __('In stock: %d', 'square-woo-sync-pro'),
              intval($opt_stock)
            )
            . ')</small>';
        }
        echo '</label>';
      }
      echo '</div>';
    }

    echo '</div>';
  }

  /**
   * Captures selected modifier sets & options in cart item data when the product is added to the cart.
   *
   * @param array $cart_item_data Existing cart item data.
   * @param int   $product_id     The product ID.
   *
   * @return array Filtered cart item data.
   */
  public function add_modifiers_to_cart_item_data($cart_item_data, $product_id)
  {
    if (isset($_POST['pws_modifier_sets']) && is_array($_POST['pws_modifier_sets'])) {
      $raw = wc_clean(wp_unslash($_POST['pws_modifier_sets']));
      $cart_item_data['pws_chosen_sets'] = $raw;
    }
    return $cart_item_data;
  }

  /**
   * Adjusts the cart item price based on selected modifiers.
   *
   * @param \WC_Cart $cart The WooCommerce cart object.
   *
   * @return void
   */
  public function adjust_cart_item_price($cart)
  {
    if (is_admin() && ! defined('DOING_AJAX')) {
      return;
    }
    if (did_action('woocommerce_before_calculate_totals') >= 2) {
      return;
    }
    if (! is_object($cart)) {
      return;
    }

    foreach ($cart->get_cart() as $cart_item) {
      if (! empty($cart_item['pws_chosen_sets'])) {
        $chosen_sets    = $cart_item['pws_chosen_sets'];
        $product_id     = $cart_item['product_id'];
        $available_sets = get_post_meta($product_id, '_pws_modifier_sets', true);
        if (! is_array($available_sets)) {
          $available_sets = [];
        }

        $sets_lookup = [];
        foreach ($available_sets as $s_i => $s_data) {
          $set_options = [];
          if (! empty($s_data['options']) && is_array($s_data['options'])) {
            foreach ($s_data['options'] as $opt) {
              if (isset($opt['name'], $opt['price'])) {
                $set_options[$opt['name']] = floatval($opt['price']);
              }
            }
          }
          $sets_lookup[$s_i] = [
            'set_name' => $s_data['set_name'] ?? '',
            'single'   => ! empty($s_data['single_choice']),
            'options'  => $set_options,
          ];
        }

        $base_price  = (float) $cart_item['data']->get_price();
        $price_extra = 0.0;

        foreach ($chosen_sets as $set_index => $selected) {
          if (! isset($sets_lookup[$set_index])) {
            continue;
          }
          $option_map = $sets_lookup[$set_index]['options'];

          if (is_array($selected)) {
            // multiple
            foreach ($selected as $opt_name) {
              if (isset($option_map[$opt_name])) {
                $price_extra += $option_map[$opt_name];
              }
            }
          } else {
            // single
            $opt_name = $selected;
            if (isset($option_map[$opt_name])) {
              $price_extra += $option_map[$opt_name];
            }
          }
        }

        $new_price = $base_price + $price_extra;
        $cart_item['data']->set_price($new_price);
      }
    }
  }

  /**
   * Displays selected modifiers in the cart and checkout page.
   *
   * @param array $item_data Cart item data array.
   * @param array $cart_item The cart item array.
   *
   * @return array Modified cart item data with our modifiers info.
   */
  public function display_modifiers_in_cart($item_data, $cart_item)
  {
    if (isset($cart_item['pws_chosen_sets']) && ! empty($cart_item['pws_chosen_sets'])) {
      $chosen_sets    = $cart_item['pws_chosen_sets'];
      $available_sets = get_post_meta($cart_item['product_id'], '_pws_modifier_sets', true);
      if (! is_array($available_sets)) {
        $available_sets = [];
      }

      foreach ($chosen_sets as $set_index => $selection) {
        $set_label = isset($available_sets[$set_index]['set_name'])
          ? $available_sets[$set_index]['set_name']
          : sprintf(__('Set #%d', 'square-woo-sync-pro'), $set_index + 1);

        if (is_array($selection)) {
          $value_str = implode(', ', array_map('esc_html', $selection));
        } else {
          $value_str = esc_html($selection);
        }

        $item_data[] = [
          'key'   => $set_label,
          'value' => $value_str,
        ];
      }
    }
    return $item_data;
  }

  /**
   * Adds the selected modifiers to the order line item meta
   * AND reduces the stock for each chosen modifier (if any stock is defined).
   *
   * @param \WC_Order_Item_Product $item          The order item.
   * @param string                 $cart_item_key The cart item key.
   * @param array                  $values        Cart item values.
   * @param \WC_Order             $order         The order object.
   *
   * @return void
   */
  public function add_modifiers_to_order_line_item($item, $cart_item_key, $values, $order)
  {
    if (empty($values['pws_chosen_sets'])) {
      return;
    }

    $chosen_sets = $values['pws_chosen_sets'];
    $product_id  = $values['product_id'];
    $quantity    = ($item->get_quantity()) ? $item->get_quantity() : 1;

    // Retrieve the sets from the product meta.
    $available_sets = get_post_meta($product_id, '_pws_modifier_sets', true);
    if (! is_array($available_sets)) {
      $available_sets = [];
    }

    $display_data     = [];
    $new_modifiers    = []; // <-- We'll collect data for the "new" approach here.

    foreach ($chosen_sets as $set_index => $selection) {
      if (! isset($available_sets[$set_index])) {
        continue;
      }

      $set_name  = $available_sets[$set_index]['set_name'] ?? '';
      $options   = &$available_sets[$set_index]['options']; // reference
      $is_array  = is_array($selection);
      $chosen    = $is_array ? $selection : [$selection];

      $chosen_names = [];

      foreach ($chosen as $opt_name) {
        // Find the chosen option in $options
        foreach ($options as &$opt) {
          if (isset($opt['name']) && $opt['name'] === $opt_name) {
            $chosen_names[] = $opt_name;

            // Reduce stock if numeric
            if (is_numeric($opt['stock']) && $opt['stock'] > 0) {
              $opt['stock'] = max(0, (int) $opt['stock'] - $quantity);
            }

            // ----------------------------------------------------------------
            // Here we store the Square modifier ID for this selected option
            // along with its price, if any, so getOrderLineItems can use it.
            // ----------------------------------------------------------------
            if (! empty($opt['square_mod_id'])) {
              $new_modifiers[] = [
                'square_mod_id' => $opt['square_mod_id'],
                'name'          => $opt_name,
                'price'         => isset($opt['price']) ? (float) $opt['price'] : 0.0,
              ];
            }

            break;
          }
        }
      }

      if (! empty($chosen_names)) {
        $display_data[] = sprintf(
          __('%1$s: %2$s', 'square-woo-sync-pro'),
          $set_name ? esc_html($set_name) : sprintf(__('Set #%d', 'square-woo-sync-pro'), $set_index + 1),
          esc_html(implode(', ', $chosen_names))
        );
      }
    }

    // 1) Add meta data to the order item for clarity (your existing logic)
    if ($display_data) {
      $item->add_meta_data('Modifiers', implode(' | ', $display_data));
    }

    // 2) Save the new modifiers array so we can read it later in getOrderLineItems
    if (! empty($new_modifiers)) {
      $item->add_meta_data('_pws_square_modifiers', $new_modifiers);
    }

    // 3) Re-save the product meta with updated stock (as you already do)
    update_post_meta($product_id, '_pws_modifier_sets', $available_sets);
  }
}
