<?php
/**
 * Block Manager.
 *
 * @package automation-web-platform
 */

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

/**
 * WAWP_Block_Manager Class
 * Handles blacklisted phone numbers and blocking logic.
 */
class WAWP_Block_Manager {
	use WAWP_Security_Trait;
	use WAWP_Settings_Handler;
	use WAWP_Phone_Helper_Trait;

	/**
	 * Parent instance.
	 *
	 * @var object
	 */
	private $parent;

	/**
	 * Constructor.
	 *
	 * @param object $wawp_instance Parent instance.
	 */
	public function __construct( $wawp_instance ) {
		$this->parent = $wawp_instance;
		add_filter( 'pre_add_option_wawp_block_list', array( $this, 'sync_blocked_numbers_db' ), 10 );
		add_filter( 'pre_update_option_wawp_block_list', array( $this, 'sync_blocked_numbers_db' ), 10 );
		add_filter( 'pre_add_option_wawp_block_email_list', array( $this, 'clear_emails_cache' ), 10 );
		add_filter( 'pre_update_option_wawp_block_email_list', array( $this, 'clear_emails_cache' ), 10 );
		add_filter( 'authenticate', array( $this, 'block_user_authentication' ), 99, 3 );
	}

	/**
	 * Synchronize block list option to blocked numbers database table.
	 *
	 * @param mixed $value New block list value.
	 * @return mixed
	 */
	public function sync_blocked_numbers_db( $value ) {
		$numbers = array();
		if ( is_string( $value ) ) {
			$numbers = array_filter( array_map( 'trim', explode( "\n", $value ) ) );
		} elseif ( is_array( $value ) ) {
			$numbers = array_filter( array_map( 'trim', $value ) );
		}
		$this->upsert_blocked_numbers( $numbers );
		return $value;
	}

	/**
	 * Get React data.
	 *
	 * @return array
	 */
	public function get_data() {
		return array(
			'blockedNumbers' => $this->get_blocked_numbers(),
			'blockedEmails'  => $this->get_blocked_emails(),
			'settings'       => $this->get_settings(),
			'nonces'         => array(
				'save' => wp_create_nonce( 'wawp_nonce' ),
			),
			'msgs'           => array(
				'title'    => esc_html__( 'Block Manager', 'automation-web-platform' ),
				'subtitle' => esc_html__( 'Manage blacklisted phone numbers and domains to prevent unwanted automated messages.', 'automation-web-platform' ),
			),
			'i18n'           => array(
				'pageTitle'                  => esc_html__( 'Access Control', 'automation-web-platform' ),
				'pageSubtitle'               => esc_html__( 'Restrict access and delivery to specific phone numbers globally.', 'automation-web-platform' ),
				'updatingList'               => esc_html__( 'Updating List...', 'automation-web-platform' ),
				'shieldUpdated'              => esc_html__( 'Shield Updated', 'automation-web-platform' ),
				'unsavedChanges'             => esc_html__( 'Unsaved Changes', 'automation-web-platform' ),
				'deployBlockList'            => esc_html__( 'Deploy Block List', 'automation-web-platform' ),
				'globalBlockList'            => esc_html__( 'Global Block List', 'automation-web-platform' ),
				'globalBlockListDesc'        => esc_html__( 'Numbers here will be denied all services', 'automation-web-platform' ),
				'addNumberToBlocklist'       => esc_html__( 'Add Number to Blocklist', 'automation-web-platform' ),
				'phoneNumberPlaceholder'     => esc_html__( 'Phone number with country code', 'automation-web-platform' ),
				'addButton'                  => esc_html__( 'Add', 'automation-web-platform' ),
				'whatsAppActive'             => esc_html__( 'WhatsApp Active', 'automation-web-platform' ),
				'currentlyRestrictedNumbers' => esc_html__( 'Currently Restricted Numbers', 'automation-web-platform' ),
				'clearAll'                   => esc_html__( 'Clear All', 'automation-web-platform' ),
				'emptyBlocklist'             => esc_html__( 'The blocklist is currently empty', 'automation-web-platform' ),
				'unblockNumber'              => esc_html__( 'Unblock Number', 'automation-web-platform' ),
				'invalidNumberError'         => esc_html__( 'Please enter a valid phone number.', 'automation-web-platform' ),
				'numberAddedSuccess'         => esc_html__( 'added to list.', 'automation-web-platform' ),
				'numberAlreadyExists'        => esc_html__( 'Number is already in the list.', 'automation-web-platform' ),
				'saveSuccess'                => esc_html__( 'Block list updated successfully!', 'automation-web-platform' ),
				'saveError'                  => esc_html__( 'Failed to save block list.', 'automation-web-platform' ),
				'unexpectedError'            => esc_html__( 'An unexpected error occurred.', 'automation-web-platform' ),
				'phoneBlockList'             => esc_html__( 'Blocked Phone Numbers', 'automation-web-platform' ),
				'emailBlockList'             => esc_html__( 'Blocked Email Addresses', 'automation-web-platform' ),
				'emailBlockListTitle'        => esc_html__( 'Email Block List', 'automation-web-platform' ),
				'emailBlockListDesc'         => esc_html__( 'Emails here will be denied all message routing services', 'automation-web-platform' ),
				'addEmailToBlocklist'        => esc_html__( 'Add Email to Blocklist', 'automation-web-platform' ),
				'emailPlaceholder'           => esc_html__( 'Enter email address to block', 'automation-web-platform' ),
				'currentlyRestrictedEmails'  => esc_html__( 'Currently Restricted Emails', 'automation-web-platform' ),
				'emptyEmailBlocklist'        => esc_html__( 'The email blocklist is currently empty', 'automation-web-platform' ),
				'unblockEmail'               => esc_html__( 'Unblock Email', 'automation-web-platform' ),
				'invalidEmailError'          => esc_html__( 'Please enter a valid email address.', 'automation-web-platform' ),
				'emailAddedSuccess'          => esc_html__( 'added to list.', 'automation-web-platform' ),
				'emailAlreadyExists'         => esc_html__( 'Email is already in the list.', 'automation-web-platform' ),
			),
		);
	}

