<?php
/**
 * Class for extending WooCommerce Core Bookable Product Imports.
 *
 * @package WooCommerce Bookings
 */

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

/**
 * WC_Booking_Products_Import class.
 *
 * Handles importing of bookable products.
 */
class WC_Booking_Products_Import {
	/**
	 * The data properties we'd like to include in the import.
	 *
	 * @deprecated 3.6.0 Use WC_Booking_Products_Export::get_column_labels() instead.
	 *
	 * @var array
	 */
	protected $properties = array();

	/**
	 * Merges booking product data into the parent object.
	 *
	 * @param int|WC_Product|object $product Product to init.
	 */
	public function __construct( $product = 0 ) {
		add_filter( 'woocommerce_csv_product_import_mapping_options', array( $this, 'add_mapped_fields' ), 20, 2 );
		add_filter( 'woocommerce_csv_product_import_mapping_default_columns', array( $this, 'auto_detect_columns' ), 20, 2 );
		add_filter( 'woocommerce_product_import_pre_insert_product_object', array( $this, 'convert_person_types' ), 20, 2 );
	}

	/**
	 * Set up the bookable properties used during import.
	 *
	 * @deprecated 3.6.0 Labels now live in WC_Booking_Products_Export::get_column_labels().
	 */
	public function set_default_properties() {
		wc_deprecated_function( __METHOD__, '3.6.0', 'WC_Booking_Products_Export::get_column_labels' );
		$this->properties = WC_Booking_Products_Export::get_column_labels();
	}

	/**
	 * Register the bookable columns in the import column-mapping dropdown.
	 *
	 * WC core expects `key => label`, so the shared label map is added as-is.
	 *
	 * @since 1.11.0
	 *
	 * @param array $mappings Column key => label options shown in the dropdown.
	 *
	 * @return array
	 */
	public function add_mapped_fields( $mappings ) {
		return $mappings + WC_Booking_Products_Export::get_column_labels();
	}

	/**
	 * Auto-detect bookable columns on the import mapping screen.
	 *
	 * WC core matches CSV header text against a `label => key` table to
	 * pre-select the right field, so the shared label map is flipped here.
	 *
	 * @since 3.6.0
	 *
	 * @param array $columns Header label => column key auto-detection table.
	 *
	 * @return array
	 */
	public function auto_detect_columns( $columns ) {
		$labels = WC_Booking_Products_Export::get_column_labels();

		// Current, human-readable headers (e.g. "Default date availability").
		$columns += array_flip( $labels );

		/*
		 * Backwards compatibility — REMOVE in a future release.
		 *
		 * Bookable products exported before 3.6.0 used raw, underscored
		 * headers derived from the property key (e.g. "Default_date_availability"
		 * via ucfirst()). Map those legacy headers back to their keys so older
		 * CSV files still auto-detect on import. New exports use the labels
		 * above, so this block can be dropped once legacy files have aged out.
		 *
		 * @todo Remove this legacy header mapping. See https://github.com/woocommerce/woocommerce-bookings.
		 */
		foreach ( array_keys( $labels ) as $key ) {
			$columns[ ucfirst( $key ) ] = $key;
		}

		return $columns;
	}

	/**
	 * We store the person type data as a serialized array on export.
	 * This function converts it from a serialized array to the required
	 * objects before we save the product.
	 *
	 * @since 1.11.0
	 *
	 * @param WC_Product $product Could be bookable or standard product.
	 * @param array $data Raw import data added tot the WC_Product.
	 *
	 * @return array
	 */
	public function convert_person_types( $product, $data ) {
		if ( ! is_callable( array( $product, 'get_person_types' ) ) ) {
			return $product;
		}

		// The importer assigns the column verbatim, so the property holds the
		// raw JSON string wrapped in an array ( (array) '[...]' ). When the
		// first element isn't a string there is nothing to convert — either the
		// column wasn't imported or the person types are already objects (e.g.
		// an update) — so leave the product untouched rather than clobbering it.
		//
		// This must run regardless of has_person_types(): the export always
		// writes a Person_types column (even an empty "[]"), so products with
		// person types disabled would otherwise leave the raw string in place
		// and fatal in the data store when it tries to ->save() a string.
		$import_types = $product->get_person_types( 'edit' );
		if ( ! is_array( $import_types ) || empty( $import_types ) || ! is_string( $import_types[0] ) ) {
			return $product;
		}

		$import_types = json_decode( $import_types[0], true );
		if ( ! is_array( $import_types ) ) {
			$import_types = array();
		}

		$person_types = array();
		foreach ( $import_types as $person_type_data ){
			$person_type = new WC_Product_Booking_Person_Type();
			$person_type->set_block_cost( $person_type_data['block_cost'] );
			$person_type->set_cost( $person_type_data['cost'] );
			$person_type->set_description( $person_type_data['description'] );
			$person_type->set_max( $person_type_data['max'] );
			$person_type->set_min( $person_type_data['min'] );
			$person_type->set_name( $person_type_data['name'] );
			$person_type->set_parent_id( $product->get_id() );
			$person_type->set_sort_order( $person_type_data['sort_order'] );
			$person_type->save();

			$person_types[] = $person_type;
		}

		$product->set_person_types( $person_types );
		return $product;
	}
}
