<?php
/**
 * Handler functions for WooCommerce SMS Notifications
 *
 * @package miniorange-sms-order-notification-otp-verification/notifications/wcsmsnotification/handler
 */

namespace WCSMSOTP\Notifications\WcSMSNotification\Handler;

use WCSMSOTP\Notifications\WcSMSNotification\Helper\MoWcAddOnMessages;
use WCSMSOTP\Notifications\WcSMSNotification\Helper\MoWcAddOnUtility;
use WCSMSOTP\Notifications\WcSMSNotification\Helper\WcOrderStatus;
use WCSMSOTP\Notifications\WcSMSNotification\Helper\WooCommerceNotificationsList;
use WCSMSOTP\Helper\MoConstants;
use WCSMSOTP\Helper\MoUtility;
use WCSMSOTP\Helper\MoMessages;
use WCSMSOTP\Helper\MoFormDocs;
use WCSMSOTP\Objects\BaseAddOnHandler;
use WCSMSOTP\Objects\BaseMessages;
use WCSMSOTP\Objects\SMSNotification;
use WCSMSOTP\Traits\Instance;
use WC_Emails;
use WC_Order;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * WooCommerceNotifications class
 */
class WooCommerceNotifications extends BaseAddOnHandler {

	use Instance;

	/**
	 * The list of all the Notification Settings
	 *
	 * @var WooCommerceNotificationsList
	 */
	private $notification_settings;

	/**
	 * The list of all the enabled notifications
	 *
	 * @var WooCommerceNotificationsList
	 */
	private $enable_notification_action;

	/**
	 * Constructor checks if add-on has been enabled by the admin and initializes
	 * all the class variables. This function also defines all the hooks to
	 * hook into to make the add-on functionality work.
	 */
	protected function __construct() {
		parent::__construct();

		// Initialize nonce for security checks.
		$this->nonce     = 'mo_admin_actions';
		$this->nonce_key = 'security';

		add_action( 'admin_enqueue_scripts', array( $this, 'mo_wc_sms_notif_settings_style' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'mo_wc_sms_notif_settings_script' ) );

		$notification_settings_option = mowc_get_mowc_option( 'notification_settings' );
		$notification_settings_option = maybe_unserialize( $notification_settings_option );
		if ( false === $notification_settings_option || ! is_object( $notification_settings_option ) ) {
			$notification_settings_option = WooCommerceNotificationsList::instance();
		}
		$this->notification_settings = $notification_settings_option;

		$this->enable_notification_action = 'mo_wc_notification_enable';

		// Only allow authenticated users with proper permissions to access this AJAX endpoint.
		add_action( 'wp_ajax_' . $this->enable_notification_action, array( $this, 'enable_wc_sms_notification' ) );

		add_action( 'woocommerce_created_customer_notification', array( $this, 'mo_send_new_customer_sms_notif' ), 1, 3 );
		add_action( 'woocommerce_new_customer_note_notification', array( $this, 'mo_send_new_customer_sms_note' ), 1, 1 );
		add_action( 'woocommerce_order_status_changed', array( $this, 'mo_send_admin_order_sms_notif' ), 1, 3 );
		add_action( 'woocommerce_order_status_changed', array( $this, 'mo_customer_order_hold_sms_notif' ), 1, 3 );

		add_action( 'admin_init', array( $this, 'handle_admin_actions' ) );
		add_action( 'add_meta_boxes', array( $this, 'add_custom_msg_meta_box' ), 1 );
	}


	/**
	 * This function hooks into the admin_init WordPress hook. This function
	 * checks the form being posted and routes the data to the correct function
	 * for processing. The 'option' value in the form post is checked to make
	 * the diversion.
	 */
	public function handle_admin_actions() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// Handle WooCommerce notification settings form submit (Save Settings button).
		$post_option = isset( $_POST['option'] ) ? sanitize_text_field( wp_unslash( $_POST['option'] ) ) : '';
		if ( 'wc_sms_notif_settings' === $post_option ) {
			if ( ! check_admin_referer( $this->nonce, $this->nonce_key ) ) {
				wp_die( esc_attr( MoMessages::showMessage( BaseMessages::INVALID_OP ) ) );
			}
			$this->save_wc_sms_notif_settings();
			return;
		}