	/**
	 * Get block manager settings.
	 *
	 * @return array
	 */
	public function get_settings() {
		return array(
			'block_list'         => $this->get_wawp_setting( WAWP_Settings_Registry::BLOCK_LIST, '' ),
			'block_email_list'   => $this->get_wawp_setting( WAWP_Settings_Registry::BLOCK_EMAIL_LIST, '' ),
			'site_block_reasons' => $this->get_wawp_setting( WAWP_Settings_Registry::SITE_B_BLOCK_REASONS, array() ),
		);
	}

	/**
	 * Get blocked phone numbers from DB.
	 *
	 * @return array
	 */
	public function get_blocked_numbers(): array {
		$cache_key = 'wawp_blocked_numbers_list_flat';
		$results   = wp_cache_get( $cache_key, 'wawp_messaging' );

		if ( false === $results ) {
			// Read from the option — always kept in sync with the DB table via sync_blocked_numbers_db hook.
			$option_val = get_option( 'wawp_block_list', '' );
			if ( ! empty( $option_val ) ) {
				$numbers = array_filter( array_map( 'trim', explode( "\n", $option_val ) ) );
				$results = array_values(
					array_unique(
						array_map(
							function ( $p ) {
								return self::get_digits_only( $p );
							},
							$numbers
						)
					)
				);
			} else {
				$results = array();
			}
			wp_cache_set( $cache_key, $results, 'wawp_messaging', HOUR_IN_SECONDS );
		}

		return $results;
	}

	/**
	 * Upsert blocked phone numbers.
	 *
	 * @param array $numbers Phone numbers to block.
	 */
	public function upsert_blocked_numbers( array $numbers ) {
		WAWP()->db->truncate_row( 'blocked_numbers' );
		foreach ( array_unique( $numbers ) as $p ) {
			WAWP()->db->insert( 'blocked_numbers', array( 'phone' => self::get_digits_only( $p ) ) );
		}
		wp_cache_delete( 'wawp_blocked_numbers_list_flat', 'wawp_messaging' );
	}

	/**
	 * Check if a phone number is blocked.
	 *
	 * @param string $phone Phone number.
	 * @return bool
	 */
	public function is_phone_blocked( string $phone ): bool {
		$blocked = $this->get_blocked_numbers();
		return in_array( self::get_digits_only( $phone ), $blocked, true );
	}

	/**
	 * Get blocked email addresses.
	 *
	 * @return array
	 */
	public function get_blocked_emails(): array {
		$cache_key = 'wawp_blocked_emails_list';
		$results   = wp_cache_get( $cache_key, 'wawp_messaging' );

		if ( false === $results ) {
			$option_val = get_option( 'wawp_block_email_list', '' );
			if ( ! empty( $option_val ) ) {
				$emails  = array_filter( array_map( 'trim', explode( "\n", $option_val ) ) );
				$results = array_values( array_unique( array_map( 'strtolower', $emails ) ) );
			} else {
				$results = array();
			}
			wp_cache_set( $cache_key, $results, 'wawp_messaging', HOUR_IN_SECONDS );
		}

		return $results;
	}

