<?php
/**
 * GGM Commerce Custom Attributes Management
 * Handles attribute groups, attribute definitions, and product attribute values
 *
 * @package GGM_Commerce
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter

/**
 * Class GGM_Commerce_Attributes
 *
 * Manages custom product attributes with group templates.
 */
class GGM_Commerce_Attributes {

	/**
	 * Singleton instance.
	 *
	 * @var GGM_Commerce_Attributes|null
	 */
	private static $instance = null;

	/**
	 * Get singleton instance.
	 *
	 * @return GGM_Commerce_Attributes
	 */
	public static function get_instance() {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor.
	 */
	public function __construct() {
		// AJAX handlers for attribute groups.
		add_action( 'wp_ajax_ggmc_create_attribute_group', array( $this, 'ajax_create_attribute_group' ) );
		add_action( 'wp_ajax_ggmc_update_attribute_group', array( $this, 'ajax_update_attribute_group' ) );
		add_action( 'wp_ajax_ggmc_delete_attribute_group', array( $this, 'ajax_delete_attribute_group' ) );

		// AJAX handlers for attributes.
		add_action( 'wp_ajax_ggmc_create_attribute', array( $this, 'ajax_create_attribute' ) );
		add_action( 'wp_ajax_ggmc_update_attribute', array( $this, 'ajax_update_attribute' ) );
		add_action( 'wp_ajax_ggmc_delete_attribute', array( $this, 'ajax_delete_attribute' ) );

		// AJAX handlers for product attributes.
		add_action( 'wp_ajax_ggmc_add_product_attribute', array( $this, 'ajax_add_product_attribute' ) );
		add_action( 'wp_ajax_ggmc_add_product_attributes_from_group', array( $this, 'ajax_add_product_attributes_from_group' ) );
		add_action( 'wp_ajax_ggmc_remove_product_attribute', array( $this, 'ajax_remove_product_attribute' ) );
		add_action( 'wp_ajax_ggmc_get_product_attribute_fields', array( $this, 'ajax_get_product_attribute_fields' ) );
	}

	/**
	 * Get available attribute types.
	 *
	 * @return array
	 */
	public function get_attribute_types() {
		return array(
			'text'   => __( 'Text', 'ggm-commerce' ),
			'number' => __( 'Number', 'ggm-commerce' ),
			'select' => __( 'Dropdown Select', 'ggm-commerce' ),
		);
	}

	// =========================================================================
	// ATTRIBUTE GROUPS CRUD
	// =========================================================================

	/**
	 * Get all attribute groups.
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	public function get_attribute_groups( $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status'  => 'active',
			'orderby' => 'display_order',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		$sql = "SELECT * FROM {$wpdb->prefix}ggmc_attribute_groups WHERE 1=1";

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND status = %s', $args['status'] );
		}

		$sql .= " ORDER BY {$args['orderby']} {$args['order']}";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely with wpdb->prepare.
		return $wpdb->get_results( $sql );
	}

	/**
	 * Get attribute group by ID.
	 *
	 * @param int $group_id Group ID.
	 * @return object|null
	 */
	public function get_attribute_group( $group_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}ggmc_attribute_groups WHERE id = %d",
				$group_id
			)
		);
	}

	/**
	 * Create attribute group.
	 *
	 * @param array $data Group data.
	 * @return int|WP_Error
	 */
	public function create_attribute_group( $data ) {
		global $wpdb;

		if ( empty( $data['name'] ) ) {
			return new WP_Error( 'missing_name', __( 'Group name is required', 'ggm-commerce' ) );
		}

		$insert_data = array(
			'name'          => sanitize_text_field( $data['name'] ),
			'description'   => sanitize_textarea_field( $data['description'] ?? '' ),
			'display_order' => intval( $data['display_order'] ?? 0 ),
			'status'        => sanitize_text_field( $data['status'] ?? 'active' ),
			'created_at'    => current_time( 'mysql' ),
			'updated_at'    => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$inserted = $wpdb->insert( "{$wpdb->prefix}ggmc_attribute_groups", $insert_data );

		if ( $inserted ) {
			return $wpdb->insert_id;
		}

		return new WP_Error( 'insert_failed', __( 'Failed to create attribute group', 'ggm-commerce' ) );
	}

	/**
	 * Update attribute group.
	 *
	 * @param int   $group_id Group ID.
	 * @param array $data     Group data.
	 * @return bool|WP_Error
	 */
	public function update_attribute_group( $group_id, $data ) {
		global $wpdb;

		$group = $this->get_attribute_group( $group_id );
		if ( ! $group ) {
			return new WP_Error( 'not_found', __( 'Attribute group not found', 'ggm-commerce' ) );
		}

		$update_data = array();

		if ( isset( $data['name'] ) ) {
			$update_data['name'] = sanitize_text_field( $data['name'] );
		}

		if ( isset( $data['description'] ) ) {
			$update_data['description'] = sanitize_textarea_field( $data['description'] );
		}

		if ( isset( $data['display_order'] ) ) {
			$update_data['display_order'] = intval( $data['display_order'] );
		}

		if ( isset( $data['status'] ) ) {
			$update_data['status'] = sanitize_text_field( $data['status'] );
		}

		if ( ! empty( $update_data ) ) {
			$update_data['updated_at'] = current_time( 'mysql' );

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$updated = $wpdb->update(
				"{$wpdb->prefix}ggmc_attribute_groups",
				$update_data,
				array( 'id' => $group_id )
			);

			if ( $updated !== false ) {
				return true;
			}

			return new WP_Error( 'update_failed', __( 'Failed to update attribute group', 'ggm-commerce' ) );
		}

		return true;
	}

	/**
	 * Delete attribute group.
	 * Note: Deleting a group also deletes its attributes and their product values.
	 *
	 * @param int $group_id Group ID.
	 * @return bool|WP_Error
	 */
	public function delete_attribute_group( $group_id ) {
		global $wpdb;

		$group = $this->get_attribute_group( $group_id );
		if ( ! $group ) {
			return new WP_Error( 'not_found', __( 'Attribute group not found', 'ggm-commerce' ) );
		}

		// Get all attributes in this group.
		$attributes = $this->get_attributes( $group_id );

		// Delete product attribute values for each attribute in this group.
		foreach ( $attributes as $attribute ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->delete(
				"{$wpdb->prefix}ggmc_product_attribute_values",
				array( 'attribute_id' => $attribute->id ),
				array( '%d' )
			);
		}

		// Delete all attributes in this group.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			"{$wpdb->prefix}ggmc_attributes",
			array( 'group_id' => $group_id ),
			array( '%d' )
		);

		// Delete the group.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$deleted = $wpdb->delete(
			"{$wpdb->prefix}ggmc_attribute_groups",
			array( 'id' => $group_id )
		);

		if ( $deleted ) {
			return true;
		}

		return new WP_Error( 'delete_failed', __( 'Failed to delete attribute group', 'ggm-commerce' ) );
	}

	/**
	 * Get product count for a group.
	 *
	 * @param int $group_id Group ID.
	 * @return int
	 */
	public function get_group_product_count( $group_id ) {
		global $wpdb;

		// Count distinct products that have any attribute from this group.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(DISTINCT pav.product_id)
				FROM {$wpdb->prefix}ggmc_product_attribute_values pav
				INNER JOIN {$wpdb->prefix}ggmc_attributes a ON pav.attribute_id = a.id
				WHERE a.group_id = %d",
				$group_id
			)
		);
	}

	// =========================================================================
	// ATTRIBUTES CRUD
	// =========================================================================

	/**
	 * Get attributes, optionally filtered by group.
	 *
	 * @param int|null $group_id Group ID or null for all attributes.
	 * @param array    $args     Query arguments.
	 * @return array
	 */
	public function get_attributes( $group_id = null, $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status'  => 'active',
			'orderby' => 'display_order',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		$sql = "SELECT * FROM {$wpdb->prefix}ggmc_attributes WHERE 1=1";

		if ( $group_id !== null ) {
			$sql .= $wpdb->prepare( ' AND group_id = %d', $group_id );
		}

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND status = %s', $args['status'] );
		}

		$sql .= " ORDER BY {$args['orderby']} {$args['order']}";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely.
		return $wpdb->get_results( $sql );
	}

	/**
	 * Get standalone attributes (not in any group).
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	public function get_standalone_attributes( $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status'  => 'active',
			'orderby' => 'display_order',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		$sql = "SELECT * FROM {$wpdb->prefix}ggmc_attributes WHERE group_id IS NULL";

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND status = %s', $args['status'] );
		}

		$sql .= " ORDER BY {$args['orderby']} {$args['order']}";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely.
		return $wpdb->get_results( $sql );
	}

	/**
	 * Get all attributes (both grouped and standalone).
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	public function get_all_attributes( $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status'  => 'active',
			'orderby' => 'name',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		$sql = "SELECT a.*, g.name as group_name
				FROM {$wpdb->prefix}ggmc_attributes a
				LEFT JOIN {$wpdb->prefix}ggmc_attribute_groups g ON a.group_id = g.id
				WHERE 1=1";

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND a.status = %s', $args['status'] );
		}

		$sql .= " ORDER BY a.{$args['orderby']} {$args['order']}";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely.
		return $wpdb->get_results( $sql );
	}

	/**
	 * Get all filterable attributes.
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	public function get_filterable_attributes( $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status'  => 'active',
			'orderby' => 'name',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		// Use transient cache to avoid query on every page load.
		$cache_key = 'ggmc_filterable_attrs_' . md5( wp_json_encode( $args ) );
		$cached    = get_transient( $cache_key );
		if ( false !== $cached ) {
			return $cached;
		}

		$sql = "SELECT a.*, g.name as group_name
				FROM {$wpdb->prefix}ggmc_attributes a
				LEFT JOIN {$wpdb->prefix}ggmc_attribute_groups g ON a.group_id = g.id
				WHERE a.filterable = 1";

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND a.status = %s', $args['status'] );
		}

		$sql .= " ORDER BY a.{$args['orderby']} {$args['order']}";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely.
		$results = $wpdb->get_results( $sql );

		set_transient( $cache_key, $results, HOUR_IN_SECONDS );

		return $results;
	}

	/**
	 * Clear filterable attributes cache.
	 * Called when attributes are created, updated, or deleted.
	 *
	 * @return void
	 */
	public function clear_filterable_cache() {
		global $wpdb;

		// Delete all transients matching the filterable attributes cache pattern.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query(
			"DELETE FROM {$wpdb->options}
			WHERE option_name LIKE '_transient_ggmc_filterable_attrs_%'
			OR option_name LIKE '_transient_timeout_ggmc_filterable_attrs_%'"
		);
	}

	/**
	 * Get value range for a number-type attribute across all products.
	 *
	 * @param int $attribute_id Attribute ID.
	 * @return array Array with 'min' and 'max' values.
	 */
	public function get_attribute_value_range( $attribute_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT
					MIN(CAST(pav.attribute_value AS DECIMAL(10,2))) as min_value,
					MAX(CAST(pav.attribute_value AS DECIMAL(10,2))) as max_value
				FROM {$wpdb->prefix}ggmc_product_attribute_values pav
				INNER JOIN {$wpdb->prefix}ggmc_products p ON pav.product_id = p.id
				WHERE pav.attribute_id = %d
				AND pav.attribute_value != ''
				AND pav.attribute_value REGEXP '^[0-9]+\\.?[0-9]*$'
				AND (p.status = 'active' OR p.status IS NULL)",
				$attribute_id
			)
		);

		return array(
			'min' => $result && $result->min_value !== null ? floatval( $result->min_value ) : 0,
			'max' => $result && $result->max_value !== null ? floatval( $result->max_value ) : 100,
		);
	}

	/**
	 * Get distinct option values and product counts for a select-type attribute.
	 *
	 * @param int   $attribute_id Attribute ID.
	 * @param array $product_ids  Optional. Filter to specific product IDs.
	 * @return array Array of objects with 'attribute_value' and 'count'.
	 */
	public function get_attribute_option_counts( $attribute_id, $product_ids = array() ) {
		global $wpdb;

		$sql = "SELECT pav.attribute_value, COUNT(DISTINCT pav.product_id) as count
				FROM {$wpdb->prefix}ggmc_product_attribute_values pav
				INNER JOIN {$wpdb->prefix}ggmc_products p ON pav.product_id = p.id
				WHERE pav.attribute_id = %d
				AND pav.attribute_value != ''
				AND (p.status = 'active' OR p.status IS NULL)";

		$params = array( $attribute_id );

		if ( ! empty( $product_ids ) ) {
			$ids_placeholder = implode( ',', array_fill( 0, count( $product_ids ), '%d' ) );
			$sql            .= " AND pav.product_id IN ($ids_placeholder)";
			$params          = array_merge( $params, array_map( 'intval', $product_ids ) );
		}

		$sql .= ' GROUP BY pav.attribute_value ORDER BY pav.attribute_value ASC';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $sql is built safely.
		return $wpdb->get_results( $wpdb->prepare( $sql, $params ) );
	}

	/**
	 * Get attributes mapped by group ID for JavaScript.
	 *
	 * @return array Group ID => array of attributes.
	 */
	public function get_group_attributes_map() {
		$groups = $this->get_attribute_groups();
		$map    = array();

		foreach ( $groups as $group ) {
			$attrs = $this->get_attributes( $group->id );
			$map[ $group->id ] = array();

			foreach ( $attrs as $attr ) {
				$map[ $group->id ][] = array(
					'id'             => $attr->id,
					'name'           => $attr->name,
					'attribute_type' => $attr->attribute_type,
					'options'        => $attr->options,
					'unit'           => $attr->unit,
					'icon'           => $attr->icon ?? '',
				);
			}
		}

		return $map;
	}

	/**
	 * Get attribute by ID.
	 *
	 * @param int $attribute_id Attribute ID.
	 * @return object|null
	 */
	public function get_attribute( $attribute_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}ggmc_attributes WHERE id = %d",
				$attribute_id
			)
		);
	}

	/**
	 * Create attribute.
	 *
	 * @param array $data Attribute data.
	 * @return int|WP_Error
	 */
	public function create_attribute( $data ) {
		global $wpdb;

		if ( empty( $data['name'] ) ) {
			return new WP_Error( 'missing_name', __( 'Attribute name is required', 'ggm-commerce' ) );
		}

		$valid_types = array_keys( $this->get_attribute_types() );
		$attr_type   = isset( $data['attribute_type'] ) ? sanitize_text_field( $data['attribute_type'] ) : 'text';
		if ( ! in_array( $attr_type, $valid_types, true ) ) {
			$attr_type = 'text';
		}

		// Handle options for select type.
		$options = '';
		if ( 'select' === $attr_type && ! empty( $data['options'] ) ) {
			if ( is_array( $data['options'] ) ) {
				$options = wp_json_encode( array_map( 'sanitize_text_field', $data['options'] ) );
			} else {
				// Assume comma-separated string.
				$options_array = array_map( 'trim', explode( ',', $data['options'] ) );
				$options_array = array_map( 'sanitize_text_field', $options_array );
				$options_array = array_filter( $options_array );
				$options       = wp_json_encode( $options_array );
			}
		}

		// Handle icon - can be JSON from icon picker or legacy class string.
		$icon_value = '';
		if ( ! empty( $data['icon'] ) ) {
			$raw_icon = wp_unslash( $data['icon'] );
			// Check if it's already JSON format from icon picker.
			$decoded = json_decode( $raw_icon, true );
			if ( is_array( $decoded ) && isset( $decoded['value'] ) ) {
				// Already valid JSON, sanitize and use as-is.
				$icon_value = wp_json_encode(
					array(
						'value'   => sanitize_text_field( $decoded['value'] ),
						'library' => sanitize_text_field( $decoded['library'] ?? 'fa-solid' ),
					)
				);
			} else {
				// Legacy format - plain class string, convert to JSON.
				$icon_class = sanitize_text_field( $raw_icon );
				$icon_value = wp_json_encode(
					array(
						'value'   => $icon_class,
						'library' => 'fa-solid',
					)
				);
			}
		}

		$insert_data = array(
			'group_id'       => ! empty( $data['group_id'] ) ? intval( $data['group_id'] ) : null,
			'name'           => sanitize_text_field( $data['name'] ),
			'attribute_type' => $attr_type,
			'options'        => $options,
			'unit'           => sanitize_text_field( $data['unit'] ?? '' ),
			'icon'           => $icon_value,
			'filterable'     => ! empty( $data['filterable'] ) ? 1 : 0,
			'display_order'  => intval( $data['display_order'] ?? 0 ),
			'status'         => sanitize_text_field( $data['status'] ?? 'active' ),
			'created_at'     => current_time( 'mysql' ),
			'updated_at'     => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$inserted = $wpdb->insert( "{$wpdb->prefix}ggmc_attributes", $insert_data );

		if ( $inserted ) {
			$this->clear_filterable_cache();
			return $wpdb->insert_id;
		}

		return new WP_Error( 'insert_failed', __( 'Failed to create attribute', 'ggm-commerce' ) );
	}

	/**
	 * Update attribute.
	 *
	 * @param int   $attribute_id Attribute ID.
	 * @param array $data         Attribute data.
	 * @return bool|WP_Error
	 */
	public function update_attribute( $attribute_id, $data ) {
		global $wpdb;

		$attribute = $this->get_attribute( $attribute_id );
		if ( ! $attribute ) {
			return new WP_Error( 'not_found', __( 'Attribute not found', 'ggm-commerce' ) );
		}

		$update_data = array();

		if ( isset( $data['name'] ) ) {
			$update_data['name'] = sanitize_text_field( $data['name'] );
		}

		if ( isset( $data['group_id'] ) ) {
			$update_data['group_id'] = ! empty( $data['group_id'] ) ? intval( $data['group_id'] ) : null;
		}

		if ( isset( $data['attribute_type'] ) ) {
			$valid_types = array_keys( $this->get_attribute_types() );
			$attr_type   = sanitize_text_field( $data['attribute_type'] );
			if ( in_array( $attr_type, $valid_types, true ) ) {
				$update_data['attribute_type'] = $attr_type;
			}
		}

		if ( isset( $data['options'] ) ) {
			if ( is_array( $data['options'] ) ) {
				$update_data['options'] = wp_json_encode( array_map( 'sanitize_text_field', $data['options'] ) );
			} else {
				$options_array          = array_map( 'trim', explode( ',', $data['options'] ) );
				$options_array          = array_map( 'sanitize_text_field', $options_array );
				$options_array          = array_filter( $options_array );
				$update_data['options'] = wp_json_encode( $options_array );
			}
		}

		if ( isset( $data['unit'] ) ) {
			$update_data['unit'] = sanitize_text_field( $data['unit'] );
		}

		if ( isset( $data['icon'] ) ) {
			$raw_icon = wp_unslash( $data['icon'] );
			if ( ! empty( $raw_icon ) ) {
				// Check if it's already JSON format from icon picker.
				$decoded = json_decode( $raw_icon, true );
				if ( is_array( $decoded ) && isset( $decoded['value'] ) ) {
					// Already valid JSON, sanitize and use as-is.
					$update_data['icon'] = wp_json_encode(
						array(
							'value'   => sanitize_text_field( $decoded['value'] ),
							'library' => sanitize_text_field( $decoded['library'] ?? 'fa-solid' ),
						)
					);
				} else {
					// Legacy format - plain class string, convert to JSON.
					$icon_class          = sanitize_text_field( $raw_icon );
					$update_data['icon'] = wp_json_encode(
						array(
							'value'   => $icon_class,
							'library' => 'fa-solid',
						)
					);
				}
			} else {
				$update_data['icon'] = '';
			}
		}

		if ( isset( $data['display_order'] ) ) {
			$update_data['display_order'] = intval( $data['display_order'] );
		}

		if ( isset( $data['status'] ) ) {
			$update_data['status'] = sanitize_text_field( $data['status'] );
		}

		// Handle filterable field - checkbox will only be present if checked
		// Use array_key_exists to detect if form was submitted with this field
		if ( array_key_exists( 'filterable', $data ) ) {
			$update_data['filterable'] = ! empty( $data['filterable'] ) ? 1 : 0;
		}

		if ( ! empty( $update_data ) ) {
			$update_data['updated_at'] = current_time( 'mysql' );

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$updated = $wpdb->update(
				"{$wpdb->prefix}ggmc_attributes",
				$update_data,
				array( 'id' => $attribute_id )
			);

			if ( $updated !== false ) {
				$this->clear_filterable_cache();
				return true;
			}

			return new WP_Error( 'update_failed', __( 'Failed to update attribute', 'ggm-commerce' ) );
		}

		return true;
	}

	/**
	 * Delete attribute.
	 * Note: Also deletes all product attribute values for this attribute.
	 *
	 * @param int $attribute_id Attribute ID.
	 * @return bool|WP_Error
	 */
	public function delete_attribute( $attribute_id ) {
		global $wpdb;

		$attribute = $this->get_attribute( $attribute_id );
		if ( ! $attribute ) {
			return new WP_Error( 'not_found', __( 'Attribute not found', 'ggm-commerce' ) );
		}

		// Delete all product attribute values for this attribute.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			"{$wpdb->prefix}ggmc_product_attribute_values",
			array( 'attribute_id' => $attribute_id ),
			array( '%d' )
		);

		// Delete the attribute.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$deleted = $wpdb->delete(
			"{$wpdb->prefix}ggmc_attributes",
			array( 'id' => $attribute_id )
		);

		if ( $deleted ) {
			// Also remove from all groups.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->delete(
				"{$wpdb->prefix}ggmc_attribute_group_members",
				array( 'attribute_id' => $attribute_id ),
				array( '%d' )
			);
			$this->clear_filterable_cache();
			return true;
		}

		return new WP_Error( 'delete_failed', __( 'Failed to delete attribute', 'ggm-commerce' ) );
	}

	// =========================================================================
	// ATTRIBUTE GROUP MEMBERSHIP (Many-to-Many)
	// =========================================================================

	/**
	 * Add an attribute to a group.
	 *
	 * @param int $group_id     Group ID.
	 * @param int $attribute_id Attribute ID.
	 * @return bool|WP_Error
	 */
	public function add_attribute_to_group( $group_id, $attribute_id ) {
		global $wpdb;

		// Check if already exists.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$existing = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}ggmc_attribute_group_members WHERE group_id = %d AND attribute_id = %d",
				$group_id,
				$attribute_id
			)
		);

		if ( $existing ) {
			return true; // Already in group.
		}

		// Get max display order.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$max_order = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT MAX(display_order) FROM {$wpdb->prefix}ggmc_attribute_group_members WHERE group_id = %d",
				$group_id
			)
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$inserted = $wpdb->insert(
			"{$wpdb->prefix}ggmc_attribute_group_members",
			array(
				'group_id'      => $group_id,
				'attribute_id'  => $attribute_id,
				'display_order' => $max_order + 1,
				'created_at'    => current_time( 'mysql' ),
			)
		);

		return $inserted ? true : new WP_Error( 'insert_failed', __( 'Failed to add attribute to group', 'ggm-commerce' ) );
	}

	/**
	 * Remove an attribute from a group.
	 *
	 * @param int $group_id     Group ID.
	 * @param int $attribute_id Attribute ID.
	 * @return bool
	 */
	public function remove_attribute_from_group( $group_id, $attribute_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			"{$wpdb->prefix}ggmc_attribute_group_members",
			array(
				'group_id'     => $group_id,
				'attribute_id' => $attribute_id,
			),
			array( '%d', '%d' )
		);

		return true;
	}

	/**
	 * Get attributes in a group (using junction table).
	 *
	 * @param int   $group_id Group ID.
	 * @param array $args     Query arguments.
	 * @return array
	 */
	public function get_group_attributes( $group_id, $args = array() ) {
		global $wpdb;

		$defaults = array(
			'status' => 'active',
		);
		$args     = wp_parse_args( $args, $defaults );

		$sql = "SELECT a.*, gm.display_order as group_display_order
				FROM {$wpdb->prefix}ggmc_attributes a
				INNER JOIN {$wpdb->prefix}ggmc_attribute_group_members gm ON a.id = gm.attribute_id
				WHERE gm.group_id = %d";

		if ( $args['status'] ) {
			$sql .= $wpdb->prepare( ' AND a.status = %s', $args['status'] );
		}

		$sql .= ' ORDER BY gm.display_order ASC';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Built with prepare calls.
		return $wpdb->get_results( $wpdb->prepare( $sql, $group_id ) );
	}

	/**
	 * Get groups an attribute belongs to.
	 *
	 * @param int $attribute_id Attribute ID.
	 * @return array
	 */
	public function get_attribute_groups_for_attribute( $attribute_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT g.*
				FROM {$wpdb->prefix}ggmc_attribute_groups g
				INNER JOIN {$wpdb->prefix}ggmc_attribute_group_members gm ON g.id = gm.group_id
				WHERE gm.attribute_id = %d
				ORDER BY g.name ASC",
				$attribute_id
			)
		);
	}

	/**
	 * Set group attributes (replaces all existing).
	 *
	 * @param int   $group_id      Group ID.
	 * @param array $attribute_ids Array of attribute IDs.
	 * @return bool
	 */
	public function set_group_attributes( $group_id, $attribute_ids ) {
		global $wpdb;

		// Remove all existing.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			"{$wpdb->prefix}ggmc_attribute_group_members",
			array( 'group_id' => $group_id ),
			array( '%d' )
		);

		// Add new ones.
		$order = 0;
		foreach ( $attribute_ids as $attr_id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->insert(
				"{$wpdb->prefix}ggmc_attribute_group_members",
				array(
					'group_id'      => $group_id,
					'attribute_id'  => intval( $attr_id ),
					'display_order' => $order++,
					'created_at'    => current_time( 'mysql' ),
				)
			);
		}

		return true;
	}

	/**
	 * Get group attributes map for JavaScript (using junction table).
	 *
	 * @return array Group ID => array of attributes.
	 */
	public function get_group_attributes_map_v2() {
		$groups = $this->get_attribute_groups();
		$map    = array();

		foreach ( $groups as $group ) {
			$attrs             = $this->get_group_attributes( $group->id );
			$map[ $group->id ] = array();

			foreach ( $attrs as $attr ) {
				$map[ $group->id ][] = array(
					'id'             => $attr->id,
					'name'           => $attr->name,
					'attribute_type' => $attr->attribute_type,
					'options'        => $attr->options,
					'unit'           => $attr->unit,
					'icon'           => $attr->icon ?? '',
				);
			}
		}

		return $map;
	}

	// =========================================================================
	// PRODUCT ATTRIBUTE MANAGEMENT
	// =========================================================================

	/**
	 * Add attribute to product.
	 *
	 * @param int    $product_id   Product ID.
	 * @param int    $attribute_id Attribute ID.
	 * @param string $value        Attribute value.
	 * @return bool|WP_Error
	 */
	public function add_attribute_to_product( $product_id, $attribute_id, $value = '' ) {
		global $wpdb;

		$attribute = $this->get_attribute( $attribute_id );
		if ( ! $attribute ) {
			return new WP_Error( 'not_found', __( 'Attribute not found', 'ggm-commerce' ) );
		}

		// Get current max display order for this product.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$max_order = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT MAX(display_order) FROM {$wpdb->prefix}ggmc_product_attribute_values WHERE product_id = %d",
				$product_id
			)
		);

		// Check if attribute already exists for this product.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$existing = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}ggmc_product_attribute_values WHERE product_id = %d AND attribute_id = %d",
				$product_id,
				$attribute_id
			)
		);

		if ( $existing ) {
			// Update existing value.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_product_attribute_values",
				array(
					'attribute_value' => sanitize_text_field( $value ),
					'updated_at'      => current_time( 'mysql' ),
				),
				array( 'id' => $existing )
			);
			return true;
		}

		// Insert new.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$inserted = $wpdb->insert(
			"{$wpdb->prefix}ggmc_product_attribute_values",
			array(
				'product_id'      => $product_id,
				'attribute_id'    => $attribute_id,
				'attribute_value' => sanitize_text_field( $value ),
				'display_order'   => $max_order + 1,
				'created_at'      => current_time( 'mysql' ),
				'updated_at'      => current_time( 'mysql' ),
			)
		);

		return $inserted ? true : new WP_Error( 'insert_failed', __( 'Failed to add attribute to product', 'ggm-commerce' ) );
	}

	/**
	 * Add all attributes from a group to a product.
	 *
	 * @param int $product_id Product ID.
	 * @param int $group_id   Group ID.
	 * @return array|WP_Error Array of added attribute IDs.
	 */
	public function add_attributes_from_group( $product_id, $group_id ) {
		$group = $this->get_attribute_group( $group_id );
		if ( ! $group ) {
			return new WP_Error( 'not_found', __( 'Attribute group not found', 'ggm-commerce' ) );
		}

		$attributes = $this->get_attributes( $group_id );
		$added_ids  = array();

		foreach ( $attributes as $attribute ) {
			$result = $this->add_attribute_to_product( $product_id, $attribute->id, '' );
			if ( ! is_wp_error( $result ) ) {
				$added_ids[] = $attribute->id;
			}
		}

		return $added_ids;
	}

	/**
	 * Remove attribute from product.
	 *
	 * @param int $product_id   Product ID.
	 * @param int $attribute_id Attribute ID.
	 * @return bool
	 */
	public function remove_attribute_from_product( $product_id, $attribute_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$deleted = $wpdb->delete(
			"{$wpdb->prefix}ggmc_product_attribute_values",
			array(
				'product_id'   => $product_id,
				'attribute_id' => $attribute_id,
			),
			array( '%d', '%d' )
		);

		return $deleted !== false;
	}

	/**
	 * Update product attribute value.
	 *
	 * @param int    $product_id   Product ID.
	 * @param int    $attribute_id Attribute ID.
	 * @param string $value        Attribute value.
	 * @return bool
	 */
	public function update_product_attribute_value( $product_id, $attribute_id, $value ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$updated = $wpdb->update(
			"{$wpdb->prefix}ggmc_product_attribute_values",
			array(
				'attribute_value' => sanitize_text_field( $value ),
				'updated_at'      => current_time( 'mysql' ),
			),
			array(
				'product_id'   => $product_id,
				'attribute_id' => $attribute_id,
			)
		);

		return $updated !== false;
	}

	/**
	 * Get product attributes with their values.
	 *
	 * @param int $product_id Product ID.
	 * @return array
	 */
	public function get_product_attributes( $product_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT pav.*, a.name, a.attribute_type, a.options, a.unit, a.icon, a.group_id,
				        g.name as group_name
				FROM {$wpdb->prefix}ggmc_product_attribute_values pav
				INNER JOIN {$wpdb->prefix}ggmc_attributes a ON pav.attribute_id = a.id
				LEFT JOIN {$wpdb->prefix}ggmc_attribute_groups g ON a.group_id = g.id
				WHERE pav.product_id = %d AND a.status = 'active'
				ORDER BY pav.display_order ASC, a.name ASC",
				$product_id
			)
		);
	}

	/**
	 * Set product attribute values in bulk.
	 *
	 * @param int   $product_id Product ID.
	 * @param array $values     Array of attribute_id => value pairs.
	 * @return bool
	 */
	public function set_product_attribute_values( $product_id, $values ) {
		global $wpdb;

		if ( ! is_array( $values ) ) {
			return false;
		}

		// Handle form data format: array of ['attribute_id' => X, 'value' => Y, 'display_order' => Z]
		// First, collect all submitted attribute IDs.
		$submitted_attr_ids = array();
		foreach ( $values as $item ) {
			if ( isset( $item['attribute_id'] ) ) {
				$submitted_attr_ids[] = intval( $item['attribute_id'] );
			}
		}

		// Delete any product attributes that were removed (not in submitted list).
		if ( ! empty( $submitted_attr_ids ) ) {
			$placeholders = implode( ',', array_fill( 0, count( $submitted_attr_ids ), '%d' ) );
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Safe usage with array merge.
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}ggmc_product_attribute_values
					WHERE product_id = %d AND attribute_id NOT IN ($placeholders)",
					array_merge( array( $product_id ), $submitted_attr_ids )
				)
			);
		} else {
			// No attributes submitted - remove all.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->delete(
				"{$wpdb->prefix}ggmc_product_attribute_values",
				array( 'product_id' => $product_id )
			);
		}

		// Now insert or update each submitted attribute.
		foreach ( $values as $item ) {
			if ( ! isset( $item['attribute_id'] ) ) {
				continue;
			}

			$attribute_id  = intval( $item['attribute_id'] );
			$value         = isset( $item['value'] ) ? sanitize_text_field( $item['value'] ) : '';
			$display_order = isset( $item['display_order'] ) ? intval( $item['display_order'] ) : 0;

			// Check if exists.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$existing = $wpdb->get_var(
				$wpdb->prepare(
					"SELECT id FROM {$wpdb->prefix}ggmc_product_attribute_values WHERE product_id = %d AND attribute_id = %d",
					$product_id,
					$attribute_id
				)
			);

			if ( $existing ) {
				// Update.
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->update(
					"{$wpdb->prefix}ggmc_product_attribute_values",
					array(
						'attribute_value' => $value,
						'display_order'   => $display_order,
						'updated_at'      => current_time( 'mysql' ),
					),
					array( 'id' => $existing )
				);
			} else {
				// Insert.
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->insert(
					"{$wpdb->prefix}ggmc_product_attribute_values",
					array(
						'product_id'      => $product_id,
						'attribute_id'    => $attribute_id,
						'attribute_value' => $value,
						'display_order'   => $display_order,
						'created_at'      => current_time( 'mysql' ),
						'updated_at'      => current_time( 'mysql' ),
					)
				);
			}
		}

		return true;
	}

	/**
	 * Reorder product attributes.
	 *
	 * @param int   $product_id      Product ID.
	 * @param array $attribute_order Array of attribute IDs in desired order.
	 * @return bool
	 */
	public function reorder_product_attributes( $product_id, $attribute_order ) {
		global $wpdb;

		if ( ! is_array( $attribute_order ) ) {
			return false;
		}

		$order = 0;
		foreach ( $attribute_order as $attribute_id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_product_attribute_values",
				array( 'display_order' => $order ),
				array(
					'product_id'   => $product_id,
					'attribute_id' => intval( $attribute_id ),
				)
			);
			++$order;
		}

		return true;
	}

	/**
	 * Get formatted product attributes for frontend display.
	 *
	 * @param int $product_id Product ID.
	 * @return array
	 */
	public function get_formatted_product_attributes( $product_id ) {
		$attributes = $this->get_product_attributes( $product_id );
		$formatted  = array();

		foreach ( $attributes as $attr ) {
			$formatted[] = array(
				'name'  => $attr->name,
				'value' => $attr->attribute_value,
				'unit'  => $attr->unit,
				'type'  => $attr->attribute_type,
				'icon'  => $attr->icon ?? '',
			);
		}

		return $formatted;
	}

	// =========================================================================
	// AJAX HANDLERS
	// =========================================================================

	/**
	 * AJAX: Create attribute group.
	 */
	public function ajax_create_attribute_group() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$result = $this->create_attribute_group( wp_unslash( $_POST ) );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success(
				array(
					'message'  => __( 'Attribute group created successfully', 'ggm-commerce' ),
					'group_id' => $result,
				)
			);
		}
	}

	/**
	 * AJAX: Update attribute group.
	 */
	public function ajax_update_attribute_group() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$group_id = intval( $_POST['group_id'] ?? 0 );
		$result   = $this->update_attribute_group( $group_id, wp_unslash( $_POST ) );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success( array( 'message' => __( 'Attribute group updated successfully', 'ggm-commerce' ) ) );
		}
	}

	/**
	 * AJAX: Delete attribute group.
	 */
	public function ajax_delete_attribute_group() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$group_id = intval( $_POST['group_id'] ?? 0 );
		$result   = $this->delete_attribute_group( $group_id );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success( array( 'message' => __( 'Attribute group deleted successfully', 'ggm-commerce' ) ) );
		}
	}

	/**
	 * AJAX: Create attribute.
	 */
	public function ajax_create_attribute() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$result = $this->create_attribute( wp_unslash( $_POST ) );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success(
				array(
					'message'      => __( 'Attribute created successfully', 'ggm-commerce' ),
					'attribute_id' => $result,
				)
			);
		}
	}

	/**
	 * AJAX: Update attribute.
	 */
	public function ajax_update_attribute() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$attribute_id = intval( $_POST['attribute_id'] ?? 0 );
		$result       = $this->update_attribute( $attribute_id, wp_unslash( $_POST ) );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success( array( 'message' => __( 'Attribute updated successfully', 'ggm-commerce' ) ) );
		}
	}

	/**
	 * AJAX: Delete attribute.
	 */
	public function ajax_delete_attribute() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$attribute_id = intval( $_POST['attribute_id'] ?? 0 );
		$result       = $this->delete_attribute( $attribute_id );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			wp_send_json_success( array( 'message' => __( 'Attribute deleted successfully', 'ggm-commerce' ) ) );
		}
	}

	/**
	 * AJAX: Add attribute to product.
	 */
	public function ajax_add_product_attribute() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$product_id   = intval( $_POST['product_id'] ?? 0 );
		$attribute_id = intval( $_POST['attribute_id'] ?? 0 );

		$result = $this->add_attribute_to_product( $product_id, $attribute_id, '' );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			$attribute = $this->get_attribute( $attribute_id );
			wp_send_json_success(
				array(
					'message'   => __( 'Attribute added successfully', 'ggm-commerce' ),
					'attribute' => $attribute,
				)
			);
		}
	}

	/**
	 * AJAX: Add attributes from group to product.
	 */
	public function ajax_add_product_attributes_from_group() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$product_id = intval( $_POST['product_id'] ?? 0 );
		$group_id   = intval( $_POST['group_id'] ?? 0 );

		$result = $this->add_attributes_from_group( $product_id, $group_id );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		} else {
			$attributes = $this->get_attributes( $group_id );
			wp_send_json_success(
				array(
					'message'    => __( 'Attributes added successfully', 'ggm-commerce' ),
					'added_ids'  => $result,
					'attributes' => $attributes,
				)
			);
		}
	}

	/**
	 * AJAX: Remove attribute from product.
	 */
	public function ajax_remove_product_attribute() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$product_id   = intval( $_POST['product_id'] ?? 0 );
		$attribute_id = intval( $_POST['attribute_id'] ?? 0 );

		$result = $this->remove_attribute_from_product( $product_id, $attribute_id );

		if ( $result ) {
			wp_send_json_success( array( 'message' => __( 'Attribute removed successfully', 'ggm-commerce' ) ) );
		} else {
			wp_send_json_error( array( 'message' => __( 'Failed to remove attribute', 'ggm-commerce' ) ) );
		}
	}

	/**
	 * AJAX: Get product attribute fields HTML.
	 */
	public function ajax_get_product_attribute_fields() {
		check_ajax_referer( 'ggm_commerce_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'ggm-commerce' ) ) );
		}

		$product_id = intval( $_POST['product_id'] ?? 0 );
		$attributes = $this->get_product_attributes( $product_id );

		ob_start();
		foreach ( $attributes as $attr ) {
			$this->render_attribute_row( $attr, $product_id );
		}
		$html = ob_get_clean();

		wp_send_json_success( array( 'html' => $html ) );
	}

	/**
	 * Render a single attribute row for the product edit form.
	 *
	 * @param object $attr       Attribute data.
	 * @param int    $product_id Product ID.
	 */
	public function render_attribute_row( $attr, $product_id ) {
		$options = ! empty( $attr->options ) ? json_decode( $attr->options, true ) : array();
		?>
		<tr class="ggmc-attribute-row" data-attribute-id="<?php echo esc_attr( $attr->attribute_id ); ?>">
			<td class="ggmc-attribute-drag">
				<span class="dashicons dashicons-menu" title="<?php esc_attr_e( 'Drag to reorder', 'ggm-commerce' ); ?>"></span>
			</td>
			<td class="ggmc-attribute-name">
				<?php echo esc_html( $attr->name ); ?>
			</td>
			<td class="ggmc-attribute-value">
				<?php if ( 'select' === $attr->attribute_type && ! empty( $options ) ) : ?>
					<select name="ggmc_attribute_values[<?php echo esc_attr( $attr->attribute_id ); ?>]" class="regular-text">
						<option value=""><?php esc_html_e( '-- Select --', 'ggm-commerce' ); ?></option>
						<?php foreach ( $options as $option ) : ?>
							<option value="<?php echo esc_attr( $option ); ?>" <?php selected( $attr->attribute_value, $option ); ?>>
								<?php echo esc_html( $option ); ?>
							</option>
						<?php endforeach; ?>
					</select>
				<?php elseif ( 'number' === $attr->attribute_type ) : ?>
					<input type="number" step="any"
						name="ggmc_attribute_values[<?php echo esc_attr( $attr->attribute_id ); ?>]"
						value="<?php echo esc_attr( $attr->attribute_value ); ?>"
						class="regular-text">
				<?php else : ?>
					<input type="text"
						name="ggmc_attribute_values[<?php echo esc_attr( $attr->attribute_id ); ?>]"
						value="<?php echo esc_attr( $attr->attribute_value ); ?>"
						class="regular-text">
				<?php endif; ?>
			</td>
			<td class="ggmc-attribute-unit">
				<?php echo esc_html( $attr->unit ); ?>
			</td>
			<td class="ggmc-attribute-actions">
				<button type="button" class="button button-small ggmc-remove-attribute"
					data-product-id="<?php echo esc_attr( $product_id ); ?>"
					data-attribute-id="<?php echo esc_attr( $attr->attribute_id ); ?>"
					title="<?php esc_attr_e( 'Remove', 'ggm-commerce' ); ?>">
					<span class="dashicons dashicons-no-alt"></span>
				</button>
			</td>
		</tr>
		<?php
	}
}

// Initialize attributes system.
GGM_Commerce_Attributes::get_instance();
