<?php
/**
 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @package MetaCommerce
 */

namespace WooCommerce\Facebook\API\Plugin\Settings;

use WooCommerce\Facebook\API\Plugin\AbstractRESTEndpoint;
use WooCommerce\Facebook\API\Plugin\Settings\Update\Request as UpdateRequest;
use WooCommerce\Facebook\API\Plugin\Settings\Uninstall\Request as UninstallRequest;
use WooCommerce\Facebook\Framework\Logger;

defined( 'ABSPATH' ) || exit;

/**
 * Settings REST API endpoint handler.
 *
 * @since 3.5.0
 */
class Handler extends AbstractRESTEndpoint {

	/**
	 * Register routes for this endpoint.
	 *
	 * @since 3.5.0
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			$this->get_namespace(),
			'/settings/update',
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'handle_update' ],
				'permission_callback' => [ $this, 'permission_callback' ],
			]
		);

		register_rest_route(
			$this->get_namespace(),
			'/settings/uninstall',
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'handle_uninstall' ],
				'permission_callback' => [ $this, 'permission_callback' ],
			]
		);
	}

	/**
	 * Handle the update settings request.
	 *
	 * @since 3.5.0
	 * @http_method POST
	 * @description Update Facebook settings
	 *
	 * @param \WP_REST_Request $wp_request The WordPress request object.
	 * @return \WP_REST_Response
	 */
	public function handle_update( \WP_REST_Request $wp_request ): \WP_REST_Response {
		try {
			$request           = new UpdateRequest( $wp_request );
			$request_data      = $request->get_data();
			$validation_result = $request->validate();

			if ( is_wp_error( $validation_result ) ) {
				return $this->error_response(
					$validation_result->get_error_message(),
					400
				);
			}

			// Check if we should trigger product sync and/or metadata feed uploads for this update
			// Only trigger products and sets sync if catalog id is being updated
			$should_trigger_products_and_sets_sync = ! empty( $request_data['product_catalog_id'] ) && facebook_for_woocommerce()->get_integration()->get_product_catalog_id() !== $request_data['product_catalog_id'];
			// Only trigger metadata feed uploads if CPI id is being updated
			$should_trigger_metadata_feed_uploads = ! empty( $request_data['commerce_partner_integration_id'] ) && facebook_for_woocommerce()->get_connection_handler()->get_commerce_partner_integration_id() !== $request_data['commerce_partner_integration_id'];

			// Map parameters to options and update settings
			$options = $this->map_params_to_options( $request_data );
			$this->update_settings( $options );

			// Update connection status flags
			$this->update_connection_status( $request_data );

			// Maybe trigger products sync and/or metadata feed uploads
			$this->maybe_trigger_feed_uploads( $should_trigger_products_and_sets_sync, $should_trigger_metadata_feed_uploads, $request_data );

			return $this->success_response(
				[
					'message' => __( 'Facebook settings updated successfully', 'facebook-for-woocommerce' ),
				]
			);
		} catch ( \Exception $e ) {
			return $this->error_response(
				$e->getMessage(),
				500
			);
		}
	}

	/**
	 * Handle the uninstall request.
	 *
	 * @since 3.5.0
	 * @http_method POST
	 * @description Uninstall Facebook integration
	 *
	 * @param \WP_REST_Request $wp_request The WordPress request object.
	 * @return \WP_REST_Response
	 */
	public function handle_uninstall( \WP_REST_Request $wp_request ): \WP_REST_Response {
		try {
			$request           = new UninstallRequest( $wp_request );
			$validation_result = $request->validate();

			if ( is_wp_error( $validation_result ) ) {
				return $this->error_response(
					$validation_result->get_error_message(),
					400
				);
			}

			// Clear integration options
			$this->clear_integration_options();

			return $this->success_response(
				[
					'message' => __( 'Facebook integration successfully uninstalled', 'facebook-for-woocommerce' ),
				]
			);
		} catch ( \Exception $e ) {
			return $this->error_response(
				$e->getMessage(),
				500
			);
		}
	}

