<?php

namespace Limb_Chatbot\Includes\Services\Notifications;

use Limb_Chatbot\Includes\Services\Notifications\Channels\Email_Notification_Channel;
use Limb_Chatbot\Includes\Services\Notifications\Types\Chat_Created_Notification_Type;
use Limb_Chatbot\Includes\Services\Notifications\Types\Action_Entry_Notification_Type;
use Limb_Chatbot\Includes\Services\Notifications\Types\Lead_Captured_Notification_Type;

/**
 * Bootstraps the notification system: registers types, channels, and Action Scheduler handler.
 *
 * Called once from the main plugin. Provides a single Notification_Service instance.
 *
 * @package Limb_Chatbot\Includes\Services\Notifications
 * @since 1.0.15
 */
class Notification_Bootstrap {

	/**
	 * @var Notification_Type_Registry|null
	 * @since 1.0.15
	 */
	private static ?Notification_Type_Registry $type_registry = null;

	/**
	 * @var Notification_Channel_Registry|null
	 * @since 1.0.15
	 */
	private static ?Notification_Channel_Registry $channel_registry = null;

	/**
	 * @var Notification_Service|null
	 * @since 1.0.15
	 */
	private static ?Notification_Service $service = null;

	/**
	 * Initialize the notification system (register types, channels, and action handler).
	 *
	 * @return void
	 * @since 1.0.15
	 */
	public static function init(): void {
		self::register_channels();
		self::register_types();
		self::register_action_scheduler_handler();
	}

	/**
	 * Register notification channels (email only by default).
	 *
	 * @return void
	 * @since 1.0.15
	 */
	private static function register_channels(): void {
		$registry = self::get_channel_registry();
		$registry->register( new Email_Notification_Channel() );

		/**
		 * Fires after default notification channels are registered.
		 * Use to add custom channels (e.g. push, SMS).
		 *
		 * @param Notification_Channel_Registry $registry Channel registry.
		 * @since 1.0.15
		 */
		do_action( 'lbaic_notification_channels_registered', $registry );
	}

	/**
	 * Register notification types (chat_created and others).
	 *
	 * @return void
	 * @since 1.0.15
	 */
	private static function register_types(): void {
		$registry = self::get_type_registry();
		$registry->register( new Chat_Created_Notification_Type() );
		$registry->register( new Lead_Captured_Notification_Type() );
		$registry->register( new Action_Entry_Notification_Type() );

		/**
		 * Fires after default notification types are registered.
		 * Use to add custom types (e.g. admin_alert, agent_assigned).
		 *
		 * @param Notification_Type_Registry $registry Type registry.
		 * @since 1.0.15
		 */
		do_action( 'lbaic_notification_types_registered', $registry );
	}

	/**
	 * Register the Action Scheduler (or immediate) handler for processing queued notifications.
	 *
	 * @return void
	 * @since 1.0.15
	 */
	private static function register_action_scheduler_handler(): void {
		add_action(
			Notification_Service::ACTION_PROCESS_NOTIFICATION,
			[ self::class, 'handle_process_notification' ],
			10,
			3
		);
	}

	/**
	 * Callback for Action Scheduler: process a single queued notification.
	 *
	 * @param  int  $chatbot_user_id  Chatbot user ID.
	 * @param  string  $type_key  Notification type key.
	 * @param  array  $payload  Context payload.
	 *
	 * @return void
	 * @since 1.0.15
	 */
	public static function handle_process_notification( int $chatbot_user_id, string $type_key, array $payload = [] ): void {
		$service = self::get_service();
		$service->process( $chatbot_user_id, $type_key, $payload );
	}

	/**
	 * Get the notification service instance.
	 *
	 * @return Notification_Service
	 * @since 1.0.15
	 */
	public static function get_service(): Notification_Service {
		if ( self::$service === null ) {
			$log_repository = null;
			if ( self::notification_log_table_exists() ) {
				$log_repository = new Notification_Log_Repository();
			}
			self::$service = new Notification_Service(
				self::get_type_registry(),
				self::get_channel_registry(),
				$log_repository
			);
		}

		return self::$service;
	}

	/**
	 * Get the type registry (lazy).
	 *
	 * @return Notification_Type_Registry
	 * @since 1.0.15
	 */
	public static function get_type_registry(): Notification_Type_Registry {
		if ( self::$type_registry === null ) {
			self::$type_registry = new Notification_Type_Registry();
		}

		return self::$type_registry;
	}

	/**
	 * Get the channel registry (lazy).
	 *
	 * @return Notification_Channel_Registry
	 * @since 1.0.15
	 */
	public static function get_channel_registry(): Notification_Channel_Registry {
		if ( self::$channel_registry === null ) {
			self::$channel_registry = new Notification_Channel_Registry();
		}

		return self::$channel_registry;
	}

	/**
	 * Check if notification log table exists (migration may not have run yet).
	 *
	 * @return bool
	 * @since 1.0.15
	 */
	private static function notification_log_table_exists(): bool {
		global $wpdb;
		$table = $wpdb->prefix . Notification_Log_Repository::TABLE_NAME;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table;
	}
}
