<?php
/**
 * Unified Messaging Service for Automation Web Platform.
 * Centralizes sending logic for WhatsApp, Meta, and Email.
 *
 * @package automation-web-platform
 */

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


/**
 * Class WAWP_Messaging_Service.
 */
class WAWP_Messaging_Service {

	use WAWP_Settings_Handler;
	use WAWP_Phone_Helper_Trait;
	use WAWP_Logging_Trait;

	/**
	 * Singleton instance.
	 *
	 * @var WAWP_Messaging_Service|null
	 */
	private static $instance = null;

	/**
	 * Last API/gateway response.
	 *
	 * @var array|null
	 */
	public $last_response = null;

	/**
	 * Get Singleton instance.
	 *
	 * @return WAWP_Messaging_Service
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * WAWP_Messaging_Service constructor.
	 */
	private function __construct() {
		// Modern hook.
		add_action( 'wawp_process_scheduled_message', array( $this, 'process_scheduled_message' ), 10, 1 );

		// Legacy hook (for existing crons in DB).
		add_action( 'wawp_notification_delivery', array( $this, 'handle_legacy_notification' ), 10, 1 );
	}

	/**
	 * Main dispatch method for sending messages.
	 *
	 * @param string|int $recipient Recipient.
	 * @param array      $args      Arguments.
	 * @return bool|string
	 */
	public function send( $recipient, $args ) {
		$send_timing = $args['send_timing'] ?? 'instant';
		if ( 'delayed' === $send_timing || 'delay' === $send_timing ) {
			return $this->schedule( $recipient, $args );
		}

		return $this->send_instant( $recipient, $args );
	}

	/**
	 * Deliver message immediately based on the selected channel.
	 *
	 * @param string|int $recipient Recipient.
	 * @param array      $args      Arguments.
	 * @return bool
	 */
	public function send_instant( $recipient, $args ) {
		$this->last_response = null;

		if ( empty( $recipient ) ) {
			$this->log_action( 'error', 'Failed to send: Recipient is empty', array(), 'Messaging Service' );

			return false;
		}

		if ( WAWP()->block_manager && WAWP()->block_manager->is_recipient_blocked( $recipient ) ) {
			// Log the blocked attempt in the unified log.
			if ( class_exists( 'WAWP_Unified_Log' ) ) {
				$channel = $args['channel'] ?? $args['sender_type'] ?? 'whatsapp';
				$message = $args['message'] ?? '';
				$subject = $args['subject'] ?? '';
				$details = array(
					'phone'        => is_email( $recipient ) ? '' : $recipient,
					'email'        => is_email( $recipient ) ? $recipient : '',
					'message'      => $message ? $message : $subject,
					'message_type' => 'Blocked Message',
					'response'     => array(
						'status' => 'failed',
						'error'  => 'Recipient is blocked/blacklisted.',
					),
					'context'      => $args['context'] ?? 'Blocked delivery',
				);
				WAWP()->unified_log->add_activity_log(
					array(
						'user_id'    => $args['user_id'] ?? null,
						'phone'      => $details['phone'],
						'email'      => $details['email'],
						'event_type' => 'message',
						'gateway'    => $channel,
						'log_status' => 'failed',
						'message'    => 'Blocked delivery: Recipient is blacklisted.',
						'details'    => $details,
					)
				);
			}

			return false;
		}

		// Deduplication Lock (10 seconds).
		$lock_key = 'wawp_msg_lock_' . md5( $recipient . wp_json_encode( $args ) );
		if ( get_transient( $lock_key ) ) {
			return true;
		}

		set_transient( $lock_key, 1, 10 );
		$channel = $args['channel'] ?? $args['sender_type'] ?? 'whatsapp';
		$result  = false;

		// Handle composite channels (e.g., user_whatsapp, admin_both).
		$is_both     = str_contains( $channel, 'both' );
		$is_whatsapp = ( str_contains( $channel, 'whatsapp' ) || $is_both ) && ! str_contains( $channel, 'meta' );
		$is_meta     = str_contains( $channel, 'meta' );
		$is_email    = str_contains( $channel, 'email' ) || $is_both;

		if ( $is_meta ) {
			$result = $this->send_meta_whatsapp( $recipient, $args );
		} elseif ( $is_whatsapp ) {
			$result = $this->send_whatsapp( $recipient, $args );
		}

		if ( $is_email ) {
			$email_result = $this->send_email( $recipient, $args );
			$result       = $result || $email_result;
		}

		return $result;
	}

