<?php
/**
 * Block Adapter — mimics the Elementor Widget_Base interface so that
 * existing ggmc_*_render() functions work unchanged for Gutenberg blocks.
 *
 * Only the methods actually called by render functions are implemented:
 *   get_settings_for_display(), get_product_data(), get_product_image_html(),
 *   add_render_attribute(), get_render_attribute_string(), add_link_attributes()
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching

class GGMC_Block_Adapter {

	/** @var array Block attributes (equivalent to Elementor settings). */
	private $settings = [];

	/** @var object|null Cached product data. */
	private $product_data = null;

	/** @var array Accumulated render attributes keyed by element name. */
	private $render_attributes = [];

	/**
	 * @param array       $attributes  Block attributes from block.json.
	 * @param object|null $product_data Pre-resolved product data (optional).
	 */
	public function __construct( array $attributes = [], $product_data = null ) {
		// Store original attributes and also add snake_case versions of camelCase keys
		// so render functions using Elementor's snake_case conventions find their values
		$this->settings = $attributes;
		foreach ( $attributes as $key => $value ) {
			$snake = strtolower( preg_replace( '/([a-z])([A-Z])/', '$1_$2', $key ) );
			if ( $snake !== $key && ! isset( $this->settings[ $snake ] ) ) {
				$this->settings[ $snake ] = $value;
			}
		}
		$this->product_data = $product_data;
	}

	/**
	 * Return a unique ID for this widget instance — mirrors Elementor's get_id().
	 */
	public function get_id() {
		return 'ggmc-block-' . substr( md5( serialize( $this->settings ) ), 0, 8 );
	}

	/**
	 * Return block attributes — mirrors Elementor's get_settings_for_display().
	 */
	public function get_settings_for_display( $setting = null ) {
		if ( $setting !== null ) {
			return $this->settings[ $setting ] ?? '';
		}
		// Return a default-safe array wrapper that returns '' for missing keys
		return new GGMC_Block_Settings_Array( $this->settings );
	}

	/**
	 * Resolve product data — mirrors GGM_Commerce_Base_Widget::get_product_data().
	 */
	public function get_product_data() {
		// Return pre-resolved data if set.
		if ( $this->product_data !== null ) {
			return $this->product_data;
		}

		// Check for explicit product ID in settings (custom product source).
		if ( isset( $this->settings['product_source'] ) && $this->settings['product_source'] === 'custom' ) {
			$custom_id = ! empty( $this->settings['product_id'] ) ? absint( $this->settings['product_id'] ) : 0;
			if ( $custom_id > 0 ) {
				$this->product_data = $this->fetch_product( $custom_id );
				if ( $this->product_data ) {
					return $this->product_data;
				}
			}
		}

		// Check for productId block attribute.
		if ( ! empty( $this->settings['productId'] ) ) {
			$this->product_data = $this->fetch_product( absint( $this->settings['productId'] ) );
			if ( $this->product_data ) {
				return $this->product_data;
			}
		}

		// Check global product context (Quick View AJAX, etc.).
		if ( ! empty( $GLOBALS['ggmc_current_product_id'] ) ) {
			$this->product_data = $this->fetch_product( absint( $GLOBALS['ggmc_current_product_id'] ) );
			if ( $this->product_data ) {
				return $this->product_data;
			}
		}

		// Fall back to page context.
		if ( class_exists( 'GGM_Commerce_Helpers' ) ) {
			$product_id = GGM_Commerce_Helpers::get_current_product_id();
			if ( $product_id ) {
				$this->product_data = $this->fetch_product( absint( $product_id ) );
			}
		}

		return $this->product_data;
	}

	/**
	 * Fetch a single product row from wp_ggmc_products.
	 *
	 * @param int $product_id
	 * @return object|null
	 */
	private function fetch_product( $product_id ) {
		global $wpdb;
		$table = $wpdb->prefix . 'ggmc_products';
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $product_id ) );
	}

	/**
	 * Get product image HTML — mirrors GGM_Commerce_Base_Widget::get_product_image_html().
	 */
	public function get_product_image_html( $image_id, $size = 'medium', $alt = '', $style = '' ) {
		if ( empty( $image_id ) ) {
			return '';
		}

		$attachment = get_post( $image_id );
		if ( ! $attachment || $attachment->post_type !== 'attachment' ) {
			return '';
		}

		$attrs = [ 'alt' => $alt ];
		if ( ! empty( $style ) ) {
			$attrs['style'] = $style;
		}

		$html = wp_get_attachment_image( $image_id, $size, false, $attrs );

		if ( empty( $html ) ) {
			$url = wp_get_attachment_url( $image_id );
			if ( $url ) {
				$attr_str = '';
				foreach ( $attrs as $k => $v ) {
					$attr_str .= ' ' . $k . '="' . esc_attr( $v ) . '"';
				}
				$sizes = [
					'thumbnail' => ' width="60" height="60"',
					'medium'    => ' width="150" height="150"',
					'large'     => ' width="400" height="400"',
				];
				$size_attr = $sizes[ $size ] ?? '';
				return '<img src="' . esc_url( $url ) . '"' . $attr_str . $size_attr . ' />';
			}
			return '';
		}

		return $html;
	}

	/**
	 * Accumulate HTML attributes for a named element.
	 * Mirrors Elementor\Controls_Stack::add_render_attribute().
	 */
	public function add_render_attribute( $element, $key = null, $value = null, $overwrite = false ) {
		if ( is_array( $element ) ) {
			foreach ( $element as $el_key => $attrs ) {
				$this->add_render_attribute( $el_key, $attrs );
			}
			return $this;
		}

		if ( is_array( $key ) ) {
			foreach ( $key as $attr_key => $attr_value ) {
				$this->add_render_attribute( $element, $attr_key, $attr_value, $overwrite );
			}
			return $this;
		}

		if ( $overwrite || ! isset( $this->render_attributes[ $element ][ $key ] ) ) {
			$this->render_attributes[ $element ][ $key ] = [];
		}

		$this->render_attributes[ $element ][ $key ] = array_merge(
			$this->render_attributes[ $element ][ $key ],
			(array) $value
		);

		return $this;
	}

	/**
	 * Build an HTML attribute string from accumulated attributes.
	 * Mirrors Elementor\Controls_Stack::get_render_attribute_string().
	 */
	public function get_render_attribute_string( $element ) {
		if ( empty( $this->render_attributes[ $element ] ) ) {
			return '';
		}

		$output = '';
		foreach ( $this->render_attributes[ $element ] as $attr => $values ) {
			$output .= ' ' . $attr . '="' . esc_attr( implode( ' ', $values ) ) . '"';
		}

		return ltrim( $output );
	}

	/**
	 * Build link attributes from Elementor-style link data.
	 * Mirrors Elementor\Widget_Base::add_link_attributes().
	 */
	public function add_link_attributes( $element, $url_control, $overwrite = false ) {
		if ( ! is_array( $url_control ) ) {
			return;
		}

		$this->add_render_attribute( $element, 'href', $url_control['url'] ?? '#', $overwrite );

		if ( ! empty( $url_control['is_external'] ) ) {
			$this->add_render_attribute( $element, 'target', '_blank' );
			$this->add_render_attribute( $element, 'rel', 'noopener noreferrer' );
		}

		if ( ! empty( $url_control['nofollow'] ) ) {
			$existing = $this->render_attributes[ $element ]['rel'] ?? [];
			if ( ! in_array( 'nofollow', $existing, true ) ) {
				$this->add_render_attribute( $element, 'rel', 'nofollow' );
			}
		}

		if ( ! empty( $url_control['custom_attributes'] ) ) {
			$custom = explode( ',', $url_control['custom_attributes'] );
			foreach ( $custom as $pair ) {
				$pair = explode( '|', $pair, 2 );
				if ( count( $pair ) === 2 ) {
					$this->add_render_attribute( $element, trim( $pair[0] ), trim( $pair[1] ), $overwrite );
				}
			}
		}
	}
}

/**
 * Array wrapper that returns '' for undefined keys instead of triggering PHP notices.
 * Behaves like a normal array in all other respects.
 */
class GGMC_Block_Settings_Array extends ArrayObject {
	public function offsetGet( $key ): mixed {
		if ( ! $this->offsetExists( $key ) ) {
			return '';
		}
		return parent::offsetGet( $key );
	}
}
