<?php
/**
 * GGM Commerce Product Kits & Builds Management Class
 *
 * Handles kit/build operations including component management and stock operations.
 *
 * @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_Kits
 *
 * Manages product kits and builds - composite products made from other products.
 */
class GGM_Commerce_Kits {

	/**
	 * Kit type constant - can be broken down.
	 */
	const TYPE_KIT = 'kit';

	/**
	 * Build type constant - cannot be undone.
	 */
	const TYPE_BUILD = 'build';

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

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

	/**
	 * Constructor.
	 */
	private function __construct() {
		// Private constructor for singleton.
	}

	/**
	 * Check if the kits feature is activated via license.
	 *
	 * @return bool
	 */
	public static function is_feature_activated() {
		if ( ! function_exists( 'ggm_commerce_license' ) ) {
			return false;
		}
		$license = ggm_commerce_license();
		return $license->is_widget_activated( 'product-kits' );
	}

	/**
	 * Get valid kit types.
	 *
	 * @return array
	 */
	public function get_kit_types() {
		return array(
			self::TYPE_KIT   => __( 'Kit (can be broken down)', 'ggm-commerce' ),
			self::TYPE_BUILD => __( 'Build (permanent assembly)', 'ggm-commerce' ),
		);
	}

	/**
	 * Get product types for dropdown.
	 *
	 * @return array
	 */
	public function get_product_types() {
		return array(
			'simple' => __( 'Simple Product', 'ggm-commerce' ),
			'kit'    => __( 'Kit (can be broken down)', 'ggm-commerce' ),
			'build'  => __( 'Build (permanent assembly)', 'ggm-commerce' ),
		);
	}

	// =========================================================================
	// Kit Definition CRUD
	// =========================================================================