	/**
	 * Send an OTP (One-Time Password) challenge via WhatsApp.
	 *
	 * @param string $recipient    Recipient.
	 * @param string $instance_id  Instance ID.
	 * @param string $access_token Access Token.
	 * @param array  $extra_params Optional extra parameters (lang, footer, footer_enabled).
	 * @return array
	 */
	public function send_otp( $recipient, $instance_id, $access_token, $extra_params = array() ) {
		list($digits, ) = $this->format_phone_number( $recipient );
		if ( empty( $digits ) ) {
			return array(
				'status'  => 'error',
				'message' => 'Invalid phone number.',
			);
		}

		if ( WAWP()->block_manager && WAWP()->block_manager->is_recipient_blocked( $recipient ) ) {
			// Log in Activity Hub.
			if ( class_exists( 'WAWP_Unified_Log' ) ) {
				$details = array(
					'phone'        => $recipient,
					'message'      => 'OTP Challenge Blocked',
					'message_type' => 'Blocked OTP',
					'response'     => array(
						'status' => 'failed',
						'error'  => 'Recipient is blocked/blacklisted.',
					),
					'context'      => 'OTP Login',
				);
				WAWP()->unified_log->add_activity_log(
					array(
						'phone'      => $recipient,
						'event_type' => 'message',
						'gateway'    => 'whatsapp',
						'log_status' => 'failed',
						'message'    => 'Blocked OTP: Recipient is blacklisted.',
						'details'    => $details,
					)
				);
			}
			return array(
				'status'  => 'error',
				'message' => esc_html__( 'Recipient is blocked/blacklisted.', 'automation-web-platform' ),
			);
		}

		$response = WAWP_Senders::send_otp( $instance_id, $access_token, $recipient, $extra_params );
		return $response;
	}

	/**
	 * Send an Interactive Auth List via WhatsApp.
	 *
	 * @param string $recipient    Recipient.
	 * @param string $instance_id  Instance ID.
	 * @param string $access_token Access Token.
	 * @param array  $list_data    List data.
	 * @return array
	 */
	public function send_auth_list( $recipient, $instance_id, $access_token, $list_data ) {
		if ( WAWP()->block_manager && WAWP()->block_manager->is_recipient_blocked( $recipient ) ) {
			// Log in Activity Hub.
			if ( class_exists( 'WAWP_Unified_Log' ) ) {
				$details = array(
					'phone'        => $recipient,
					'message'      => 'Auth List Blocked',
					'message_type' => 'Blocked Auth List',
					'response'     => array(
						'status' => 'failed',
						'error'  => 'Recipient is blocked/blacklisted.',
					),
					'context'      => 'Auth List Login',
				);
				WAWP()->unified_log->add_activity_log(
					array(
						'phone'      => $recipient,
						'event_type' => 'message',
						'gateway'    => 'whatsapp',
						'log_status' => 'failed',
						'message'    => 'Blocked Auth List: Recipient is blacklisted.',
						'details'    => $details,
					)
				);
			}
			return array(
				'status'  => 'error',
				'message' => esc_html__( 'Recipient is blocked/blacklisted.', 'automation-web-platform' ),
			);
		}
		$response = WAWP_Senders::send_auth_list( $instance_id, $access_token, $recipient, $list_data );
		return $response;
	}

	/**
	 * Check Authentication status for a challenge.
	 *
	 * @param string $auth_id      Auth ID.
	 * @param string $instance_id  Instance ID.
	 * @param string $access_token Access Token.
	 * @return array
	 */
	public function check_auth_status( $auth_id, $instance_id, $access_token ) {
		return WAWP_Senders::check_auth_status( $instance_id, $access_token, $auth_id );
	}

