<?php

class RPSync_WooCommerce {

	public function __construct() {
		add_action( 'rpsync_process_product_data', array( $this, 'process_product_data' ), 1 );
		add_action( 'rpsync_process_product_changes', array( $this, 'log_product_changes' ), 10, 3 );
		add_action( 'delete_post', array( $this, 'delete_post' ), 10, 2 );
		add_action( 'wc_merge_after', array( $this, 'wc_merge_after' ) );
	}

	/**
	 * Process extracted product_data for WooCommerce
	 *
	 * @param $data
	 *
	 * @return void
	 */
	public function process_product_data( $numref ) {
		$parent_stock = array();

		// Load product data from RPSync database
		if ( empty( $product_data = RPSync::get_instance()->database->load_product_data( $numref ) ) ) {
			return;
		}

		// Process associated WooCommerce products
		foreach ( $product_data['skus'] as $sku => &$product ) {
			if ( ! empty( $parent_id = wp_get_post_parent_id( $product['product_id'] ) ) ) {
				$parent_stock[ $parent_id ][] = (int) $product['stock'];
			}

			// Skip empty / invalid product_id
			if ( empty( $product['product_id'] ) ) {
				continue;
			}
			$changes = $this->update_product( $product['product_id'], $product );
			if ( ! empty( $changes ) ) {
				do_action( 'rpsync_process_product_changes', $changes, $sku, $product_data );
			}
		}
		// Update parent stock level
		foreach ( $parent_stock as $parent_id => $stock ) {
			$stock_quantity = array_sum( $stock );
			update_post_meta( $parent_id, '_stock_status', $stock_quantity ? 'instock' : 'outofstock' );
		}
	}

	/**
	 * Update WooCommerce product postmeta / wc_product_meta_lookup
	 *
	 * @param $product_id
	 * @param $metas
	 *
	 * @return array
	 */
	public function update_product( $product_id, $metas ) {
		global $wpdb;
		$wc_product_meta_lookup = $wpdb->prefix . "wc_product_meta_lookup";
		$changes                = array();
		$set                    = array();
		foreach ( $metas as $key => $value ) {
			switch ( $key ) {
				// SKU (only update if previously empty)
				case 'sku':
					if ( empty( get_post_meta( $product_id, '_sku', true ) ) ) {
						$changes['sku'] = array( 'before' => '', 'after' => $value );
						update_post_meta( $product_id, '_sku', $value );
						$set['sku'] = "'$value'";
					}
					break;

				// Stock
				case 'stock':
					if ( $value !== ( $stock_quantity = (int) get_post_meta( $product_id, '_stock', true ) ) ) {
						$changes['stock'] = array( 'before' => $stock_quantity, 'after' => $value );
						update_post_meta( $product_id, '_stock', $value );
						update_post_meta( $product_id, '_stock_status', $value ? 'instock' : 'outofstock' );
						update_post_meta( $product_id, '_manage_stock', 'yes' );
						$set['stock_quantity'] = "$value";
						$set['stock_status']   = $value ? "'instock'" : "'outofstock'";
					}
					break;

				// Prices
				case 'regular_price':
					if ( $value !== ( $regular_price = get_post_meta( $product_id, '_regular_price', true ) ) ) {
						$changes['regular_price'] = array( 'before' => $regular_price, 'after' => $value );
						update_post_meta( $product_id, '_regular_price', $value );
						update_post_meta( $product_id, '_price', $value );
						$set['min_price'] = number_format( $value, 4, '.', '' );
						$set['max_price'] = number_format( $value, 4, '.', '' );
						$set['onsale']    = 0;
					}
					break;
				case 'sale_price':
					if ( $value !== ( $sale_price = get_post_meta( $product_id, '_sale_price', true ) ) ) {
						$changes['sale_price'] = array( 'before' => $sale_price, 'after' => $value );
						update_post_meta( $product_id, '_sale_price', $value );
						update_post_meta( $product_id, '_price', $value );
						$set['min_price'] = number_format( $value, 4, '.', '' );
						$set['onsale']    = 1;
					}
					break;
			}
		}
		if ( ! empty( $changes ) ) {
			$set = implode( ', ', array_map( function ( $value, $key ) {
				return "$key=$value";
			}, $set, array_keys( $set ) ) );
			$wpdb->query( "UPDATE $wc_product_meta_lookup SET $set WHERE product_id=$product_id;" );
		}
		return $changes;
	}

	/**
	 * Log product changes
	 *
	 * @param $changes
	 * @param $sku
	 * @param $product_data
	 *
	 * @return void
	 */
	public function log_product_changes( $changes, $sku, $product_data ) {
		if ( empty( $product = $product_data['skus'][ $sku ] ) ) {
			return;
		}
		$changes = implode( "\n", array_map( function ( $key, $value ) {
			return "  $key: ${value['before']} => ${value['after']}";
		}, array_keys( $changes ), $changes ) );
		RPSync::get_instance()->info( sprintf( "%s (Numref %s / SKU %s / ID: %d)\n", $product['title'], $product_data['numref'], $sku, $product['product_id'] ) . $changes );
	}

