<?php
/**
 * Task scheduler service for background jobs and recurring tasks.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Services;

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

use Awesomemotive\Rewardswp\Factories\MailerFactory;
use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Exception;
use function Awesomemotive\Rewardswp\Helpers\create_member;
use function Awesomemotive\Rewardswp\Helpers\get_member_by;
use function Awesomemotive\Rewardswp\Helpers\get_current_time;
use function Awesomemotive\Rewardswp\Helpers\get_rewards;
use function Awesomemotive\Rewardswp\Helpers\rewardswp_log_error;
use function Awesomemotive\Rewardswp\Helpers\get_user_first_name;
use function Awesomemotive\Rewardswp\Helpers\get_user_last_name;
use function Awesomemotive\Rewardswp\Helpers\get_option_value;
use Awesomemotive\Rewardswp\Settings\Options\Points\EarnActionsOption;

class TaskSchedulerService {
	/**
	 * Mailer factory instance.
	 *
	 * @var MailerFactory
	 */
	private MailerFactory $mailer_factory;

	/**
	 * Integration instance.
	 *
	 * @var IntegrationInterface
	 */
	private IntegrationInterface $integration;

	/**
	 * Points service instance.
	 *
	 * @var PointsService
	 */
	private PointsService $points_service;

	/**
	 * Notification importer service instance.
	 *
	 * @since 1.1.0
	 *
	 * @var NotificationImporterService
	 */
	private NotificationImporterService $notification_importer;

	/**
	 * Birthday service instance.
	 *
	 * @since 1.3.0
	 *
	 * @var BirthdayService
	 */
	private BirthdayService $birthday_service;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param MailerFactory               $mailer_factory        Mailer factory instance.
	 * @param IntegrationInterface        $integration           Integration instance.
	 * @param PointsService               $points_service        Points service instance.
	 * @param NotificationImporterService $notification_importer Notification importer instance.
	 * @param BirthdayService             $birthday_service      Birthday service instance.
	 */
	public function __construct(
		MailerFactory $mailer_factory,
		IntegrationInterface $integration,
		PointsService $points_service,
		NotificationImporterService $notification_importer,
		BirthdayService $birthday_service
	) {
		$this->mailer_factory        = $mailer_factory;
		$this->integration           = $integration;
		$this->points_service        = $points_service;
		$this->notification_importer = $notification_importer;
		$this->birthday_service      = $birthday_service;
	}

	/**
	 * Update status to 'canceled' for expired rewards.
	 *
	 * Processes rewards in batches of 50 to avoid memory issues.
	 * If more rewards exist, schedules the next page for processing.
	 *
	 * @since 1.0.0
	 *
	 * @param int $page Current page number for pagination.
	 *
	 * @return void
	 *
	 * @throws Exception
	 */
	public function task_update_expired_rewards( int $page = 1 ): void {
		$rewards = get_rewards(
			[
				'status'     => 'issued',
				'date_query' => [
					'column' => 'expires_at',
					[
						'before'    => get_current_time(),
						'inclusive' => false,
						'compare'   => '<',
					],
				],
			],
			50,
			$page
		);

		if ( empty( $rewards ) ) {
			return; // Bail if there's nothing to expire.
		}

		foreach ( $rewards as $reward ) {
			$reward->set_as_canceled();
		}

		// If we processed a full batch, there might be more. Schedule next page.
		if ( count( $rewards ) === 50 ) {
			as_schedule_single_action(
				time() + 60,
				'rewardswp_task_update_expired_rewards',
				[
					'page' => $page + 1,
				]
			);
		}
	}

	/**
	 * Handle the task to send emails asynchronously.
	 *
	 * @since 1.0.0
	 *
	 * @param string       $mailer_name
	 * @param string|array $recipients
	 * @param array        $data
	 *
	 * @return void
	 *
	 * @throws Exception
	 */
	public function task_send_email( string $mailer_name, $recipients, array $data ): void {
		$mailer = $this->mailer_factory->get_mailer( $mailer_name );

		if ( is_null( $mailer ) ) {
			throw new Exception(
				sprintf(
					/* translators: %s is the mailer name */
					esc_html__( 'Failed to find mailer %s when attempting to send email asynchronously', 'rewardswp' ),
					esc_html( $mailer_name )
				)
			);
		}

		$mailer
			->set_recipient( $recipients )
			->set_data( $data )
			->send();
	}

	/**
	 * Handle the task to auto enroll customers into the program.
	 *
	 * @since 1.0.0
	 *
	 * @param int $page
	 *
	 * @return void
	 *
	 * @throws Exception
	 */
	public function task_auto_enroll_customers( int $page = 1 ): void {
		$customers = $this->integration->get_customers( [], $page, 50 );

		if ( empty( $customers ) ) {
			update_option( 'rewardswp_initial_conversion_complete', true, false );
			as_unschedule_all_actions( 'rewardswp_task_auto_enroll_customers' );

			return;
		}

		foreach ( $customers as $customer ) {
			$user_id = $customer['user_id'] ?? 0;

			if ( empty( $user_id ) || get_member_by( 'user_id', $user_id ) ) {
				continue; // Skip if non wp user or if it is already an advocate.
			}

			create_member(
				[
					'user_id'    => $user_id,
					'email'      => $customer['email'] ?? '',
					'first_name' => get_user_first_name( $user_id ) ?? '',
					'last_name'  => get_user_last_name( $user_id ) ?? '',
				]
			);
		}

		// Schedule a task for the next page (5 minutes interval).
		as_schedule_single_action(
			time() + 300,
			'rewardswp_task_auto_enroll_customers',
			[
				'page' => $page + 1,
			]
		);
	}

	/**
	 * Handle the task to sync notifications from remote feed.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function task_sync_notifications(): void {
		try {
			$this->notification_importer->sync();
		} catch ( Exception $e ) {
			rewardswp_log_error(
				'TaskSchedulerService failed to sync notifications: {message}',
				[
					'message' => $e->getMessage(),
				]
			);
		}
	}

	/**
	 * Handle the task to award birthday points to members.
	 *
	 * This task runs daily at 10:00 AM store timezone and awards points
	 * to members whose birthday is today.
	 *
	 * @since 1.3.0
	 *
	 * @param int $page Current page for pagination.
	 *
	 * @return void
	 */
	public function task_award_birthday_points( int $page = 1 ): void {
		$earn_actions = get_option_value( EarnActionsOption::class );

		// Check if birthday action is enabled.
		if ( empty( $earn_actions['birthday']['enabled'] ) ) {
			return;
		}

		try {
			$this->birthday_service->process_birthday_points( $page );
		} catch ( Exception $e ) {
			rewardswp_log_error(
				'TaskSchedulerService failed to award birthday points: {message}',
				[
					'message' => $e->getMessage(),
					'page'    => $page,
				]
			);
		}
	}
}