	/**
	 * Schedule a message for later delivery.
	 *
	 * @param string|int $recipient Recipient.
	 * @param array      $args      Arguments.
	 * @return bool
	 */
	public function schedule( $recipient, $args ) {
		$delay_value = (int) ( $args['delay_value'] ?? 1 );
		$delay_unit  = $args['delay_unit'] ?? 'minutes';
		$seconds     = $delay_value * ( 'hours' === $delay_unit ? HOUR_IN_SECONDS : ( 'days' === $delay_unit ? DAY_IN_SECONDS : MINUTE_IN_SECONDS ) );
		$cron_args   = array(
			'recipient' => $recipient,
			'args'      => $args,
		);

		// Prevent duplicate scheduling within the same minute bucket.
		$bucket   = (int) floor( time() / 60 );
		$lock_key = 'wawp_msg_sched_' . md5( wp_json_encode( $cron_args ) . '|' . $bucket );
		if ( get_transient( $lock_key ) ) {
			return true;
		}

		set_transient( $lock_key, 1, 90 );

		return WAWP_Task_Scheduler::schedule_single( time() + $seconds, 'wawp_process_scheduled_message', array( $cron_args ) );
	}

	/**
	 * Callback for scheduled messages.
	 *
	 * @param array $data Input data.
	 */
	public function process_scheduled_message( $data ) {
		if ( empty( $data['recipient'] ) || empty( $data['args'] ) ) {
			return;
		}

		$args                = $data['args'];
		$args['send_timing'] = 'instant';

		// Ensure it's not rescheduled.
		$this->send_instant( $data['recipient'], $args );
	}

	/**
	 * Handle legacy notification crons from Flow Builder.
	 *
	 * @param array $args Arguments.
	 */
	public function handle_legacy_notification( $args ) {
		if ( empty( $args ) || ! is_array( $args ) ) {
			return;
		}
		$recipient_user = $args['recipient_user'] ?? array();
		$recipient      = $recipient_user['phone'] ?? $recipient_user['email'] ?? '';
		if ( empty( $recipient ) ) {
			return;
		}
		$sender_type = $args['sender_type'] ?? 'user_whatsapp';
		$new_args    = array_merge(
			$args,
			array(
				'channel'      => $sender_type,
				'message'      => $args['whatsapp_message'] ?? $args['email_body'] ?? '',
				'subject'      => $args['email_subject'] ?? '',
				'media_url'    => $args['whatsapp_media_url'] ?? '',
				'replacements' => $args['replacements_user'] ?? array(),
				'user_id'      => $recipient_user['user_id'] ?? 0,
				'related_id'   => $args['related_object_id'] ?? null,
				'context'      => ( $args['log_message_type_base'] ?? 'Scheduled Notification' ) . ' (Legacy)',
				'send_timing'  => 'instant',
			)
		);
		$this->send_instant( $recipient, $new_args );
	}