	/**
	 * Clear blocked emails cache.
	 *
	 * @param mixed $value Option value.
	 * @return mixed
	 */
	public function clear_emails_cache( $value ) {
		wp_cache_delete( 'wawp_blocked_emails_list', 'wawp_messaging' );
		return $value;
	}

	/**
	 * Check if an email address is blocked.
	 *
	 * @param string $email Email address.
	 * @return bool
	 */
	public function is_email_blocked( string $email ): bool {
		$blocked = $this->get_blocked_emails();
		return in_array( strtolower( trim( $email ) ), $blocked, true );
	}

	/**
	 * Check if a WordPress user is blocked (by email or phone number).
	 *
	 * @param WP_User|int $user User object or ID.
	 * @return bool
	 */
	public function is_user_blocked( $user ): bool {
		if ( is_numeric( $user ) ) {
			$user = get_userdata( (int) $user );
		}
		if ( ! $user instanceof WP_User ) {
			return false;
		}

		// Check email first.
		if ( ! empty( $user->user_email ) && $this->is_email_blocked( $user->user_email ) ) {
			return true;
		}

		// Check phone numbers.
		$phones = array();

		// Custom table.
		if ( isset( $this->parent->user_manager ) ) {
			$phones[] = $this->parent->user_manager->get_user_phone( $user->ID );
		}

		// User meta.
		$meta_keys = array( '_wawp_phone', 'billing_phone', 'phone', 'wawp_user_phone' );
		foreach ( $meta_keys as $key ) {
			$meta_val = get_user_meta( $user->ID, $key, true );
			if ( ! empty( $meta_val ) ) {
				$phones[] = $meta_val;
			}
		}

		// Check each unique phone.
		$phones = array_filter( array_unique( $phones ) );
		foreach ( $phones as $phone ) {
			if ( $this->is_phone_blocked( $phone ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check if a recipient (phone, email, user ID, or user object) is blocked.
	 *
	 * @param mixed $recipient Recipient identifier (phone string, email string, user ID, or WP_User).
	 * @return bool
	 */
	public function is_recipient_blocked( $recipient ): bool {
		if ( empty( $recipient ) ) {
			return false;
		}

		// 1. If it's a WP_User object or a numeric User ID, check user block status.
		// Note: Phone numbers are numeric but have 7 or more digits, so we exclude them from being treated as user IDs.
		if ( $recipient instanceof WP_User || ( is_numeric( $recipient ) && strlen( (string) $recipient ) < 7 ) ) {
			return $this->is_user_blocked( $recipient );
		}

		// 2. If it's a string, determine if email or phone.
		if ( is_string( $recipient ) ) {
			$recipient = trim( $recipient );
			if ( is_email( $recipient ) ) {
				// Check if email is directly blocked.
				if ( $this->is_email_blocked( $recipient ) ) {
					return true;
				}
				// Check if there is an associated user, and if they are blocked.
				$user = get_user_by( 'email', $recipient );
				if ( $user && $this->is_user_blocked( $user ) ) {
					return true;
				}
			} else {
				// Check if phone is directly blocked.
				if ( $this->is_phone_blocked( $recipient ) ) {
					return true;
				}
				// Check if there is an associated user, and if they are blocked.
				if ( isset( $this->parent->user_manager ) ) {
					$user = $this->parent->user_manager->get_user_by_phone( $recipient );
					if ( $user && $this->is_user_blocked( $user ) ) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Intercept WordPress authenticate hook.
	 *
	 * @param WP_User|WP_Error|null $user     User object or error.
	 * @param string                $username Username or email.
	 * @param string                $password Password.
	 * @return WP_User|WP_Error
	 */
	public function block_user_authentication( $user, $username, $password ) {
		unset( $password );
		$check_user = null;
		if ( $user instanceof WP_User ) {
			$check_user = $user;
		} elseif ( ! empty( $username ) ) {
			$check_user = get_user_by( 'login', $username );
			if ( ! $check_user && is_email( $username ) ) {
				$check_user = get_user_by( 'email', $username );
			}
		}

		if ( $check_user && $this->is_user_blocked( $check_user ) ) {
			// Log the authentication failure.
			if ( class_exists( 'WAWP_Unified_Log' ) ) {
				$this->parent->unified_log->log_auth(
					$check_user->ID,
					'login',
					'credentials',
					'failed',
					array(
						'reason' => esc_html__( 'Login blocked: Account is blacklisted.', 'automation-web-platform' ),
						'email'  => $check_user->user_email,
					)
				);
			}
			return new WP_Error(
				'wawp_user_blocked',
				esc_html__( 'Your account is blocked.', 'automation-web-platform' )
			);
		}

		return $user;
	}
}