	/**
	 * Create WooCommerce product by numref
	 *
	 * @param string $numref
	 *
	 * @return bool
	 * @throws WC_Data_Exception
	 */
	public function create_product( string $numref ) {
		// Load Retailpoint product data
		if ( empty( $product_data = RPSync::get_instance()->database->load_product_data( $numref ) ) ) {
			return false;
		}

		// Check if WC product already exists
		if ( ! empty( $product_data['product_id'] ) ) {
			return false;
		}

		// Create simple / variable product
		if ( sizeof( $product_data['skus'] ) === 1 ) {
			// Create simple product with 'draft' status
			$product    = &$product_data['skus'][ array_key_first( $product_data['skus'] ) ];
			$wc_product = new WC_Product_Simple();
			$wc_product->set_status( 'draft' );
			$wc_product->set_name( $product['title'] );
			$wc_product->set_sku( $product['sku'] );
			$wc_product->set_manage_stock( 'yes' );
			if ( ! $wc_product->save() ) {
				return false;
			}
			// Update product_id in RPSync tables
			$product_data['product_id'] = $product['product_id'] = $wc_product->get_id();
			if ( ! RPSync::get_instance()->database->save_product_data( $product_data ) ) {
				return false;
			}
		} else {
			// Create variable product with 'draft' status
			$wc_product = new WC_Product_Variable();
			$wc_product->set_status( 'draft' );
			$wc_product->set_name( reset( $product_data['skus'] )['title'] );
			$wc_product->set_attributes( $this->create_variable_attributes( $product_data ) );
			if ( ! $wc_product->save() ) {
				return false;
			}
			$product_data['product_id'] = $wc_product->get_id();
			foreach ( $product_data['skus'] as &$product ) {
				// Create product variation
				$wc_variation = new WC_Product_Variation();
				$wc_variation->set_parent_id( $wc_product->get_id() );
				$wc_variation->set_status( 'publish' );
				$wc_variation->set_sku( $product['sku'] );
				$wc_variation->set_attributes( $product['attributes'] );
				$wc_variation->set_manage_stock( 'yes' );
				if ( ! $wc_variation->save() ) {
					return false;
				}
				$product['product_id'] = $wc_variation->get_id();
			}
			if ( ! RPSync::get_instance()->database->save_product_data( $product_data ) ) {
				return false;
			}
		}

		// Process newly created product synchronously
		do_action( 'rpsync_process_product_data', $numref );
		return true;
	}

	/**
	 * Create variable product attributes
	 *
	 * @param array $product_data
	 *
	 * @return array
	 */
	public function create_variable_attributes( array $product_data ) {
		$attributes = array();
		foreach ( $product_data['skus'] as $product ) {
			foreach ( $product['attributes'] as $name => $value ) {
				$attributes[ $name ][] = $value;
			}
		}

		return array_map( function ( $name, array $options ) {
			$wc_attribute = new WC_Product_Attribute();
			$wc_attribute->set_name( $name );
			$wc_attribute->set_options( $options );
			$wc_attribute->set_visible( true );
			if ( sizeof( $options ) > 1 ) {
				$wc_attribute->set_variation( true );
			}
			return $wc_attribute;
		}, array_keys( $attributes ), array_map( 'array_unique', $attributes ) );
	}

	/**
	 * Destroy WooCommerce product by numref
	 *
	 * @param string $numref
	 *
	 * @return bool
	 */
	public function destroy_product( string $numref ) {
		if ( empty( $product_data = RPSync::get_instance()->database->load_product_data( $numref ) ) ) {
			return false;
		}
		if ( empty( $product_data['product_id'] ) || ! wp_delete_post( $product_data['product_id'] ) ) {
			return false;
		}
		$product_data['product_id'] = null;
		foreach ( $product_data['skus'] as &$product ) {
			$product['product_id'] = null;
		}

		return RPSync::get_instance()->database->save_product_data( $product_data );
	}

	/**
	 * Delete product association when product is deleted
	 */
	public function delete_post( $post_id, WP_Post $post ) {
		if ( $post->post_type !== 'product' ) {
			return;
		}
		RPSync::get_instance()->database->delete_product_id( $post_id );
	}

	/**
	 * Update product_id in numrefs table after product merge
	 *
	 * @param $product_id
	 *
	 * @return void
	 */
	public function wc_merge_after( $product_id ) {
		$numrefs = RPSync::get_instance()->database->get_numrefs( $product_id );
		foreach ( $numrefs as $numref ) {
			$product_data               = RPSync::get_instance()->database->load_product_data( $numref );
			$product_data['product_id'] = $product_id;
			RPSync::get_instance()->database->save_product_data( $product_data );
			do_action( 'rpsync_process_product_data', $numref );
		}
	}
}