	/**
	 * Maps request parameters to WooCommerce options.
	 *
	 * @since 3.5.0
	 *
	 * @param array $params Request parameters.
	 * @return array Mapped options.
	 */
	private function map_params_to_options( array $params ): array {
		$options = [];

		// Map access tokens
		if ( ! empty( $params['access_token'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_ACCESS_TOKEN ] = $params['access_token'];
		}

		if ( ! empty( $params['commerce_merchant_settings_id'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_COMMERCE_MERCHANT_SETTINGS_ID ] = $params['commerce_merchant_settings_id'];
		}

		if ( ! empty( $params['commerce_partner_integration_id'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_COMMERCE_PARTNER_INTEGRATION_ID ] = $params['commerce_partner_integration_id'];
		}

		if ( ! empty( $params['installed_features'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_INSTALLED_FEATURES ] = $params['installed_features'];
		}

		if ( ! empty( $params['merchant_access_token'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_MERCHANT_ACCESS_TOKEN ] = $params['merchant_access_token'];
		}

		if ( ! empty( $params['page_access_token'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN ] = $params['page_access_token'];
		}

		if ( ! empty( $params['page_id'] ) ) {
			update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, $params['page_id'] );
		}

		// Prefer pixel from installed_features if available
		$pixel_from_features = '';

		if ( ! empty( $params['installed_features'] ) && is_array( $params['installed_features'] ) ) {
			foreach ( $params['installed_features'] as $feature ) {

				$feature_type = $feature['feature_type'] ?? '';
				$pixel_id     = $feature['connected_assets']['pixel_id'] ?? '';

				if ( 'pixel' === $feature_type && ! empty( $pixel_id ) ) {
					$pixel_from_features = $pixel_id;
					break;
				}
			}
		}

		$pixel_to_use = ! empty( $pixel_from_features ) ? $pixel_from_features : ( $params['pixel_id'] ?? '' );

		if ( ! empty( $pixel_to_use ) ) {
			$options[ \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_to_use;
			update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, $pixel_to_use );
		}

		if ( ! empty( $params['product_catalog_id'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID ] = $params['product_catalog_id'];
		}

		if ( ! empty( $params['profiles'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_PROFILES ] = $params['profiles'];
		}

		if ( ! empty( $params['business_manager_id'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_BUSINESS_MANAGER_ID ] = $params['business_manager_id'];
		}

		if ( ! empty( $params['ad_account_id'] ) ) {
			$options[ \WC_Facebookcommerce_Integration::OPTION_AD_ACCOUNT_ID ] = $params['ad_account_id'];
		}

		return $options;
	}

	/**
	 * Updates Facebook settings options.
	 *
	 * @since 3.5.0
	 *
	 * @param array $settings Array of settings to update.
	 * @return void
	 */
	private function update_settings( array $settings ) {
		foreach ( $settings as $key => $value ) {
			if ( ! empty( $key ) ) {
				update_option( $key, $value );
			}
		}
	}

	/**
	 * Updates connection status flags.
	 *
	 * @since 3.5.0
	 *
	 * @param array $params Request parameters.
	 * @return void
	 */
	private function update_connection_status( array $params ) {
		// Set the connection is complete
		update_option( 'wc_facebook_has_connected_fbe_2', 'yes' );
		update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' );

		// Clear any invalid connection flags since we just received fresh tokens
		delete_transient( 'wc_facebook_connection_invalid' );

		// Set the Messenger chat visibility
		if ( ! empty( $params['msger_chat'] ) ) {
			update_option( 'wc_facebook_enable_messenger', wc_bool_to_string( 'yes' === $params['msger_chat'] ) );
		}
	}

	/**
	 * Clears all integration options.
	 *
	 * @since 3.5.0
	 *
	 * @return void
	 */
	private function clear_integration_options() {
		$options = [
			\WC_Facebookcommerce_Integration::OPTION_ACCESS_TOKEN,
			\WC_Facebookcommerce_Integration::OPTION_BUSINESS_MANAGER_ID,
			\WC_Facebookcommerce_Integration::OPTION_AD_ACCOUNT_ID,
			\WC_Facebookcommerce_Integration::OPTION_SYSTEM_USER_ID,
			\WC_Facebookcommerce_Integration::OPTION_FEED_ID,
			\WC_Facebookcommerce_Integration::OPTION_COMMERCE_MERCHANT_SETTINGS_ID,
			\WC_Facebookcommerce_Integration::OPTION_COMMERCE_PARTNER_INTEGRATION_ID,
			\WC_Facebookcommerce_Integration::OPTION_ENABLE_MESSENGER,
			\WC_Facebookcommerce_Integration::OPTION_HAS_AUTHORIZED_PAGES_READ_ENGAGEMENT,
			\WC_Facebookcommerce_Integration::OPTION_HAS_CONNECTED_FBE_2,
			\WC_Facebookcommerce_Integration::OPTION_INSTALLED_FEATURES,
			\WC_Facebookcommerce_Integration::OPTION_MERCHANT_ACCESS_TOKEN,
			\WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN,
			\WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID,
			\WC_Facebookcommerce_Integration::OPTION_PROFILES,
			\WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
			\WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
		];

		foreach ( $options as $option ) {
			delete_option( $option );
		}

		// Clear facebook_config option to stop pixel tracking and prevent stale data
		if ( class_exists( 'WC_Facebookcommerce_Pixel' ) ) {
			delete_option( \WC_Facebookcommerce_Pixel::SETTINGS_KEY );
		}
	}

	/**
	 * Triggers products sync if catalog id is being set to a different value.
	 * Triggers metadata feed uploads if CPI id is being set to a different value.
	 *
	 * @since 3.5.0
	 *
	 * @param bool  $should_trigger_products_and_sets_sync
	 * @param bool  $should_trigger_metadata_feed_uploads
	 * @param array $params
	 * @return void
	 */
	private function maybe_trigger_feed_uploads( bool $should_trigger_products_and_sets_sync, bool $should_trigger_metadata_feed_uploads, array $params ) {
		try {
			if ( $should_trigger_products_and_sets_sync ) {
				// Allow opt-out of full batch-API sync, for example if store has a large number of products.
				if ( facebook_for_woocommerce()->get_integration()->allow_full_batch_api_sync() ) {
					facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_all_products();
				} else {
					Logger::log(
						'Initial full product sync disabled by filter hook `facebook_for_woocommerce_allow_full_batch_api_sync`',
						[
							'flow_name' => 'product_sync',
							'flow_step' => 'initial_sync',
						],
						array(
							'should_send_log_to_meta' => true,
							'should_save_log_in_woocommerce' => true,
							'woocommerce_log_level'   => \WC_Log_Levels::DEBUG,
						)
					);
				}
			}
		} catch ( \Exception $exception ) {
			Logger::log(
				'Product feed upload failed.',
				array(
					'event'      => 'product_sync',
					'event_type' => 'sync_products_after_settings_update',
					'extra_data' => [
						'params' => wp_json_encode( $params ),
					],
				),
				array(
					'should_send_log_to_meta'        => true,
					'should_save_log_in_woocommerce' => false,
					'woocommerce_log_level'          => \WC_Log_Levels::DEBUG,
				),
				$exception,
			);
		}

		try {
			if ( $should_trigger_products_and_sets_sync ) {
				facebook_for_woocommerce()->get_product_sets_sync_handler()->sync_all_product_sets();
			}
		} catch ( \Exception $exception ) {
			Logger::log(
				'Product sets sync failed.',
				array(
					'event'      => 'product_sets_sync',
					'event_type' => 'sync_product_sets_after_settings_update',
					'extra_data' => [
						'params' => wp_json_encode( $params ),
					],
				),
				array(
					'should_send_log_to_meta'        => true,
					'should_save_log_in_woocommerce' => false,
					'woocommerce_log_level'          => \WC_Log_Levels::DEBUG,
				),
				$exception,
			);
		}

		try {
			if ( $should_trigger_metadata_feed_uploads ) {
				facebook_for_woocommerce()->feed_manager->run_all_feed_uploads();
			}
		} catch ( \Exception $exception ) {
			Logger::log(
				'Products metadata feed upload failed.',
				array(
					'event'      => 'feed_upload',
					'event_type' => 'trigger_feed_uploads_after_settings_update',
					'extra_data' => [
						'params' => wp_json_encode( $params ),
					],
				),
				array(
					'should_send_log_to_meta'        => true,
					'should_save_log_in_woocommerce' => false,
					'woocommerce_log_level'          => \WC_Log_Levels::DEBUG,
				),
				$exception,
			);
		}
	}
}
