<?php namespace BulkPriceEditor\Actions;

use BulkPriceEditor\PriceEditorPage\Notifications\Notification;
use BulkPriceEditor\PriceEditorPage\Notifications\NotificationManager;
use BulkPriceEditor\PriceEditorPage\PriceEditorPage;
use BulkPriceEditor\ProductQuery\ProductQuery;
use BulkPriceEditor\Request\Request;

class SchedulePriceUpdatesAction {
	
	const PRODUCTS_PER_RUN = 100;
	
	const PROGRESS_DATA_KEY = 'bulk_price_editor_price_update_progress';
	
	const SCHEDULE__ACTION_NAME = 'bulk_price_editor_schedule_price_updating';
	const GET_PROGRESS__ACTION_NAME = 'bulk_price_editor_get_price_updating_progress';
	const STOP_UPDATING__ACTION_NAME = 'bulk_price_editor_stop_price_updating';
	
	public function __construct() {
		add_action( 'wp_ajax_' . self::SCHEDULE__ACTION_NAME, array( $this, 'run' ) );
		
		add_action( 'wp_ajax_' . self::GET_PROGRESS__ACTION_NAME, function () {
			wp_send_json_success( self::getProgressData() );
		} );
		
		add_action( 'admin_post_' . self::STOP_UPDATING__ACTION_NAME, function () {
			
			if ( ! current_user_can( 'manage_options' ) ) {
				wp_die( 'Access denied' );
			}
			
			$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : null;
			
			if ( ! wp_verify_nonce( $nonce, self::STOP_UPDATING__ACTION_NAME ) ) {
				wp_die( 'Access denied' );
			}
			
			WC()->queue()->cancel_all( UpdateProductPriceAction::ACTION_NAME );
			
			self::updateProgressData( array(
				'running'   => false,
				'total'     => 0,
				'processed' => 0,
			) );
			
			$notification = new Notification( __( 'Prices updating has been stopped.', 'bulk-price-editor' ), 'notice',
				true, true );
			
			NotificationManager::addNotification( $notification );
			
			wp_safe_redirect( admin_url( 'admin.php?page=' . PriceEditorPage::PAGE_SLUG ) );
		} );
	}
	
	public function run(): void {
		
		check_ajax_referer( self::SCHEDULE__ACTION_NAME, 'nonce' );
		
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array(
				'message' => 'Invalid request',
			) );
			
			return;
		}
		
		$priceModifier = Request::getPriceModifier( $_REQUEST );
		
		if ( ! $priceModifier ) {
			wp_send_json_error( array(
				'message' => 'Price modifier missed',
			) );
			
			return;
		}
		
		$productQuery = new ProductQuery( Request::getFilterManager( $_REQUEST ), array(
			'posts_per_page' => - 1,
			'limit'          => - 1,
			'return'         => 'ids',
			'paginate'       => false,
		) );
		
		$productQuery = $productQuery->build();
		
		$products = $productQuery->get_products();
		
		self::updateProgressData( array(
			'running'   => true,
			'total'     => count( $products ),
			'processed' => 0,
		) );
		
		$chunks = array_chunk( $products, self::getProductsPerRun() );
		
		/**
		 * Run first chunk immediately
		 */
		$firstChunk = array_shift( $chunks );
		
		$arguments = array(
			'products'            => $firstChunk,
			'price_modifier_type' => $priceModifier->getType(),
			'price_modifier_args' => $priceModifier->getRawArgs(),
		);
		
		$argumentsKey = time() . '_first_chunk_update_prices_arguments';
		
		set_transient( $argumentsKey, $arguments, DAY_IN_SECONDS );
		
		UpdateProductPriceAction::updatePrices( $argumentsKey );
		
		/**
		 * Schedule the rest of the chunks
		 */
		foreach ( $chunks as $key => $productIds ) {
			
			$arguments = array(
				'products'            => $productIds,
				'price_modifier_type' => $priceModifier->getType(),
				'price_modifier_args' => $priceModifier->getRawArgs(),
			);
			
			$argumentsKey = time() . '_' . $key . '_update_prices_arguments';
			
			set_transient( $argumentsKey, $arguments, DAY_IN_SECONDS );
			
			WC()->queue()->add( UpdateProductPriceAction::ACTION_NAME, array( $argumentsKey ),
				'bulk-price-editor__prices' );
			
		}
		
		if ( ! empty( $chunks ) ) {
			$notification = new Notification( __( 'Prices updating has been started.', 'bulk-price-editor' ), 'success',
				true, true );
			
			NotificationManager::addNotification( $notification );
		}
		
		wp_send_json_success( array(
			'message' => __( 'Prices updating has been started.', 'bulk-price-editor' ),
		) );
	}
	
	public static function getProgressURL(): string {
		return add_query_arg( array(
			'action' => self::GET_PROGRESS__ACTION_NAME,
			'nonce'  => wp_create_nonce( self::GET_PROGRESS__ACTION_NAME ),
		), admin_url( 'admin-ajax.php' ) );
	}
	
	public static function getURL(): string {
		return add_query_arg( array(
			'action' => self::SCHEDULE__ACTION_NAME,
			'nonce'  => wp_create_nonce( self::SCHEDULE__ACTION_NAME ),
		), admin_url( 'admin-ajax.php' ) );
	}
	
	public static function getProgressData(): array {
		return (array) get_option( self::PROGRESS_DATA_KEY, array(
			'running'   => false,
			'total'     => 0,
			'processed' => 0,
		) );
	}
	
	public static function isRunning(): bool {
		$progressData = self::getProgressData();
		
		return isset( $progressData['running'] ) && $progressData['running'];
	}
	
	public static function updateProgressData( array $data ) {
		update_option( self::PROGRESS_DATA_KEY, $data );
	}
	
	public static function updateProgressedProducts( $productsCount ) {
		
		$progressData = self::getProgressData();
		
		$progressData['processed'] = isset( $progressData['processed'] ) ? intval( $progressData['processed'] ) : 0;
		$progressData['total']     = isset( $progressData['total'] ) ? intval( $progressData['total'] ) : 0;
		
		$progressData['processed'] += $productsCount;
		
		if ( $progressData['processed'] >= $progressData['total'] ) {
			self::updateProgressData( array(
				'running'   => false,
				'processed' => $progressData['total'],
				'total'     => $progressData['total'],
			) );
			// Translators: %d - number of products
			$notification = new Notification( sprintf( __( 'Prices for %d products have been updated successfully.',
				'bulk-price-editor' ), $progressData['total'] ), 'success', false, true );
			
			NotificationManager::addNotification( $notification );
			
		} else {
			self::updateProgressData( $progressData );
		}
	}
	
	public static function getProductsPerRun(): int {
		return apply_filters( 'bulk_price_editor/action/update_prices/products_pee_run', self::PRODUCTS_PER_RUN );
	}
}