		if ( ! empty( $_POST ) && is_array( $_POST ) && array_key_exists( 'mo_send_custome_msg_option', $_POST ) && ! check_ajax_referer( $this->nonce, $this->nonce_key ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( BaseMessages::INVALID_OP ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			exit;
		}
		$post_data = ! empty( $_POST ) && is_array( $_POST ) ? wp_unslash( $_POST ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Will be sanitized below.
		$get_data  = ! empty( $_GET ) && is_array( $_GET ) ? wp_unslash( $_GET ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Will be sanitized below.
		$data      = ! empty( $post_data ) ? MoUtility::mowc_sanitize_array( $post_data ) : array();
		$getdata   = ! empty( $get_data ) ? MoUtility::mowc_sanitize_array( $get_data ) : array();

		if ( isset( $getdata['option'] ) && sanitize_text_field( $getdata['option'] ) === 'mo_send_order_custom_msg' ) {
			$this->send_custom_order_msg( $data );
		}
	}

	/**
	 * Save WooCommerce SMS notification settings from the notification tab form.
	 * Persists enable/disable toggles, SMS body templates, and admin recipient.
	 */
	private function save_wc_sms_notif_settings() {
		if ( ! check_admin_referer( $this->nonce, $this->nonce_key ) ) {
			wp_die( esc_attr( MoMessages::showMessage( BaseMessages::INVALID_OP ) ) );
		}

		$notification_keys = array(
			'wc_new_customer_notif',
			'wc_customer_note_notif',
			'wc_admin_order_status_notif',
			'wc_order_on_hold_notif',
			'wc_order_processing_notif',
			'wc_order_completed_notif',
			'wc_order_refunded_notif',
			'wc_order_cancelled_notif',
			'wc_order_failed_notif',
			'wc_order_pending_notif',
		);

		foreach ( $notification_keys as $notification_name ) {
			$wcfm_notif = $this->notification_settings->$notification_name;
			if ( ! is_object( $wcfm_notif ) ) {
				continue;
			}

			$sanitized_name = sanitize_key( $notification_name );
			$wcfm_notif->set_is_enabled( isset( $_POST[ $sanitized_name ] ) );

			$textarea_tag = $sanitized_name . '_smsbody';
			$sms_body     = isset( $_POST[ $textarea_tag ] ) ? sanitize_textarea_field( wp_unslash( $_POST[ $textarea_tag ] ) ) : '';
			$default_body = isset( $wcfm_notif->default_sms_body ) && is_string( $wcfm_notif->default_sms_body ) ? $wcfm_notif->default_sms_body : '';
			$wcfm_notif->set_sms_body( MoUtility::is_blank( $sms_body ) ? $default_body : $sms_body );

			if ( 'wc_admin_order_status_notif' === $notification_name ) {
				$recipient = MoUtility::sanitize_check( 'wc_admin_order_status_notif_recipient', isset( $_POST ) && is_array( $_POST ) ? wp_unslash( $_POST ) : array() );
				if ( ! empty( $recipient ) && is_string( $recipient ) ) {
					$recipient = preg_replace( '/[^+\-0-9; ]/', '', $recipient );
				}
				$wcfm_notif->set_recipient( is_string( $recipient ) ? $recipient : '' );
			}
		}

		mowc_update_mowc_option( 'notification_settings', $this->notification_settings );
		do_action( 'mowc_registration_show_message', MoMessages::showMessage( MoMessages::EXTRA_SETTINGS_SAVED ), 'SUCCESS' );
	}

	/**
	 * Enqueue the CSS stylesheet
	 *
	 * @return void
	 */
	public function mo_wc_sms_notif_settings_style() {
		wp_enqueue_style( 'wcfm_sn_notif_admin_settings_style', WC_MSN_CSS_URL, array(), WC_MSN_VERSION );
	}
	/**
	 * Function to register a new script
	 *
	 * @return void
	 */
	public function mo_wc_sms_notif_settings_script() {
		wp_register_script( 'settingswcnotif', MOWC_MSN_JS_URL, array( 'jquery' ), WC_MSN_VERSION, false );
		wp_localize_script(
			'settingswcnotif',
			'mowcsmsvar',
			array(
				'siteURL'  => mowc_wp_wc_ajax_url(),
				'action'   => $this->enable_notification_action,
				'nonce'    => wp_create_nonce( $this->nonce ),
				'nonceKey' => $this->nonce_key,
			)
		);
		wp_enqueue_script( 'settingswcnotif' );
	}

	/**
	 * Enable and disable toggle and update in database
	 *
	 * @return void
	 */
	public function enable_wc_sms_notification() {
		// Check if user has proper authorization (admin only).
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( BaseMessages::INVALID_OP ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			exit;
		}

		// Verify nonce to prevent CSRF attacks.
		if ( ! check_ajax_referer( $this->nonce, $this->nonce_key, false ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( BaseMessages::INVALID_OP ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			exit;
		}

		$wc_notification_type = isset( $_POST['notification'] ) ? sanitize_text_field( wp_unslash( $_POST['notification'] ) ) : '';
		if ( empty( $wc_notification_type ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( BaseMessages::INVALID_OP ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			exit;
		}

		$wcfm_notif = null;
		switch ( $wc_notification_type ) {
			case 'wc_new_customer_notif':
				$wcfm_notif = $this->notification_settings->get_wc_new_customer_notif();
				break;
			case 'wc_customer_note_notif':
				$wcfm_notif = $this->notification_settings->get_wc_customer_note_notif();
				break;
			case 'wc_admin_order_status_notif':
				$wcfm_notif = $this->notification_settings->get_wc_admin_order_status_notif();
				break;
			case 'wc_order_on_hold_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_on_hold_notif();
				break;
			case 'wc_order_processing_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_processing_notif();
				break;
			case 'wc_order_completed_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_completed_notif();
				break;
			case 'wc_order_refunded_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_refunded_notif();
				break;
			case 'wc_order_cancelled_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_cancelled_notif();
				break;
			case 'wc_order_failed_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_failed_notif();
				break;
			case 'wc_order_pending_notif':
				$wcfm_notif = $this->notification_settings->get_wc_order_pending_notif();
				break;
			default:
				wp_send_json(
					MoUtility::create_json(
						MoMessages::showMessage( BaseMessages::INVALID_OP ),
						MoConstants::ERROR_JSON_TYPE
					)
				);
				exit;
		}

		if ( ! is_object( $wcfm_notif ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( BaseMessages::INVALID_OP ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			exit;
		}

		if ( isset( $wcfm_notif->is_enabled ) && $wcfm_notif->is_enabled ) {
			$wcfm_notif->set_is_enabled( false );
		} else {
			$wcfm_notif->set_is_enabled( true );
		}

		mowc_update_mowc_option( 'notification_settings', $this->notification_settings );
	}

	/**
	 * This function hooks into the woocommerce_created_customer_notification hook
	 * to send an SMS notification to the user when he successfully creates an
	 * account using the checkout or registration page.
	 *
	 * @param  string $customer_id        id of the customer.
	 * @param  array  $new_customer_data  array of customer data.
	 * @param  bool   $password_generated was password is automatically generated.
	 */
	public function mo_send_new_customer_sms_notif( $customer_id, $new_customer_data = array(), $password_generated = false ) {
		$notification = $this->notification_settings->get_wc_new_customer_notif();
		if ( is_object( $notification ) ) {
			$notification->send_sms(
				array(
					'customer_id'        => $customer_id,
					'new_customer_data'  => $new_customer_data,
					'password_generated' => $password_generated,
				)
			);
		}
	}

	/**
	 * This function hooks into the woocommerce_new_customer_note_notification hook
	 * to send an SMS notification to the user when the admin successfully adds a
	 * note to the order that the user has ordered.
	 *
	 * @param  array $args array Customer note details.
	 */
	public function mo_send_new_customer_sms_note( $args ) {
		if ( ! is_array( $args ) || ! isset( $args['order_id'] ) ) {
			return;
		}
		$order_id = absint( $args['order_id'] );
		if ( empty( $order_id ) ) {
			return;
		}
		$notification = $this->notification_settings->get_wc_customer_note_notif();
		if ( is_object( $notification ) ) {
			$order = wc_get_order( $order_id );
			if ( is_a( $order, 'WC_Order' ) ) {
				$notification->send_sms(
					array( 'orderDetails' => $order )
				);
			}
		}
	}

	/**
	 * This function hooks into woocommerce_order_status_changed hook
	 * to send an SMS notification to the admin that a order needs to be
	 * processed as its status has changed.
	 *
	 * @param int    $order_id string the id of the order.
	 * @param string $old_status string the old status of the order.
	 * @param string $new_status string the new status of the order.
	 */
	public function mo_send_admin_order_sms_notif( $order_id, $old_status, $new_status ) {
		$order = new WC_Order( $order_id );
		if ( ! is_a( $order, 'WC_Order' ) ) {
			return;
		}
		$notification = $this->notification_settings->get_wc_admin_order_status_notif();
		if ( is_object( $notification ) ) {
			$notification->send_sms(
				array(
					'orderDetails' => $order,
					'new_status'   => sanitize_text_field( $new_status ),
					'old_status'   => sanitize_text_field( $old_status ),
				)
			);
		}
	}

	/**
	 * This function hooks into all of the On-Hold notification Woocommerce
	 * hook to send an SMS notification to the customer that a order has been
	 * put on hold
	 *
	 * @param int    $order_id string the id of the order.
	 * @param string $old_status string the old status of the order.
	 * @param string $new_status string the new status of the order.
	 */
	public function mo_customer_order_hold_sms_notif( $order_id, $old_status, $new_status ) {
		$order = new WC_Order( $order_id );
		if ( ! is_a( $order, 'WC_Order' ) ) {
			return;
		}

		if ( strcasecmp( $new_status, WcOrderStatus::ON_HOLD ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_on_hold_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::PROCESSING ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_processing_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::COMPLETED ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_completed_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::REFUNDED ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_refunded_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::CANCELLED ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_cancelled_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::FAILED ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_failed_notif();
		} elseif ( strcasecmp( $new_status, WcOrderStatus::PENDING ) === 0 ) {
			$notification = $this->notification_settings->get_wc_order_pending_notif();
		} else {
			return;
		}

		if ( ! is_object( $notification ) ) {
			return;
		}

		$notification->send_sms( array( 'orderDetails' => $order ) );
	}

	/**
	 * Unhook all the emails that we will be sending sms notifications for.
	 *
	 * @param WC_Emails $email_class the class to disable notification for.
	 */
	private function unhook( $email_class ) {
		$new_order_email  = array( $email_class->emails['WC_Email_New_Order'], 'trigger' );
		$processing_order = array( $email_class->emails['WC_Email_Customer_Processing_Order'], 'trigger' );
		$completed_order  = array( $email_class->emails['WC_Email_Customer_Completed_Order'], 'trigger' );
		$new_customer     = array( $email_class->emails['WC_Email_Customer_Note'], 'trigger' );

		remove_action( 'woocommerce_low_stock_notification', array( $email_class, 'low_stock' ) );
		remove_action( 'woocommerce_no_stock_notification', array( $email_class, 'no_stock' ) );
		remove_action( 'woocommerce_product_on_backorder_notification', array( $email_class, 'backorder' ) );
		remove_action( 'woocommerce_order_status_pending_to_processing_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_pending_to_completed_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_pending_to_on-hold_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_failed_to_processing_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_failed_to_completed_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_failed_to_on-hold_notification', $new_order_email );
		remove_action( 'woocommerce_order_status_pending_to_processing_notification', $processing_order );
		remove_action( 'woocommerce_order_status_pending_to_on-hold_notification', $processing_order );
		remove_action( 'woocommerce_order_status_completed_notification', $completed_order );
		remove_action( 'woocommerce_new_customer_note_notification', $new_customer );
	}

	/**
	 * Add a meta box in the order page so that customer can send custom messages to
	 * the users if he wants to.
	 */
	public function add_custom_msg_meta_box() {
		if ( class_exists( 'WooCommerce' ) ) {
			$screen = wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled()
			? wc_get_page_screen_id( 'shop-order' )
			: 'shop_order';

			add_meta_box(
				'mo_wc_custom_sms_meta_box',
				esc_html__( 'Send Custom SMS', 'miniorange-sms-order-notification-otp-verification' ),
				array( $this, 'mo_show_send_custom_msg_box' ),
				$screen,
				'side',
				'default'
			);
		}
	}

	/**
	 * This function is a call back to our meta box hook so that we
	 * can design our metabox and provide our own frontend to the
	 * metabox. In this cases its being used to show a box where
	 * admin can send custom messages to the user.
	 *
	 * @param  array $post_or_order_object - the post or order object passed by WordPress.
	 */
	public function mo_show_send_custom_msg_box( $post_or_order_object ) {
		$order_details = is_a( $post_or_order_object, 'WP_Post' ) ? wc_get_order( $post_or_order_object->ID ) : $post_or_order_object;
		$phone_numbers = MoWcAddOnUtility::get_customer_number_from_order( $order_details );
		$template_path = trailingslashit( WC_MSN_DIR ) . 'views/custom-order-msg.php';
		if ( file_exists( $template_path ) ) {
			include $template_path;
		}
	}

	/**
	 * This function is used to send custom SMS messages to the user
	 * from the order page of WooCommer using our meta box.
	 *
	 * @param array $data the posted data.
	 */
	private function send_custom_order_msg( $data ) {
		if ( ! is_array( $data ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoWcAddOnMessages::showMessage( MoWcAddOnMessages::INVALID_PHONE ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			return;
		}

		$numbers = isset( $data['numbers'] ) ? sanitize_text_field( $data['numbers'] ) : '';
		$message = isset( $data['msg'] ) ? sanitize_textarea_field( $data['msg'] ) : '';

		if ( MoUtility::is_blank( $numbers ) || MoUtility::is_blank( $message ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoWcAddOnMessages::showMessage( MoWcAddOnMessages::INVALID_PHONE ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			return;
		}

		$phone_numbers = explode( ';', $numbers );
		$phone_numbers = array_map( 'trim', $phone_numbers );
		$phone_numbers = array_filter( $phone_numbers );

		if ( empty( $phone_numbers ) ) {
			wp_send_json(
				MoUtility::create_json(
					MoWcAddOnMessages::showMessage( MoWcAddOnMessages::INVALID_PHONE ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
			return;
		}

		$success_count = 0;
		foreach ( $phone_numbers as $number ) {
			$number = sanitize_text_field( $number );
			if ( ! MoUtility::is_blank( $number ) && MoUtility::send_phone_notif( $number, $message ) ) {
				++$success_count;
			}
		}

		if ( $success_count > 0 ) {
			wp_send_json(
				MoUtility::create_json(
					MoWcAddOnMessages::showMessage( MoWcAddOnMessages::SMS_SENT_SUCCESS ),
					MoConstants::SUCCESS_JSON_TYPE
				)
			);
		} else {
			wp_send_json(
				MoUtility::create_json(
					MoWcAddOnMessages::showMessage( MoWcAddOnMessages::ERROR_SENDING_SMS ),
					MoConstants::ERROR_JSON_TYPE
				)
			);
		}
	}


	/** Set Addon Key */
	public function set_addon_key() {
		$this->add_on_key = 'wc_sms_notification_addon';
	}

	/** Set AddOn Desc */
	public function set_add_on_desc() {
		$this->add_on_desc = mowc_(
			'Allows your site to send order and WooCommerce notifications to buyers, '
			. 'sellers and admins. Click on the settings button to the right to see the list of notifications '
			. 'that go out.'
		);
	}

	/** Set an AddOnName */
	public function set_add_on_name() {
		$this->addon_name = mowc_( 'WooCommerce SMS Notification' );
	}

	/** Set an Addon Docs link */
	public function set_add_on_docs() {
		$this->add_on_docs = MoFormDocs::WOCOMMERCE_SMS_NOTIFICATION_LINK['guideLink'];
	}

	/** Set an Addon Video link */
	public function set_add_on_video() {
		$this->add_on_video = MoFormDocs::WOCOMMERCE_SMS_NOTIFICATION_LINK['videoLink'];
	}

	/** Set Settings Page URL */
	public function set_settings_url() {
		$request_url        = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$this->settings_url = esc_url( add_query_arg( array( 'addon' => 'wc_notif' ), $request_url ) );
	}
}