	/**
	 * Create a new kit/build definition for a product.
	 *
	 * @param int    $product_id   The product ID.
	 * @param string $type         Kit type ('kit' or 'build').
	 * @param array  $components   Array of components: [['product_id' => int, 'quantity' => int], ...].
	 * @param bool   $auto_price   Whether to auto-calculate price from components.
	 * @return int|WP_Error Kit ID on success, WP_Error on failure.
	 */
	public function create_kit( $product_id, $type, $components = array(), $auto_price = true ) {
		global $wpdb;

		$product_id = absint( $product_id );
		$type       = in_array( $type, array( self::TYPE_KIT, self::TYPE_BUILD ), true ) ? $type : self::TYPE_KIT;

		// Check product exists.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$product = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT id, name FROM {$wpdb->prefix}ggmc_products WHERE id = %d",
				$product_id
			)
		);

		if ( ! $product ) {
			return new WP_Error( 'product_not_found', __( 'Product not found.', 'ggm-commerce' ) );
		}

		// Check if kit already exists for this product.
		$existing = $this->get_kit_by_product_id( $product_id );
		if ( $existing ) {
			return new WP_Error( 'kit_exists', __( 'A kit definition already exists for this product.', 'ggm-commerce' ) );
		}

		// Validate components for circular references.
		if ( ! empty( $components ) && $this->has_circular_reference( $product_id, $components ) ) {
			return new WP_Error( 'circular_reference', __( 'Cannot add a kit/build as a component of itself.', 'ggm-commerce' ) );
		}

		// Insert kit definition.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->insert(
			"{$wpdb->prefix}ggmc_product_kits",
			array(
				'product_id'           => $product_id,
				'kit_type'             => $type,
				'auto_calculate_price' => $auto_price ? 1 : 0,
				'created_at'           => current_time( 'mysql' ),
				'updated_at'           => current_time( 'mysql' ),
			),
			array( '%d', '%s', '%d', '%s', '%s' )
		);

		if ( false === $result ) {
			return new WP_Error( 'db_error', __( 'Failed to create kit definition.', 'ggm-commerce' ) );
		}

		$kit_id = $wpdb->insert_id;

		// Update product type.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->update(
			"{$wpdb->prefix}ggmc_products",
			array( 'product_type' => $type ),
			array( 'id' => $product_id ),
			array( '%s' ),
			array( '%d' )
		);

		// Add components if provided.
		if ( ! empty( $components ) ) {
			$this->update_kit_components( $kit_id, $components );
		}

		return $kit_id;
	}

	/**
	 * Get kit definition by product ID.
	 *
	 * @param int $product_id The product ID.
	 * @return object|null Kit object or null if not found.
	 */
	public function get_kit_by_product_id( $product_id ) {
		global $wpdb;

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

	/**
	 * Get kit definition by kit ID.
	 *
	 * @param int $kit_id The kit ID.
	 * @return object|null Kit object or null if not found.
	 */
	public function get_kit( $kit_id ) {
		global $wpdb;

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

	/**
	 * Update kit type and settings.
	 *
	 * @param int    $kit_id     The kit ID.
	 * @param string $type       Kit type.
	 * @param bool   $auto_price Auto-calculate price.
	 * @return bool|WP_Error
	 */
	public function update_kit( $kit_id, $type, $auto_price = true ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		$type = in_array( $type, array( self::TYPE_KIT, self::TYPE_BUILD ), true ) ? $type : self::TYPE_KIT;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->update(
			"{$wpdb->prefix}ggmc_product_kits",
			array(
				'kit_type'             => $type,
				'auto_calculate_price' => $auto_price ? 1 : 0,
				'updated_at'           => current_time( 'mysql' ),
			),
			array( 'id' => $kit_id ),
			array( '%s', '%d', '%s' ),
			array( '%d' )
		);

		// Update product type.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->update(
			"{$wpdb->prefix}ggmc_products",
			array( 'product_type' => $type ),
			array( 'id' => $kit->product_id ),
			array( '%s' ),
			array( '%d' )
		);

		return false !== $result;
	}

	/**
	 * Delete kit definition.
	 *
	 * @param int $kit_id The kit ID.
	 * @return bool|WP_Error
	 */
	public function delete_kit( $kit_id ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		// Delete components first.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			"{$wpdb->prefix}ggmc_kit_components",
			array( 'kit_id' => $kit_id ),
			array( '%d' )
		);

		// Delete kit.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->delete(
			"{$wpdb->prefix}ggmc_product_kits",
			array( 'id' => $kit_id ),
			array( '%d' )
		);

		// Reset product type to simple.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->update(
			"{$wpdb->prefix}ggmc_products",
			array( 'product_type' => 'simple' ),
			array( 'id' => $kit->product_id ),
			array( '%s' ),
			array( '%d' )
		);

		return false !== $result;
	}

	// =========================================================================
	// Component Management
	// =========================================================================

	/**
	 * Get components for a kit.
	 *
	 * @param int $kit_id The kit ID.
	 * @return array Array of component objects with product details.
	 */
	public function get_kit_components( $kit_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT c.*, p.name AS product_name, p.sku, p.price, p.stock, p.image_id, p.status AS product_status
				 FROM {$wpdb->prefix}ggmc_kit_components c
				 LEFT JOIN {$wpdb->prefix}ggmc_products p ON c.component_product_id = p.id
				 WHERE c.kit_id = %d
				 ORDER BY c.display_order ASC, c.id ASC",
				absint( $kit_id )
			)
		);
	}

	/**
	 * Get components for a kit by product ID.
	 *
	 * @param int $product_id The product ID.
	 * @return array Array of component objects.
	 */
	public function get_components_by_product_id( $product_id ) {
		$kit = $this->get_kit_by_product_id( $product_id );
		if ( ! $kit ) {
			return array();
		}
		return $this->get_kit_components( $kit->id );
	}

	/**
	 * Update kit components.
	 *
	 * @param int   $kit_id     The kit ID.
	 * @param array $components Array of components: [['product_id' => int, 'quantity' => int], ...].
	 * @return bool|WP_Error
	 */
	public function update_kit_components( $kit_id, $components ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		// Validate for circular references.
		if ( $this->has_circular_reference( $kit->product_id, $components ) ) {
			return new WP_Error( 'circular_reference', __( 'Cannot add a kit/build as a component of itself.', 'ggm-commerce' ) );
		}

		// Start transaction.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query( 'START TRANSACTION' );

		try {
			// Delete existing components.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->delete(
				"{$wpdb->prefix}ggmc_kit_components",
				array( 'kit_id' => $kit_id ),
				array( '%d' )
			);

			// Insert new components.
			$display_order = 0;
			foreach ( $components as $component ) {
				$component_product_id = absint( $component['product_id'] ?? 0 );
				$quantity             = max( 1, absint( $component['quantity'] ?? 1 ) );

				if ( $component_product_id <= 0 ) {
					continue;
				}

				// Verify component product exists and is simple.
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$component_product = $wpdb->get_row(
					$wpdb->prepare(
						"SELECT id, product_type FROM {$wpdb->prefix}ggmc_products WHERE id = %d",
						$component_product_id
					)
				);

				if ( ! $component_product ) {
					continue; // Skip invalid products.
				}

				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->insert(
					"{$wpdb->prefix}ggmc_kit_components",
					array(
						'kit_id'               => $kit_id,
						'component_product_id' => $component_product_id,
						'quantity'             => $quantity,
						'display_order'        => $display_order,
						'created_at'           => current_time( 'mysql' ),
					),
					array( '%d', '%d', '%d', '%d', '%s' )
				);

				++$display_order;
			}

			// Update kit timestamp.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_product_kits",
				array( 'updated_at' => current_time( 'mysql' ) ),
				array( 'id' => $kit_id ),
				array( '%s' ),
				array( '%d' )
			);

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'COMMIT' );
			return true;

		} catch ( Exception $e ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'save_failed', $e->getMessage() );
		}
	}

	/**
	 * Add a single component to a kit.
	 *
	 * @param int $kit_id               The kit ID.
	 * @param int $component_product_id The component product ID.
	 * @param int $quantity             The quantity.
	 * @return int|WP_Error Component ID on success, WP_Error on failure.
	 */
	public function add_component( $kit_id, $component_product_id, $quantity = 1 ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		// Check circular reference.
		if ( $component_product_id == $kit->product_id ) {
			return new WP_Error( 'circular_reference', __( 'Cannot add a product as a component of itself.', 'ggm-commerce' ) );
		}

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

		if ( $existing ) {
			return new WP_Error( 'component_exists', __( 'This product is already a component of this kit.', 'ggm-commerce' ) );
		}

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

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->insert(
			"{$wpdb->prefix}ggmc_kit_components",
			array(
				'kit_id'               => $kit_id,
				'component_product_id' => absint( $component_product_id ),
				'quantity'             => max( 1, absint( $quantity ) ),
				'display_order'        => ( $max_order ?? -1 ) + 1,
				'created_at'           => current_time( 'mysql' ),
			),
			array( '%d', '%d', '%d', '%d', '%s' )
		);

		if ( false === $result ) {
			return new WP_Error( 'db_error', __( 'Failed to add component.', 'ggm-commerce' ) );
		}

		return $wpdb->insert_id;
	}

	/**
	 * Remove a component from a kit.
	 *
	 * @param int $component_id The component ID.
	 * @return bool|WP_Error
	 */
	public function remove_component( $component_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->delete(
			"{$wpdb->prefix}ggmc_kit_components",
			array( 'id' => absint( $component_id ) ),
			array( '%d' )
		);

		return false !== $result;
	}

	// =========================================================================
	// Stock Calculations
	// =========================================================================

	/**
	 * Calculate the maximum number of kits that can be created from current stock.
	 *
	 * @param int $kit_id The kit ID.
	 * @return int Maximum creatable units.
	 */
	public function calculate_max_creatable( $kit_id ) {
		$components = $this->get_kit_components( $kit_id );

		if ( empty( $components ) ) {
			return 0;
		}

		$max = PHP_INT_MAX;

		foreach ( $components as $component ) {
			$available = (int) $component->stock;
			$required  = (int) $component->quantity;

			if ( $required <= 0 ) {
				continue;
			}

			$possible = floor( $available / $required );
			$max      = min( $max, $possible );
		}

		return $max === PHP_INT_MAX ? 0 : (int) $max;
	}

	/**
	 * Check component availability for creating a specific quantity.
	 *
	 * @param int $kit_id   The kit ID.
	 * @param int $quantity Desired quantity to create.
	 * @return array Status array with availability info per component.
	 */
	public function check_component_availability( $kit_id, $quantity ) {
		$components = $this->get_kit_components( $kit_id );
		$result     = array(
			'can_create'     => true,
			'components'     => array(),
			'limiting_items' => array(),
		);

		foreach ( $components as $component ) {
			$required  = (int) $component->quantity * (int) $quantity;
			$available = (int) $component->stock;
			$shortage  = max( 0, $required - $available );

			$component_status = array(
				'product_id'   => $component->component_product_id,
				'product_name' => $component->product_name,
				'required'     => $required,
				'available'    => $available,
				'shortage'     => $shortage,
				'sufficient'   => $shortage === 0,
			);

			$result['components'][] = $component_status;

			if ( $shortage > 0 ) {
				$result['can_create']       = false;
				$result['limiting_items'][] = $component_status;
			}
		}

		return $result;
	}

	/**
	 * Calculate total component price.
	 *
	 * @param int $kit_id The kit ID.
	 * @return float Total price of all components.
	 */
	public function calculate_component_price( $kit_id ) {
		$components = $this->get_kit_components( $kit_id );
		$total      = 0.0;

		foreach ( $components as $component ) {
			$price    = (float) $component->price;
			$quantity = (int) $component->quantity;
			$total   += $price * $quantity;
		}

		return $total;
	}

	// =========================================================================
	// Stock Operations
	// =========================================================================

	/**
	 * Create kit stock by deducting components from inventory.
	 *
	 * @param int  $kit_id   The kit ID.
	 * @param int  $quantity Quantity to create.
	 * @param bool $override Allow override of stock limits.
	 * @return bool|WP_Error True on success, WP_Error on failure.
	 */
	public function create_kit_stock( $kit_id, $quantity, $override = false ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		$quantity   = max( 1, absint( $quantity ) );
		$components = $this->get_kit_components( $kit_id );

		if ( empty( $components ) ) {
			return new WP_Error( 'no_components', __( 'Kit has no components defined.', 'ggm-commerce' ) );
		}

		// Check availability.
		$availability = $this->check_component_availability( $kit_id, $quantity );

		if ( ! $availability['can_create'] && ! $override ) {
			$shortage_list = array();
			foreach ( $availability['limiting_items'] as $item ) {
				$shortage_list[] = sprintf( '%s (need %d, have %d)', $item['product_name'], $item['required'], $item['available'] );
			}
			return new WP_Error(
				'insufficient_stock',
				__( 'Insufficient component stock: ', 'ggm-commerce' ) . implode( ', ', $shortage_list )
			);
		}

		// Start transaction.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query( 'START TRANSACTION' );

		try {
			$user_id = get_current_user_id();

			// Deduct component stock.
			foreach ( $components as $component ) {
				$deduct        = (int) $component->quantity * $quantity;
				$current_stock = (int) $component->stock;
				$new_stock     = max( 0, $current_stock - $deduct );

				// Update component stock.
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->update(
					"{$wpdb->prefix}ggmc_products",
					array( 'stock' => $new_stock ),
					array( 'id' => $component->component_product_id ),
					array( '%d' ),
					array( '%d' )
				);

				// Log movement.
				$this->log_stock_movement(
					$component->component_product_id,
					-$deduct,
					$current_stock,
					$new_stock,
					'kit_creation',
					$kit->product_id,
					sprintf( 'Used in kit creation: %d units', $quantity ),
					$user_id,
					$override
				);
			}

			// Increase kit product stock.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$kit_product = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT stock FROM {$wpdb->prefix}ggmc_products WHERE id = %d",
					$kit->product_id
				)
			);

			$kit_current_stock = (int) ( $kit_product->stock ?? 0 );
			$kit_new_stock     = $kit_current_stock + $quantity;

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_products",
				array( 'stock' => $kit_new_stock ),
				array( 'id' => $kit->product_id ),
				array( '%d' ),
				array( '%d' )
			);

			// Log kit stock increase.
			$this->log_stock_movement(
				$kit->product_id,
				$quantity,
				$kit_current_stock,
				$kit_new_stock,
				'kit_creation',
				$kit_id,
				sprintf( 'Created %d kit units', $quantity ),
				$user_id,
				$override
			);

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'COMMIT' );
			return true;

		} catch ( Exception $e ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'creation_failed', $e->getMessage() );
		}
	}

	/**
	 * Breakdown kit stock, returning components to inventory.
	 * Only available for 'kit' type, not 'build' type.
	 *
	 * @param int $kit_id   The kit ID.
	 * @param int $quantity Quantity to breakdown.
	 * @return bool|WP_Error True on success, WP_Error on failure.
	 */
	public function breakdown_kit_stock( $kit_id, $quantity ) {
		global $wpdb;

		$kit = $this->get_kit( $kit_id );
		if ( ! $kit ) {
			return new WP_Error( 'kit_not_found', __( 'Kit not found.', 'ggm-commerce' ) );
		}

		// Only kits can be broken down, not builds.
		if ( self::TYPE_BUILD === $kit->kit_type ) {
			return new WP_Error( 'cannot_breakdown', __( 'Build products cannot be broken down.', 'ggm-commerce' ) );
		}

		$quantity = max( 1, absint( $quantity ) );

		// Check kit has enough stock.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$kit_product = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT stock, name FROM {$wpdb->prefix}ggmc_products WHERE id = %d",
				$kit->product_id
			)
		);

		if ( ! $kit_product || (int) $kit_product->stock < $quantity ) {
			return new WP_Error(
				'insufficient_kit_stock',
				sprintf(
					/* translators: 1: available stock quantity, 2: requested quantity */
					__( 'Not enough kit stock. Available: %1$d, Requested: %2$d', 'ggm-commerce' ),
					(int) ( $kit_product->stock ?? 0 ),
					$quantity
				)
			);
		}

		$components = $this->get_kit_components( $kit_id );

		if ( empty( $components ) ) {
			return new WP_Error( 'no_components', __( 'Kit has no components defined.', 'ggm-commerce' ) );
		}

		// Start transaction.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query( 'START TRANSACTION' );

		try {
			$user_id = get_current_user_id();

			// Return component stock.
			foreach ( $components as $component ) {
				$return        = (int) $component->quantity * $quantity;
				$current_stock = (int) $component->stock;
				$new_stock     = $current_stock + $return;

				// Update component stock.
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->update(
					"{$wpdb->prefix}ggmc_products",
					array( 'stock' => $new_stock ),
					array( 'id' => $component->component_product_id ),
					array( '%d' ),
					array( '%d' )
				);

				// Log movement.
				$this->log_stock_movement(
					$component->component_product_id,
					$return,
					$current_stock,
					$new_stock,
					'kit_breakdown',
					$kit->product_id,
					sprintf( 'Returned from kit breakdown: %d units', $quantity ),
					$user_id,
					false
				);
			}

			// Decrease kit product stock.
			$kit_current_stock = (int) $kit_product->stock;
			$kit_new_stock     = $kit_current_stock - $quantity;

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_products",
				array( 'stock' => $kit_new_stock ),
				array( 'id' => $kit->product_id ),
				array( '%d' ),
				array( '%d' )
			);

			// Log kit stock decrease.
			$this->log_stock_movement(
				$kit->product_id,
				-$quantity,
				$kit_current_stock,
				$kit_new_stock,
				'kit_breakdown',
				$kit_id,
				sprintf( 'Broke down %d kit units', $quantity ),
				$user_id,
				false
			);

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'COMMIT' );
			return true;

		} catch ( Exception $e ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'breakdown_failed', $e->getMessage() );
		}
	}

	/**
	 * Log a stock movement.
	 *
	 * @param int    $product_id     Product ID.
	 * @param int    $quantity_change Change amount (negative for decrease).
	 * @param int    $previous_stock  Previous stock level.
	 * @param int    $new_stock       New stock level.
	 * @param string $movement_type   Type of movement.
	 * @param int    $reference_id    Reference ID (kit/order ID).
	 * @param string $notes           Notes.
	 * @param int    $user_id         User ID.
	 * @param bool   $override        Whether override was enabled.
	 * @return int|false Insert ID or false on failure.
	 */
	private function log_stock_movement( $product_id, $quantity_change, $previous_stock, $new_stock, $movement_type, $reference_id = null, $notes = '', $user_id = 0, $override = false ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->insert(
			"{$wpdb->prefix}ggmc_stock_movements",
			array(
				'product_id'       => absint( $product_id ),
				'quantity_change'  => (int) $quantity_change,
				'previous_stock'   => (int) $previous_stock,
				'new_stock'        => (int) $new_stock,
				'movement_type'    => sanitize_text_field( $movement_type ),
				'reference_id'     => $reference_id ? absint( $reference_id ) : null,
				'notes'            => sanitize_textarea_field( $notes ),
				'user_id'          => absint( $user_id ),
				'override_enabled' => $override ? 1 : 0,
				'created_at'       => current_time( 'mysql' ),
			),
			array( '%d', '%d', '%d', '%d', '%s', '%d', '%s', '%d', '%d', '%s' )
		);

		return false !== $result ? $wpdb->insert_id : false;
	}

	/**
	 * Get stock movement history for a product.
	 *
	 * @param int $product_id Product ID.
	 * @param int $limit      Max records to return.
	 * @return array Array of movement records.
	 */
	public function get_stock_movements( $product_id, $limit = 50 ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT m.*, u.display_name AS user_name
				 FROM {$wpdb->prefix}ggmc_stock_movements m
				 LEFT JOIN {$wpdb->users} u ON m.user_id = u.ID
				 WHERE m.product_id = %d
				 ORDER BY m.created_at DESC
				 LIMIT %d",
				absint( $product_id ),
				absint( $limit )
			)
		);
	}

	// =========================================================================
	// Validation Helpers
	// =========================================================================

	/**
	 * Check for circular references (a kit containing itself or a kit that contains it).
	 *
	 * @param int   $product_id The product ID being configured as a kit.
	 * @param array $components The proposed components.
	 * @return bool True if circular reference detected.
	 */
	private function has_circular_reference( $product_id, $components ) {
		foreach ( $components as $component ) {
			$component_id = absint( $component['product_id'] ?? 0 );

			// Direct self-reference.
			if ( $component_id === $product_id ) {
				return true;
			}

			// Check if component is a kit that contains this product.
			$component_kit = $this->get_kit_by_product_id( $component_id );
			if ( $component_kit ) {
				$nested_components = $this->get_kit_components( $component_kit->id );
				foreach ( $nested_components as $nested ) {
					if ( (int) $nested->component_product_id === $product_id ) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Check if a product is used as a component in any kit.
	 *
	 * @param int $product_id Product ID.
	 * @return array Array of kit product IDs using this product.
	 */
	public function get_kits_using_product( $product_id ) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT k.product_id
				 FROM {$wpdb->prefix}ggmc_kit_components c
				 INNER JOIN {$wpdb->prefix}ggmc_product_kits k ON c.kit_id = k.id
				 WHERE c.component_product_id = %d",
				absint( $product_id )
			)
		);
	}

	/**
	 * Get all simple products (for component selection).
	 *
	 * @param string $search   Search term.
	 * @param int    $exclude  Product ID to exclude.
	 * @param int    $limit    Max results.
	 * @return array Array of products.
	 */
	public function get_available_components( $search = '', $exclude = 0, $limit = 20 ) {
		global $wpdb;

		$where = "WHERE (product_type = 'simple' OR product_type IS NULL OR product_type = '')";

		if ( $exclude > 0 ) {
			$where .= $wpdb->prepare( ' AND id != %d', $exclude );
		}

		if ( ! empty( $search ) ) {
			$like   = '%' . $wpdb->esc_like( $search ) . '%';
			$where .= $wpdb->prepare( ' AND (name LIKE %s OR sku LIKE %s)', $like, $like );
		}

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $where is safely built above
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, name, sku, price, stock, image_id
				 FROM {$wpdb->prefix}ggmc_products
				 {$where}
				 ORDER BY name ASC
				 LIMIT %d",
				$limit
			)
		);
	}
}

// Initialize singleton.
GGM_Commerce_Kits::get_instance();
