<?php

/**
 * Core.
 * copyright Simple Direct Plugins - support@simpledirectplugins.com
 *
 * @author 			Simple Direct Plugins
 * @link			https://simpledirectplugins.com
 * @since			1.0.0
 * @package			SCOSWoo
 *
 */

defined ('ABSPATH') or exit;

if (!class_exists ('SCOS_Statuses')) :

	Class SCOS_Statuses {

		const ACCION = 'mark_';
		const ENVIO  = 'scos_order_status_';

		private array $estados;

		public function __construct () {

			foreach ($this->estados = SCOS_CPT::pide_query() as $estado) {

				/* translators: the custom status ID, just in case the user didn't set a title for it */
				$titulo = $estado->post_title ?: sprintf (__('Custom status #%s', 'sdp-custom-order-status-for-woocommerce'), $estado->ID);

				//Registramos los nuevos estados de pedido.
				register_post_status ('wc-' . $estado->post_name,
					[
						'label'						=> esc_html ($titulo),
						'public'					=> true,
						'exclude_from_search'		=> false,
						'show_in_admin_all_list'	=> true,
						'show_in_admin_status_list'	=> true,
						'label_count'				=> _n_noop ($titulo . ' <span class="count">(%s)</span>', $titulo . ' <span class="count">(%s)</span>', 'sdp-custom-order-status-for-woocommerce'), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain WordPress.WP.I18n.NonSingularStringLiteralSingular WordPress.WP.I18n.NonSingularStringLiteralPlural
						]
					);

				//Disparamos las notificaciones desde la metabox de acciones del pedido.
				add_action ('woocommerce_order_action_notificaciones_' . $estado->post_name, function ($pedido) use ($estado) {

					$envio = WC()->mailer()->get_emails();

					if (null !== $envio[self::ENVIO . $estado->post_name]) //Mejor prevenir, por si algún otro está filtrando mal.
						$envio[self::ENVIO . $estado->post_name]->trigger($this->devuelve_post_id($pedido->get_id()), true, $estado); //El true va a permitir enviarlo manualmente aunque la notificación esté deshabilitada en los ajustes de correo electrónico de Woo.
					}, 10, 1);
				}

			//Añadimos los nuevos estados a la lista de estados de pedidos de Woo.
			add_filter ('wc_order_statuses', [$this, 'add_lista_estados'], 10, 1);

			//Lo hacemos el estado por defecto, si debe ser así.
			add_filter ('woocommerce_payment_complete_order_status', [$this, 'override_payment_complete_order_status'], 10, 3);

			//Los hacemos editables en caso necesario.
			add_filter ('wc_order_is_editable', [$this, 'estado_editable'], 10, 2);

			//Y añadimos un mensaje de ayuda indicándolo en los pedidos en este estado.
			add_action ('woocommerce_order_item_add_action_buttons', [$this, 'mensaje_pedido_editar'], 10, 1);

			//Permitimos las descargas de archivos en caso necesario.
			add_filter ('woocommerce_order_is_download_permitted', [$this, 'descarga_permitida'], 10, 2);

			//Añadimos un botón de acción para cambiar al estado correspondiente.
			add_filter ('woocommerce_admin_order_actions', [$this, 'boton_cambia_estado'], 10, 2);

			//Añadimos una opción en la metabox de acciones para enviar/reenviar las notificaciones.
			add_filter ('woocommerce_order_actions', [$this, 'metabox_acciones'], 10, 2);

			//Vamos a ponerles unos iconos.
			add_action ('admin_enqueue_scripts', [$this, 'carga_scripts'], 10);

			//Añadimos las acciones en lote.
			add_filter ('bulk_actions-edit-shop_order', [$this, 'add_acciones_lote'], 10, 1); //Sin HPOS.
			add_filter ('bulk_actions-woocommerce_page_wc-orders', [$this, 'add_acciones_lote_hpos'], 10, 1); //Con HPOS.

			//Definimos los manejadores para las acciones en lote.
			add_filter ('handle_bulk_actions-edit-shop_order', [$this, 'manejadores_acciones_lote'], 10, 3); //Sin HPOS.
			add_filter ('handle_bulk_actions-woocommerce_page_wc-orders', [$this, 'manejadores_acciones_lote'], 10, 3); //Con HPOS.

			//Añadimos un aviso para informar al usuario de los cambios en lote.
			add_action ('admin_notices', [$this, 'aviso_acciones_lote'], 10, 1);

			//Añadimos hooks de acción para los distintos estados personalizados.
			//Permitimos la descarga de archivos descargables cuando proceda.
			//Disparamos las notificaciones al pasar a un estado personalizado.
			//Tenemos que lanzar la notificación de Procesando si el pedido viene de un estado personalizado.
			//Guardamos los meta de completado si el pedido en ese estado se considera como tal.
			//Devolvemos los productos al inventario si así está configurado en el estado.
			add_action ('woocommerce_order_status_changed', [$this, 'cambio_estado'], 10, 4);

			//Incluimos los pedidos en estados personalizados en los informes de Woo (método heredado) si es necesario.
			add_filter ('woocommerce_reports_order_statuses', [$this, 'estado_informes'], 10, 1);
			}

		public function add_lista_estados ($estados) {

			foreach ($this->estados as $estado)
				/* translators: the ID of the custom status, just in case the user didn't set a title for it */
				$estados['wc-' . $estado->post_name] = sanitize_text_field ($estado->post_title) ? : sprintf (esc_html__('Custom #%s', 'sdp-custom-order-status-for-woocommerce'), $estado->ID);

			return $estados;
			}

		/**
		 * Establece el estado de pedido como estado por defecto ante un pedido pagado.
		 *
		 * @since 	5.8.0
		 *
		 */
		public function override_payment_complete_order_status ($status, $id_pedido, $pedido) {

			if (!$pedido instanceof WC_Order)
				return $status;

			foreach ($this->estados as $estado)
				if (get_field ('scos_default', $estado->ID))
					return $estado->post_name;
				
			return $status;
			}

		/**
		 * Hace editables los pedidos en el estado personalizado si está configurado para ello.
		 *
		 * @since 	4.0.0
		 *
		 */
		public function estado_editable ($editable, $pedido) {

			foreach ($this->estados as $estado)
				if ($pedido->get_status() == $estado->post_name)
					if (($caracteristicas = get_field ('scos_settings', $estado->ID)) && is_array ($caracteristicas) && in_array ('editable', $caracteristicas))
						return true; //Ya no hace falta seguir.

			return $editable;
			}

		/**
		 * Muestra un mensaje de ayuda en el pedido si está en un estado en el que no es editable.
		 *
		 * @since 	3.1.3
		 *
		 */
		public function mensaje_pedido_editar ($pedido) {

			foreach ($this->estados as $estado) {

				if ($pedido->get_status() == $estado->post_name) {

					if (($caracteristicas = get_field ('scos_settings', $estado->ID)) && (!is_array ($caracteristicas) || !in_array ('editable', $caracteristicas)))
						/* translators: 1$: the title of the status. 2$: link to the settings of the order status. 3$: closing a tag */
						printf ('<br><span class="description">%s</span>', sprintf (esc_html__('To edit orders in %1$s status, change the %2$sorder status settings%3$s.', 'sdp-custom-order-status-for-woocommerce'), '&laquo;' . esc_html ($estado->post_title) . '&raquo;', '<a target="_blank" href="' . esc_url ('post.php?post=' . $estado->ID . '&action=edit') . '">', '</a>'));

					return; //Ya no hace falta seguir.
					}
				}
			}

		/**
		 * Permite acceso a los archivos descargables del pedido en un estado personalizado.
		 *
		 * @since 	4.0.0
		 *
		 */
		public function descarga_permitida ($permitida, $pedido) {

			foreach ($this->estados as $estado)
				if ($pedido->get_status() == $estado->post_name)
					if (($caracteristicas = get_field ('scos_settings', $estado->ID)) && is_array ($caracteristicas) && in_array ('descargas', $caracteristicas))
						return true; //Ya no hace falta seguir.

			return $permitida;
			}

		public function boton_cambia_estado ($acciones, $pedido) {
			
			$id_pedido = $this->devuelve_post_id($pedido->get_order_number()); //Por si hay activo algún plugin de numeración secuencial de pedidos.

			if (!$pedido->has_status('processing') && !isset ($acciones['processing']))
				$acciones['processing'] = [
					'url'		=> wp_nonce_url (admin_url ('admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $id_pedido), 'woocommerce-mark-order-status'),
					'name'		=> esc_html__('Processing', 'sdp-custom-order-status-for-woocommerce'),
					'action'	=> 'processing',
					];

			if (!$pedido->has_status('completed') && !isset ($acciones['complete']))
				$acciones['complete'] = [
					'url'		=> wp_nonce_url (admin_url ('admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $id_pedido), 'woocommerce-mark-order-status'),
					'name'		=> esc_html__('Complete', 'sdp-custom-order-status-for-woocommerce'),
					'action'	=> 'complete',
					];

			foreach ($this->estados as $estado) {

				if (!$pedido->has_status($estado->post_name)) //Sólo añadimos la acción a los pedidos que no están ya en ese estado.
					$acciones[$estado->post_name] = [
						'url'		=> wp_nonce_url (admin_url ('admin-ajax.php?action=woocommerce_mark_order_status&status=' . $estado->post_name . '&order_id=' . $id_pedido), 'woocommerce-mark-order-status'),
						/* translators: the ID of the order status, just in case the user didn't set a title for it */
						'name'		=> sprintf (esc_html__('Mark as %s', 'sdp-custom-order-status-for-woocommerce'), mb_strtolower (esc_html ($estado->post_title) ? : sprintf (esc_html__('Custom status #%s', 'sdp-custom-order-status-for-woocommerce'), $estado->ID)), 'UTF-8'),
						'action'	=> $estado->post_name,
						];

				else //El estado en el que está el pedido.
					$sigestado = get_field ('scos_sigestado', $estado->ID);
				}

			if (isset ($sigestado) && strlen ($sigestado)) {

				'wc-' == substr ($sigestado, 0, 3) and
					$sigestado = substr ($sigestado, 3);

				foreach ($this->estados as $estado)
					if ($estado->post_name == $sigestado)
						$siguiente = $estado->post_name;

				switch ($sigestado) {

					case 'completed':

						$accion_sig = $acciones['complete'];
						$accion_key = 'complete';

						break;

					case 'processing':

						$accion_sig = $acciones['processing'];
						$accion_key = 'processing';

						break;

					default:

						$accion_sig = $acciones[$siguiente];
						$accion_key = $siguiente;
						
						break;
					}

				unset ($acciones[$accion_key]); //Quitamos el botón de acción correspondiente al siguiente estado...
				array_unshift ($acciones, $accion_sig); //...para ponerlo el primero.
				}

			/**
			 * Para ocultar el botón de acción de "Procesando":
			 *
			 * add_filter ('scos_hide_action_processing', '__return_true');
			 *
			 */
			if (apply_filters ('scos_hide_action_processing', false))
				unset ($acciones['processing']);

			/**
			 * Para ocultar el botón de acción de "Completado":
			 *
			 * add_filter ('scos_hide_action_completed', '__return_true');
			 *
			 */
			if (apply_filters ('scos_hide_action_completed', false))
				unset ($acciones['complete']);

			return $acciones;
			}

		public function metabox_acciones ($acciones) {

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (!isset ($_GET['post']) && !isset ($_GET['id'])) //Sin HPOS es $_GET['post'], con HPOS es $_GET['id'].
				return $acciones;

			$acciones_dos = $acciones_final = [];

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$pedido = new WC_Order(isset ($_GET['post']) ? absint ($_GET['post']) : absint ($_GET['id'])); //Sin HPOS es $_GET['post'], con HPOS es $_GET['id'].
			$status = $pedido->get_status();

			foreach ($this->estados as $estado)
				if (is_countable ($notificaciones = get_field ('scos_notificaciones', $estado->ID)) && $cuantas = count ($notificaciones))
					$acciones_dos['notificaciones_' . esc_attr ($estado->post_name)] = ($status == $estado->post_name) ?
						/* translators: the title of the order status */
						esc_html (sprintf (_n('Resend %s notification', 'Resend %s notifications', $cuantas, 'sdp-custom-order-status-for-woocommerce'), $estado->post_title ?
							mb_strtolower ($estado->post_title, 'UTF-8') :
							__('custom status #', 'sdp-custom-order-status-for-woocommerce') . $estado->ID)) :
						/* translators: the title of the order status */
						esc_html (sprintf (_n('Send %s notification', 'Send %s notifications', $cuantas, 'sdp-custom-order-status-for-woocommerce'), $estado->post_title ?
							mb_strtolower ($estado->post_title, 'UTF-8') :
							__('custom status #', 'sdp-custom-order-status-for-woocommerce') . $estado->ID));

			foreach ($acciones as $accion => $etiqueta) {

				$acciones_final[$accion] = $etiqueta;

				if ('send_order_details_admin' == $accion)
					foreach ($acciones_dos as $accion_dos => $etiqueta_dos)
						$acciones_final[$accion_dos] = $etiqueta_dos;
				}

			return $acciones_final;
			}

		/**
		 * Declaramos las nuevas acciones en lote (método previo a HPOS).
		 *
		 * @since 2.1.1
		 *
		 */
		public function add_acciones_lote ($acciones) {

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (!isset ($_GET['post_status']) || 'trash' != $_GET['post_status']) //En la papelera de pedidos no añadimos acciones.
				foreach ($this->estados as $estado)
					/* translators: the title or the order status */
					$acciones[self::ACCION . esc_attr ($estado->post_name)] = sprintf (esc_html__('Change status to %s', 'sdp-custom-order-status-for-woocommerce'), mb_strtolower (esc_html ($estado->post_title) ? : sprintf (esc_html__('Custom #%s', 'sdp-custom-order-status-for-woocommerce'), esc_attr ($estado->ID)), 'UTF-8'));

			return $acciones;
			}

		/**
		 * Declaramos las acciones a realizar en lote con HPOS.
		 *
		 * @since 5.0.0
		 *
		 */
		public function add_acciones_lote_hpos ($acciones) {

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (isset ($_GET['status']) && 'trash' == $_GET['status']) //En la papelera de pedidos no añadimos acciones.
				return $acciones;

			$mis_acciones = [];
						
			foreach ($this->estados as $estado)
				/* translators: the title or the order status */
				$mis_acciones[self::ACCION . $estado->post_name] = sprintf (esc_html__('Change status to %s', 'sdp-custom-order-status-for-woocommerce'), mb_strtolower (sanitize_text_field ($estado->post_title) ? : sprintf (esc_html__('Custom #%s', 'sdp-custom-order-status-for-woocommerce'), $estado->ID), 'UTF-8'));
				
			foreach ($acciones as $accion => $nombre)
				if ('trash' != $accion)
					$mis_acciones[$accion] = $nombre;

			$mis_acciones['trash'] = $acciones['trash'];

			return $mis_acciones;
			}

		/**
		 * Definimos las acciones a realizar en lote mediante el modo nativo de WordPress 4.7+ (método válido también con HPOS).
		 * https://github.com/woocommerce/woocommerce/pull/35442
		 *
		 * @since 2.1.1
		 *
		 */
		public function manejadores_acciones_lote ($redirect, $accion, $ids) {

			foreach ($this->estados as $estado) {

				if (self::ACCION . $estado->post_name == $accion) {

					foreach ($ids as $id) {

						$pedido = new WC_Order($id);
						$pedido->update_status($estado->post_name, false, true);
						}

					$redirect = add_query_arg (self::ACCION . $estado->post_name, count ($ids), $redirect);
					}

				else
					$redirect = remove_query_arg (self::ACCION . $estado->post_name, $redirect);
				}

			return $redirect;
			}

		/**
		 * Añadimos un aviso para confirmar al usuario cuántos pedidos se han cambiado en lote (previo a HPOS).
		 *
		 * @since 2.1.1
		 *
		 */
		public function aviso_acciones_lote () {

			foreach ($this->estados as $estado) {

				// phpcs:ignore WordPress.Security.NonceVerification.Recommended
				if (isset ($_GET[self::ACCION . $estado->post_name])) {

					// phpcs:ignore WordPress.Security.NonceVerification.Recommended
					$cantidad = absint ($_GET[self::ACCION . $estado->post_name]);
					/* translators: the number of orders and the title or the order status */
					$message  = sprintf (_n('%1$s order has been marked as %2$s.', '%1$s orders have been marked as %2$s.', $cantidad, 'sdp-custom-order-status-for-woocommerce'), $cantidad, mb_strtolower ($estado->post_title ? : sprintf (__('Custom #%s', 'sdp-custom-order-status-for-woocommerce'), $estado->ID), 'UTF-8'))

					?>
						<div class="updated">
							<p><?php echo esc_html ($message); ?></p>
						</div>
					<?php
					}
				}
			}

		/**
		 * Creamos hooks de acción para el cambio a los diferentes estados personalizados.
		 * Permitimos la descarga de archivos descargables cuando proceda.
		 * Disparamos las notificaciones al pasar a un estado personalizado.
		 * Lanzamos la notificación de procesando si viene de un estado de pedido personalizado.
		 * Guardamos los meta de completado si el pedido en ese estado se considera como tal.
		 *
		 * @since 2.5.4
		 *
		 */
		public function cambio_estado ($id_pedido, $desde_estado, $a_estado, $pedido) {

			$id_convertida = $this->devuelve_post_id($id_pedido); //Por si hay activo algún plugin de numeración secuencial de pedidos.

			foreach ($this->estados as $estado) {

				if ($a_estado == $estado->post_name) {

					//Si el estado permite la descarga de archivos descargables, la habilitamos.
					if (($incluir = get_field ('scos_informes', $estado->ID)) && is_array ($incluir) && in_array ('descargas', $incluir))
						wc_downloadable_product_permissions ($id_convertida, true);

					//Guardamos los meta de completado si el pedido en ese estado se considera como tal.
					if ($incluir && is_array ($incluir) && in_array ('completado', $incluir)) {

						$momento = time();
						$pedido->set_date_paid($momento, true);
						$pedido->update_meta_data('_completed_date', gmdate ('Y-m-d H:i:s', $momento));
						$pedido->save();
						}

					//Creamos el hook de acción para el cambio al estado personalizado.
					do_action (self::ENVIO . $estado->post_name, $id_convertida); //phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound

					//Disparamos las notificaciones al pasar al estado personalizado.
					$envio = WC()->mailer()->get_emails();

					if (null !== $envio[self::ENVIO . $estado->post_name])
						$envio[self::ENVIO . $estado->post_name]->trigger($id_convertida);				
					}

				//Lanzamos la notificación de procesando si viene de un estado de pedido personalizado (Woo no lo hace).
				if ($pedido->has_status('processing') && $desde_estado == $estado->post_name) {

					$envio = WC()->mailer()->get_emails();
					$envio['WC_Email_Customer_Processing_Order']->trigger($id_convertida);
					}
				}
			}

		public function estado_informes ($estados) {

			if ($estados) //Si no añadimos este condicional, por algún motivo deja de tener en cuenta los refunds en los informes (WTF).
				foreach ($this->estados as $estado)
					if (($incluir = get_field ('scos_informes', $estado->ID)) && is_array ($incluir) && in_array ('incluir', $incluir))
						$estados[] = $estado->post_name;

			return $estados;
			}

		/**
		 * Devuelve el número de post a partir del número de pedido.
		 * Puede no coincidir si hay activo un plugin de números de pedido secuenciales.
		 *
		 * @since 	2.3.0
		 *
		 * @param 	int|string 		ID
		 * @return 	int|string 		ID
		 *
		 */
		private function devuelve_post_id ($order_id) {

			/**
			 * Sequential Order Numbers for WooCommerce (SkyVerge).
			 * https://wordpress.org/plugins/woocommerce-sequential-order-numbers/
			 * 
			 */
			if (class_exists ('WC_Seq_Order_Number')) {

				if (class_exists ('wc_sequential_order_numbers'))
					return wc_sequential_order_numbers()->find_order_by_order_number($order_id);

				else
					$meta = '_order_number';
				}

			/**
			 * WooCommerce Sequential Order Numbers Pro (SkyVerge).
			 * https://woocommerce.com/products/sequential-order-numbers-pro/
			 * 
			 */
			else if (class_exists ('WC_Seq_Order_Number_Pro')) {
				
				if (class_exists ('wc_seq_order_number_pro'))
					return wc_seq_order_number_pro()->find_order_by_order_number($order_id);

				else
					$meta = '_order_number_formatted';
				}

			/**
			 * YITH Sequential Order Number (YITH).
			 * https://yithemes.com/themes/plugins/yith-woocommerce-sequential-order-number/
			 * 
			 */
			else if (function_exists ('YITH_Sequential_Order_Number_Premium_Init'))
				$meta = '_ywson_custom_number_order_complete';

			/**
			 * Sequential Order Number for WooCommerce (WebToffee).
			 * https://wordpress.org/plugins/wt-woocommerce-sequential-order-numbers/
			 * 
			 */
			else if (function_exists ('run_wt_advanced_order_number'))
				$meta = '_order_number';

			else
				return $order_id;

			global $wpdb;

			$cache_key = 'sonw_order_number';

			if (SCOS_HPOS_ACTIVE) { //Con HPOS.	

				if (($order_number = wp_cache_get ($cache_key)) === false) {

					$order_number = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->prepare(
							"SELECT order_id FROM $wpdb->wc_orders_meta WHERE meta_key = %s AND meta_value = %d",
							$meta,
							absint ($order_id)
							)
						);
					wp_cache_set ($cache_key, $order_number);
					}
	
				return $order_number[0]['order_id'] ?? $order_id;
				}

			else { //Sin HPOS.

				if (($order_number = wp_cache_get ($cache_key)) === false) {

					$order_number = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->prepare(
							"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %d",
							$meta,
							absint ($order_id)
							)
						);
					wp_cache_set ($cache_key, $order_number);
					}

				return $order_number[0]['post_id'] ?? $order_id;
				}
			}

		public function carga_scripts () {

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (!(isset ($_GET['page']) && 'wc-orders' == $_GET['page']) && !(isset ($_GET['post_type']) && 'shop_order' == $_GET['post_type']))
				return;

			wp_enqueue_style ('scos-custom-order-status-orders', esc_url (SCOS_ASSETS . 'css/orders.css'), [], '1.0'); //To enqueue dnamicaly generated CSS for orders in custom status.

			$dynamic_css = '';

			foreach ($this->estados as $estado) {

				$color = get_field ('scos_color', $estado->ID) ? : '#1e73be';
				$label = get_field ('scos_color_label', $estado->ID);
				$campo = get_field ('scos_icono', $estado->ID);
				$icono = isset ($campo) ? (is_array ($campo) ? $campo['value'] : $campo) : '159';

				$dynamic_css .= sprintf ('.wc-action-button-%s::after {
						content:"\f%s";
						color:%s!important;
						}
					.status-%s:not(.type-shop_order) {
							background-color:%s;
							color:%s;
						}',
					esc_attr ($estado->post_name),
					esc_html ($icono),
					esc_html ($color),
					esc_attr ($estado->post_name),
					esc_html ($color),
					esc_html ($label)
					);
				}

			wp_add_inline_style ('scos-custom-order-status-orders', $dynamic_css);
			}

		}

endif;