	/**
	 * Send via WhatsApp Web (WAWP).
	 *
	 * @param string $phone Phone number.
	 * @param array  $args  Arguments.
	 * @return bool
	 */
	private function send_whatsapp( $phone, $args ) {
		list($digits, ) = $this->format_phone_number( $phone );
		if ( empty( $digits ) ) {
			$this->last_response = array(
				'status'  => 'error',
				'message' => 'Invalid phone number for WhatsApp.',
			);
			$this->log_action(
				'failed',
				'Invalid phone number for WhatsApp.',
				array(
					'recipient' => $phone,
					'user_id'   => $args['user_id'] ?? 0,
					'error'     => 'Format returned empty digits.',
				),
				'whatsapp'
			);

			return false;
		}

		$phone      = $digits;
		$message    = $this->parse_content( $args['message'] ?? '', $args );
		$media_url  = $args['media_url'] ?? '';
		$user_id    = $args['user_id'] ?? 0;
		$context    = $args['context'] ?? 'WhatsApp Notification';
		$related_id = $args['related_id'] ?? null;
		$skip_log   = ! empty( $args['skip_log'] );

		// Select instance (load balance among online instances).
		$instance = $this->get_instance_to_use( $args );
		if ( ! $instance ) {
			$this->last_response = array(
				'status'  => 'error',
				'message' => 'No online WhatsApp instance found.',
			);
			$this->log_action(
				'failed',
				'No online WhatsApp instance found.',
				array(
					'recipient' => $phone,
					'user_id'   => $user_id,
					'order_id'  => $related_id,
					'error'     => 'Load balancer returned no instance.',
				),
				'whatsapp'
			);

			return false;
		}

		$response            = WAWP_Senders::send_message( $instance->instance_id, $instance->access_token, $phone, $message, $media_url );
		$this->last_response = $response;
		$status              = ( isset( $response['status'] ) && 'success' === $response['status'] ) ? 'success' : 'failed';
		if ( ! $skip_log ) {
			$this->log_action(
				$status,
				$message,
				array(
					'recipient' => $phone,
					'user_id'   => $user_id,
					'order_id'  => $related_id,
					'context'   => $context,
					'response'  => $response,
					'rule_id'   => $args['id'] ?? null,
				),
				'whatsapp'
			);
		}

		return ( isset( $response['status'] ) && 'success' === $response['status'] );
	}

	/**
	 * Send via Meta WhatsApp API.
	 *
	 * @param string $phone Phone number.
	 * @param array  $args  Arguments.
	 * @return bool
	 */
	private function send_meta_whatsapp( $phone, $args ) {
		list($digits, ) = $this->format_phone_number( $phone );
		if ( empty( $digits ) ) {
			$this->last_response = array(
				'status'  => 'error',
				'message' => 'Invalid phone number for Meta WhatsApp.',
			);
			$this->log_action(
				'failed',
				'Invalid phone number for Meta WhatsApp.',
				array(
					'recipient' => $phone,
					'user_id'   => $args['user_id'] ?? 0,
					'error'     => 'Format returned empty digits.',
				),
				'meta'
			);

			return false;
		}

		$phone      = $digits;
		$user_id    = $args['user_id'] ?? 0;
		$context    = $args['context'] ?? 'Meta WhatsApp';
		$related_id = $args['related_id'] ?? null;
		$skip_log   = ! empty( $args['skip_log'] );

		if ( ! class_exists( 'WAWP_Meta_Sender' ) ) {
			$this->last_response = array(
				'status'  => 'error',
				'message' => 'Meta Sender class not found.',
			);
			return false;
		}

		$sender_instance = new WAWP_Meta_Sender( null );
		$message_type    = $args['meta_type'] ?? $args['meta_message_type'] ?? 'text';

		if ( 'template' === $message_type ) {
			$template_name = $args['template_name'] ?? '';
			$template_lang = $args['template_lang'] ?? 'en';
			$components    = $args['components'] ?? array();
			$result        = $sender_instance->send_template( $phone, $template_name, $template_lang, $components );
		} else {
			$message = $this->parse_content( $args['message'] ?? '', $args );
			$result  = $sender_instance->send_message( $phone, $message );
		}

		$this->last_response = $result;
		$status              = ( isset( $result['status'] ) && 'success' === $result['status'] ) ? 'success' : 'failed';
		if ( ! $skip_log ) {
			$this->log_action(
				$status,
				'Meta WhatsApp',
				array(
					'recipient' => $phone,
					'user_id'   => $user_id,
					'order_id'  => $related_id,
					'gateway'   => 'meta',
					'context'   => $context,
					'response'  => $result,
					'rule_id'   => $args['id'] ?? null,
				),
				'meta'
			);
		}

		return ( isset( $result['status'] ) && 'success' === $result['status'] );
	}

