/**
 * Opus Price Builder - Web Component
 *
 * Custom element for displaying WooCommerce prices with dynamic layouts.
 * Usage: <oppb-price data-product-id="123" data-scenario="sale" ...></oppb-price>
 *
 * @package OpusPriceBuilder
 */

(function () {
  "use strict";

  // Configuration
  const CONFIG = {
    maxRetries: 10,
    retryDelay: 100, // ms
    debug: false, // Set to true for console logging
  };

  /**
   * Log debug messages if debug mode is enabled
   */
  function debugLog(...args) {
    if (CONFIG.debug || window.oppbDebug) {
      console.log('[OPPB Price]', ...args);
    }
  }

  /**
   * OppbPrice Web Component
   * Renders price displays based on exported layouts from Opus Price Builder
   */
  class OppbPrice extends HTMLElement {
    // Observed attributes for reactive updates
    static get observedAttributes() {
      return [
        "data-product-id",
        "data-scenario",
        "data-location",
        "data-regular-price",
        "data-sale-price",
        "data-currency-symbol",
        "data-discount-percentage",
        "data-layout-id",
        "data-decimal-separator",
        "data-thousand-separator",
        // Variable price range attributes
        "data-min-price",
        "data-max-price",
        "data-min-regular-price",
        "data-max-regular-price",
        "data-min-sale-price",
        "data-max-sale-price",
      ];
    }

    constructor() {
      super();

      // Create shadow DOM for style encapsulation
      this.attachShadow({ mode: "open" });

      // Internal state
      this._data = {
        productId: null,
        scenario: "simple",
        location: "single_product",
        regularPrice: "0.00",
        salePrice: null,
        currencySymbol: "$",
        discountPercentage: null,
        layoutId: null,
        decimalSeparator: ".",
        thousandSeparator: ",",
        // Variable price range data
        minPrice: null,
        maxPrice: null,
        minRegularPrice: null,
        maxRegularPrice: null,
        minSalePrice: null,
        maxSalePrice: null,
      };

      // Render state
      this._rendered = false;
      this._retryCount = 0;

      // Cache for layout templates
      this._layoutCache = new Map();
    }

    /**
     * Called when element is added to DOM
     */
    connectedCallback() {
      this._parseAttributes();
      this._renderWithRetry();
    }

    /**
     * Called when observed attribute changes
     */
    attributeChangedCallback(name, oldValue, newValue) {
      if (oldValue !== newValue) {
        this._parseAttributes();
        this._render();
      }
    }

    /**
     * Parse data attributes into internal state
     */
    _parseAttributes() {
      this._data = {
        productId: this.getAttribute("data-product-id"),
        scenario: this.getAttribute("data-scenario") || "simple",
        location: this.getAttribute("data-location") || "single_product",
        regularPrice: this.getAttribute("data-regular-price") || "0.00",
        salePrice: this.getAttribute("data-sale-price"),
        currencySymbol: this.getAttribute("data-currency-symbol") || "$",
        discountPercentage: this.getAttribute("data-discount-percentage"),
        layoutId: this.getAttribute("data-layout-id"),
        decimalSeparator: this.getAttribute("data-decimal-separator") || ".",
        thousandSeparator: this.getAttribute("data-thousand-separator") || ",",
        // Variable price range data
        minPrice: this.getAttribute("data-min-price"),
        maxPrice: this.getAttribute("data-max-price"),
        minRegularPrice: this.getAttribute("data-min-regular-price"),
        maxRegularPrice: this.getAttribute("data-max-regular-price"),
        minSalePrice: this.getAttribute("data-min-sale-price"),
        maxSalePrice: this.getAttribute("data-max-sale-price"),
      };
    }

    /**
     * Render with retry mechanism for when layouts might not be loaded yet
     */
    _renderWithRetry() {
      // Check if layouts data is available
      if (window.oppbLayouts && Object.keys(window.oppbLayouts).length > 0) {
        // Layouts available - render immediately
        this._render();
        return;
      }

      // Layouts not loaded yet - the inner HTML serves as fallback
      // Try rendering with default layout, or retry if layouts might still be loading
      if (window.oppbLayoutsLoaded === true) {
        // Layouts were loaded but empty - use default
        this._render();
        return;
      }

      // Still waiting for layouts - retry
      if (this._retryCount < CONFIG.maxRetries) {
        this._retryCount++;
        
        setTimeout(() => {
          this._renderWithRetry();
        }, CONFIG.retryDelay);
      } else {
        // Max retries reached - render with default layout
        debugLog('Max retries reached, using default layout', {
          scenario: this._data.scenario,
          location: this._data.location,
        });
        this._render();
      }
    }

    /**
     * Get fallback HTML using the inner content or price data
     */
    _getFallbackHtml() {
      // First, try to use any inner HTML as fallback
      const innerHtml = this.innerHTML.trim();
      if (innerHtml) {
        return innerHtml;
      }
      
      // Otherwise build from data attributes
      const { currencySymbol, regularPrice, salePrice } = this._data;
      
      if (salePrice) {
        return `<del>${currencySymbol}${regularPrice}</del> <ins>${currencySymbol}${salePrice}</ins>`;
      }
      
      return `${currencySymbol}${regularPrice}`;
    }

    /**
     * Split price into main and decimal parts
     * Uses WooCommerce separator settings passed via data attributes
     * @param {string} price - The price string (e.g., "4.525,99" or "4,525.99")
     * @returns {Object} - {main: "4.525", decimal: "99", separator: ","}
     */
    _splitPrice(price) {
      const decSep = this._data.decimalSeparator || ".";
      const thousandSep = this._data.thousandSeparator || ",";
      
      if (!price) return {main: "0", decimal: "00", separator: decSep};

      // Remove currency symbols and whitespace, keep digits and separators
      const cleanPrice = String(price).replace(/[^0-9.,\s]/g, "").trim();
      
      // Split by decimal separator to get main and decimal parts
      // The decimal separator is the one from WooCommerce settings
      let parts;
      if (decSep && cleanPrice.includes(decSep)) {
        parts = cleanPrice.split(decSep);
      } else {
        // Fallback: no decimal separator found, treat entire value as main
        parts = [cleanPrice, "00"];
      }

      // Main part keeps thousand separators intact for display
      return {
        main: parts[0] || "0",
        decimal: parts[1] || "00",
        separator: decSep,
      };
    }

    /**
     * Calculate discount percentage
     * @returns {string|null}
     */
    _calculateDiscount() {
      if (this._data.discountPercentage) {
        return this._data.discountPercentage;
      }

      if (this._data.regularPrice && this._data.salePrice) {
        const regular = parseFloat(
          this._data.regularPrice.replace(/[^0-9.]/g, "")
        );
        const sale = parseFloat(this._data.salePrice.replace(/[^0-9.]/g, ""));

        if (regular > 0 && sale < regular) {
          const discount = Math.round(((regular - sale) / regular) * 100);
          return String(discount);
        }
      }

      return null;
    }

    /**
     * Replace placeholders in HTML template
     * @param {string} html - HTML template with placeholders
     * @returns {string} - Processed HTML
     */
    _processTemplate(html) {
      const regularParts = this._splitPrice(this._data.regularPrice);
      const saleParts = this._data.salePrice
        ? this._splitPrice(this._data.salePrice)
        : null;
      const discount = this._calculateDiscount();

      // Calculate saved amount (discount in currency)
      const savedAmount = this._data.regularPrice && this._data.salePrice
        ? (
            parseFloat(this._data.regularPrice.replace(/[^0-9.]/g, "")) -
            parseFloat(this._data.salePrice.replace(/[^0-9.]/g, ""))
          ).toFixed(2)
        : "";

      // Variable price range parts
      const minPriceParts = this._data.minPrice
        ? this._splitPrice(this._data.minPrice)
        : null;
      const maxPriceParts = this._data.maxPrice
        ? this._splitPrice(this._data.maxPrice)
        : null;
      const minRegularParts = this._data.minRegularPrice
        ? this._splitPrice(this._data.minRegularPrice)
        : null;
      const maxRegularParts = this._data.maxRegularPrice
        ? this._splitPrice(this._data.maxRegularPrice)
        : null;
      const minSaleParts = this._data.minSalePrice
        ? this._splitPrice(this._data.minSalePrice)
        : null;
      const maxSaleParts = this._data.maxSalePrice
        ? this._splitPrice(this._data.maxSalePrice)
        : null;

      const replacements = {
        // Product information
        "{productId}": this._data.productId || "",
        "{productID}": this._data.productId || "", // Alias for consistency
        "{productName}": this.getAttribute("data-product-name") || "",
        
        // Regular price (full and parts)
        "{regularPrice}":
          this._data.currencySymbol +
          regularParts.main +
          regularParts.separator +
          regularParts.decimal,
        "{regularPriceMain}": regularParts.main,
        "{regularPriceDecimal}": regularParts.decimal,
        "{regularPriceSeparator}": regularParts.separator,
        
        // Sale price (full and parts)
        "{salePrice}": saleParts
          ? this._data.currencySymbol +
            saleParts.main +
            saleParts.separator +
            saleParts.decimal
          : "",
        "{salePriceMain}": saleParts ? saleParts.main : "",
        "{salePriceDecimal}": saleParts ? saleParts.decimal : "",
        "{salePriceSeparator}": saleParts ? saleParts.separator : "",
        
        // Variable price range - min/max current prices
        "{minPrice}": minPriceParts
          ? this._data.currencySymbol +
            minPriceParts.main +
            minPriceParts.separator +
            minPriceParts.decimal
          : "",
        "{minPriceMain}": minPriceParts ? minPriceParts.main : "",
        "{minPriceDecimal}": minPriceParts ? minPriceParts.decimal : "",
        "{minPriceSeparator}": minPriceParts ? minPriceParts.separator : "",
        "{maxPrice}": maxPriceParts
          ? this._data.currencySymbol +
            maxPriceParts.main +
            maxPriceParts.separator +
            maxPriceParts.decimal
          : "",
        "{maxPriceMain}": maxPriceParts ? maxPriceParts.main : "",
        "{maxPriceDecimal}": maxPriceParts ? maxPriceParts.decimal : "",
        "{maxPriceSeparator}": maxPriceParts ? maxPriceParts.separator : "",
        
        // Variable price range - min/max regular prices (before sale)
        "{minRegularPrice}": minRegularParts
          ? this._data.currencySymbol +
            minRegularParts.main +
            minRegularParts.separator +
            minRegularParts.decimal
          : "",
        "{minRegularPriceMain}": minRegularParts ? minRegularParts.main : "",
        "{minRegularPriceDecimal}": minRegularParts ? minRegularParts.decimal : "",
        "{maxRegularPrice}": maxRegularParts
          ? this._data.currencySymbol +
            maxRegularParts.main +
            maxRegularParts.separator +
            maxRegularParts.decimal
          : "",
        "{maxRegularPriceMain}": maxRegularParts ? maxRegularParts.main : "",
        "{maxRegularPriceDecimal}": maxRegularParts ? maxRegularParts.decimal : "",
        
        // Variable price range - min/max sale prices
        "{minSalePrice}": minSaleParts
          ? this._data.currencySymbol +
            minSaleParts.main +
            minSaleParts.separator +
            minSaleParts.decimal
          : "",
        "{minSalePriceMain}": minSaleParts ? minSaleParts.main : "",
        "{minSalePriceDecimal}": minSaleParts ? minSaleParts.decimal : "",
        "{maxSalePrice}": maxSaleParts
          ? this._data.currencySymbol +
            maxSaleParts.main +
            maxSaleParts.separator +
            maxSaleParts.decimal
          : "",
        "{maxSalePriceMain}": maxSaleParts ? maxSaleParts.main : "",
        "{maxSalePriceDecimal}": maxSaleParts ? maxSaleParts.decimal : "",
        
        // Currency
        "{currencySymbol}": this._data.currencySymbol,
        
        // Discount - multiple aliases for flexibility
        "{discount}": discount || "", // Just the number (e.g., "20")
        "{discountPercentage}": discount || "", // Alias
        "{discountAmount}": savedAmount ? this._data.currencySymbol + savedAmount : "", // With currency (e.g., "$10.00")
        "{discountAmountRaw}": savedAmount, // Just the number (e.g., "10.00")
        
        // Savings - aliases for discount amount
        "{savedAmount}": savedAmount ? this._data.currencySymbol + savedAmount : "",
        "{savedAmountRaw}": savedAmount,
        "{savings_amount}": savedAmount ? this._data.currencySymbol + savedAmount : "", // Snake case alias
        
        // Original price alias (same as regular price)
        "{original_price}": 
          this._data.currencySymbol +
          regularParts.main +
          regularParts.separator +
          regularParts.decimal,
        
        // Additional product info
        "{taxLabel}": this.getAttribute("data-tax-label") || "",
        "{stockStatus}": this.getAttribute("data-stock-status") || "",
        "{sku}": this.getAttribute("data-sku") || "",
      };

      let processed = html;
      for (const [placeholder, value] of Object.entries(replacements)) {
        processed = processed.replace(new RegExp(placeholder, "g"), value);
      }

      return processed;
    }

    /**
     * Get layout template from global registry or default
     * @returns {Object} - {html: string, css: string, customJs?: string, _isCustomLayout?: boolean}
     */
    _getLayout() {
      const layoutId = this._data.layoutId;
      const scenario = this._data.scenario;
      const location = this._data.location || this.getAttribute("data-location") || "single_product";

      // Check global layout registry (populated by PHP)
      if (window.oppbLayouts && Object.keys(window.oppbLayouts).length > 0) {
        // Try specific layout ID first
        if (layoutId && window.oppbLayouts[layoutId] && window.oppbLayouts[layoutId].html) {
          debugLog('Using layout by ID:', layoutId);
          return { ...window.oppbLayouts[layoutId], _isCustomLayout: true };
        }

        // Try scenario+location specific key (e.g., "simple_loop")
        const locationKey = scenario + "_" + location;
        if (window.oppbLayouts[locationKey] && window.oppbLayouts[locationKey].html) {
          debugLog('Using layout by location key:', locationKey);
          return { ...window.oppbLayouts[locationKey], _isCustomLayout: true };
        }

        // Try scenario default (backward compatibility)
        if (window.oppbLayouts[scenario] && window.oppbLayouts[scenario].html) {
          debugLog('Using layout by scenario:', scenario);
          return { ...window.oppbLayouts[scenario], _isCustomLayout: true };
        }

        // Try generic default
        if (window.oppbLayouts.default && window.oppbLayouts.default.html) {
          debugLog('Using default layout');
          return { ...window.oppbLayouts.default, _isCustomLayout: true };
        }
      }

      // Return fallback layout
      debugLog('Using fallback layout for:', { scenario, location, availableLayouts: window.oppbLayouts ? Object.keys(window.oppbLayouts) : [] });
      return this._getDefaultLayout();
    }

    /**
     * Get default fallback layout
     * @returns {Object}
     */
    _getDefaultLayout() {
      const isSale =
        this._data.scenario === "sale" ||
        this._data.scenario === "variable_sale";

      if (isSale && this._data.salePrice) {
        return {
          html: `
            <div class="oppb-price-container oppb-price--sale">
              <span class="oppb-regular-price">{regularPrice}</span>
              <span class="oppb-sale-price">{salePrice}</span>
            </div>
          `,
          css: `
            .oppb-price-container {
              display: inline-flex;
              align-items: baseline;
              gap: 8px;
              font-family: inherit;
            }
            .oppb-price--sale .oppb-regular-price {
              text-decoration: line-through;
              opacity: 0.7;
              font-size: 0.9em;
            }
            .oppb-sale-price {
              color: #22c55e;
              font-weight: 600;
            }
          `,
        };
      }

      return {
        html: `
          <div class="oppb-price-container">
            <span class="oppb-regular-price">{regularPrice}</span>
          </div>
        `,
        css: `
          .oppb-price-container {
            display: inline-flex;
            align-items: baseline;
            gap: 4px;
            font-family: inherit;
          }
          .oppb-regular-price {
            font-weight: 600;
          }
        `,
      };
    }

    /**
     * Render the component
     */
    _render() {
      const layout = this._getLayout();
      
      debugLog('Rendering:', {
        scenario: this._data.scenario,
        location: this._data.location,
        layoutId: this._data.layoutId,
        hasCustomLayout: !!layout?._isCustomLayout,
        htmlLength: layout?.html?.length || 0,
        availableLayouts: window.oppbLayouts ? Object.keys(window.oppbLayouts) : [],
      });
      
      const processedHtml = this._processTemplate(layout.html);

      // Build shadow DOM content - shadow DOM automatically hides light DOM content
      this.shadowRoot.innerHTML = `
        <style>
          :host {
            display: inline-block !important;
            visibility: visible !important;
          }
          * {
            box-sizing: border-box;
          }
          ${layout.css || ''}
        </style>
        ${processedHtml}
      `;

      this._rendered = true;

      // Execute custom JavaScript if provided
      if (layout.customJs) {
        this._executeCustomJs(layout.customJs);
      }

      // Dispatch render event for external integrations
      this.dispatchEvent(
        new CustomEvent("oppb-price-rendered", {
          bubbles: true,
          detail: {
            productId: this._data.productId,
            scenario: this._data.scenario,
            location: this._data.location,
            hasCustomLayout: !!layout._isCustomLayout,
          },
        })
      );
    }

    /**
     * Execute custom JavaScript in the context of this component
     * @param {string} script - The JavaScript code to execute
     */
    _executeCustomJs(script) {
      if (!script) return;
      
      try {
        // Get the price container element from shadow DOM
        const priceContainer = this.shadowRoot.querySelector('.oppb-price-container');
        if (!priceContainer) {
          console.warn('[OPPB Price] No price container found for custom JS');
          return;
        }
        
        // Create a function with 'priceContainer' available in scope
        // This matches the SSR behavior where priceContainer is also available
        const fn = new Function('priceContainer', script);
        fn.call(this, priceContainer);
      } catch (error) {
        console.error('[OPPB Price] Custom JS error:', error);
      }
    }

    /**
     * Public method to refresh the component
     */
    refresh() {
      this._parseAttributes();
      this._render();
    }

    /**
     * Public method to update data programmatically
     * @param {Object} data - New data to merge
     */
    updateData(data) {
      if (data.regularPrice)
        this.setAttribute("data-regular-price", data.regularPrice);
      if (data.salePrice) this.setAttribute("data-sale-price", data.salePrice);
      if (data.currencySymbol)
        this.setAttribute("data-currency-symbol", data.currencySymbol);
      if (data.discountPercentage)
        this.setAttribute("data-discount-percentage", data.discountPercentage);
      if (data.scenario) this.setAttribute("data-scenario", data.scenario);
    }
  }

  // Register the custom element
  if (!customElements.get("oppb-price")) {
    customElements.define("oppb-price", OppbPrice);
  }

  // Export for programmatic use
  window.OppbPrice = OppbPrice;

  /**
   * Utility function to create price element programmatically
   * @param {Object} options - Price options
   * @returns {HTMLElement}
   */
  window.createOppbPrice = function (options = {}) {
    const el = document.createElement("oppb-price");

    if (options.productId) el.setAttribute("data-product-id", options.productId);
    if (options.scenario) el.setAttribute("data-scenario", options.scenario);
    if (options.location) el.setAttribute("data-location", options.location);
    if (options.regularPrice)
      el.setAttribute("data-regular-price", options.regularPrice);
    if (options.salePrice) el.setAttribute("data-sale-price", options.salePrice);
    if (options.currencySymbol)
      el.setAttribute("data-currency-symbol", options.currencySymbol);
    if (options.discountPercentage)
      el.setAttribute("data-discount-percentage", options.discountPercentage);
    if (options.layoutId) el.setAttribute("data-layout-id", options.layoutId);

    return el;
  };

  /**
   * Re-render all OPPB price components on the page
   * Useful when layouts are loaded dynamically or updated
   */
  window.oppbRefreshAllPrices = function () {
    const prices = document.querySelectorAll("oppb-price");
    debugLog('Refreshing all prices:', prices.length);
    prices.forEach((el) => {
      if (el.refresh) {
        el.refresh();
      }
    });
  };

  /**
   * Update layouts and re-render all prices
   * @param {Object} layouts - New layouts object
   */
  window.oppbUpdateLayouts = function (layouts) {
    window.oppbLayouts = layouts;
    window.oppbLayoutsLoaded = true;
    window.oppbRefreshAllPrices();
  };

  // Listen for layout data being added to the page
  // This handles cases where the script loads before the inline data
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
      // Give a small delay for any remaining inline scripts to execute
      setTimeout(function() {
        if (window.oppbLayoutsLoaded) {
          window.oppbRefreshAllPrices();
        }
      }, 50);
    });
  }
})();