	/**
	 * Send via Email.
	 *
	 * @param string $email Email address.
	 * @param array  $args  Arguments.
	 * @return bool
	 */
	private function send_email( $email, $args ) {
		if ( empty( $email ) || ! is_email( $email ) ) {
			return false;
		}

		$subject    = $this->parse_content( $args['subject'] ?? 'Notification', $args );
		$body       = $this->parse_content( $args['message'] ?? '', $args );
		$user_id    = $args['user_id'] ?? 0;
		$context    = $args['context'] ?? 'Email Notification';
		$related_id = $args['related_id'] ?? null;

		$skip_log = ! empty( $args['skip_log'] );
		$log_id   = false;

		if ( ! $skip_log ) {
			// 1. Initial Log (Pending)
			$log_id = WAWP()->unified_log->log_message(
				array(
					'phone'        => $email,
					'message'      => $body,
					'message_type' => $subject,
					'log_status'   => 'pending',
					'user_id'      => $user_id,
					'order_id'     => $related_id,
					'gateway'      => 'email',
					'context'      => $context,
					'rule_id'      => $args['id'] ?? null,
				)
			);
		}

		// 2. Inject Tracking Pixel
		if ( $log_id && $this->get_wawp_setting( WAWP_Settings_Registry::EMAIL_TRACKING_ENABLED, 0 ) ) {
			$tracking_url = add_query_arg( 'wawp_email_open', $log_id, home_url( '/' ) );
			$pixel        = '<img src="' . esc_url( $tracking_url ) . '" width="1" height="1" style="display:none;" alt="" />';
			$body        .= $pixel;
		}

		$headers = array( 'Content-Type: text/html; charset=UTF-8' );

		if ( ! empty( $args['is_html_template'] ) ) {
			$body_processed = $body; // Preserve full HTML and CSS for templates.
		} else {
			$body_processed = wpautop( wp_kses_post( $body ) );
		}

		// 3. Send
		$sent                = wp_mail( $email, $subject, $body_processed, $headers );
		$this->last_response = array(
			'status'  => $sent ? 'success' : 'error',
			'message' => $sent ? 'Sent successfully' : 'wp_mail() returned false',
		);

		// 4. Update Log with result
		if ( $log_id ) {
			WAWP()->unified_log->update_activity_log( $log_id, $sent ? 'success' : 'failed', $sent ? 'OK' : 'wp_mail() returned false' );
		}

		return $sent;
	}

	/**
	 * Helper: Parse placeholders in content.
	 *
	 * @param string $content Content.
	 * @param array  $args    Arguments.
	 * @return string
	 */
	private function parse_content( $content, $args ) {
		if ( empty( $content ) ) {
			return '';
		}

		return WAWP_Message_Parser::parse_message_placeholders( $content, $args['replacements'] ?? array(), $args['related_id'] ?? null, $args['user_id'] ?? null );
	}

	/**
	 * Helper: Determine which WhatsApp instance to use.
	 *
	 * @param array $args Arguments.
	 * @return object|null
	 */
	private function get_instance_to_use( $args ) {
		$instance_manager = WAWP()->instance_manager;
		$settings         = WAWP()->notification_manager->get_global_settings();
		$is_admin         = str_contains( $args['channel'] ?? '', 'admin' );
		$ids_csv          = $is_admin ? ( $settings['admin_selected_instance_ids'] ?? '' ) : ( $settings['selected_instance_ids'] ?? '' );

		$ids = array_filter( array_map( 'intval', explode( ',', $ids_csv ) ) );

		// If no specific IDs selected, get any online instance.
		if ( ! $ids ) {
			return $instance_manager->get_first_online_instance();
		}

		$online_instances = $instance_manager->get_online_instances_cached();
		$available        = array();
		foreach ( $online_instances as $inst ) {
			if ( in_array( (int) $inst->id, $ids, true ) ) {
				$available[] = $inst;
			}
		}

		if ( ! $available ) {
			return $instance_manager->get_first_online_instance(); // Absolute fallback.
		}

		// Random selection for load balancing.
		return $available[ array_rand( $available ) ];
	}
}
