<?php
/**
 * Base dependency injection definitions.
 *
 * @package Awesomemotive\Rewardswp\DependencyInjection
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\DependencyInjection;

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

use Awesomemotive\Rewardswp\Abstracts\AbstractIntegrationWithCoupon;
use Awesomemotive\Rewardswp\Api\V1\Routes;
use Awesomemotive\Rewardswp\Controllers\ApiController;
use Awesomemotive\Rewardswp\Controllers\PrivacyController;
use Awesomemotive\Rewardswp\Controllers\Admin\DashboardAdminController;
use Awesomemotive\Rewardswp\Abstracts\AbstractView;
use Awesomemotive\Rewardswp\Controllers\Admin\EmailsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\LicenseController;
use Awesomemotive\Rewardswp\Controllers\Admin\MembersAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\NotificationsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\PointsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\ReferralsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\RewardsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\ReportsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\SettingsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\SetupAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\ToolsAdminController;
use Awesomemotive\Rewardswp\Controllers\Admin\UsersAdminController;
use Awesomemotive\Rewardswp\Controllers\AdvocateController;
use Awesomemotive\Rewardswp\Controllers\ClaimController;
use Awesomemotive\Rewardswp\Controllers\IntegrationController;
use Awesomemotive\Rewardswp\Controllers\OrderLinkingVerificationController;
use Awesomemotive\Rewardswp\Controllers\PointsController;
use Awesomemotive\Rewardswp\Controllers\EventsController;
use Awesomemotive\Rewardswp\Controllers\TrackingController;
use Awesomemotive\Rewardswp\Database\Tables\EmailsTable;
use Awesomemotive\Rewardswp\Database\Tables\EventsTable;
use Awesomemotive\Rewardswp\Database\Tables\MembersTable;
use Awesomemotive\Rewardswp\Database\Tables\NotificationsTable;
use Awesomemotive\Rewardswp\Database\Tables\PointsTable;
use Awesomemotive\Rewardswp\Database\Tables\ReferralsTable;
use Awesomemotive\Rewardswp\Database\Tables\RewardsTable;
use Awesomemotive\Rewardswp\Database\Upgrades\AddContextColumnToEventsTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\AddPointsBalanceColumnToMemberTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\AddTokenColumnToMembersTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\CreateEmailsTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\CreateNotificationsTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\CreatePointsTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\FlushRewriteRulesUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\RenameAdvocateIdToMemberIdInEventsTableUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\SeedBirthdayEmailUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\SeedDefaultEmailsUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\TranslatableEmailSubjectsUpgrade;
use Awesomemotive\Rewardswp\Database\Upgrades\UnscheduleLegacyTasksUpgrade;
use Awesomemotive\Rewardswp\Factories\IntegrationsFactory;
use Awesomemotive\Rewardswp\Integrations\EasyDigitalDownloadsIntegration;
use Awesomemotive\Rewardswp\Integrations\UnknownIntegration;
use Awesomemotive\Rewardswp\Integrations\WooCommerceIntegration;
use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Awesomemotive\Rewardswp\Pluggable\ConnectSkin;
use Awesomemotive\Rewardswp\Pluggable\PluginSilentUpgrader;
use Awesomemotive\Rewardswp\AntiSpam\Providers\HoneypotProvider;
use Awesomemotive\Rewardswp\RewardTypes\FixedDiscountRewardType;
use Awesomemotive\Rewardswp\RewardTypes\FreeProductRewardType;
use Awesomemotive\Rewardswp\RewardTypes\FreeShippingRewardType;
use Awesomemotive\Rewardswp\RewardTypes\PercentageDiscountRewardType;
use Awesomemotive\Rewardswp\RewardTypes\PointsRewardType;
use Awesomemotive\Rewardswp\Services\AntiSpamService;
use Awesomemotive\Rewardswp\Services\AssetsService;
use Awesomemotive\Rewardswp\Services\PointsService;
use Awesomemotive\Rewardswp\Services\RewardsService;
use Awesomemotive\Rewardswp\Services\TaskSchedulerService;
use Awesomemotive\Rewardswp\Controllers\TaskSchedulerController;
use Awesomemotive\Rewardswp\Tasks\UpdateExpiredRewardsTask;
use Awesomemotive\Rewardswp\Tasks\SyncNotificationsTask;
use Awesomemotive\Rewardswp\Views\Panel\Advocate\RewardsWidgetView;
use DI\Container;
use DI\DependencyException;
use DI\NotFoundException;
use function DI\autowire;
use function DI\factory;
use function DI\get;


/**
 * Base Definitions.
 *
 * @since 1.0.0
 */
class BaseDefinitions {
	/**
	 * Common database tables that must run on any version of the plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_base_database_tables(): array {
		return [
			MembersTable::class,
			ReferralsTable::class,
			RewardsTable::class,
			EventsTable::class,
			PointsTable::class,
			EmailsTable::class,
			NotificationsTable::class,
		];
	}

	/**
	 * Common database upgrade classes that must run on any version of the plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_base_plugin_upgrade_classes(): array {
		return [
			'lite' => [
				'1.0.1' => [
					AddTokenColumnToMembersTableUpgrade::class,
				],
			'1.1.0' => [
				CreatePointsTableUpgrade::class,
				AddPointsBalanceColumnToMemberTableUpgrade::class,
				CreateEmailsTableUpgrade::class,
				SeedDefaultEmailsUpgrade::class,
				AddContextColumnToEventsTableUpgrade::class,
				RenameAdvocateIdToMemberIdInEventsTableUpgrade::class,
				CreateNotificationsTableUpgrade::class,
			],
			'1.2.0' => [
				FlushRewriteRulesUpgrade::class,
			],
			'1.4.0' => [
				SeedBirthdayEmailUpgrade::class,
				UnscheduleLegacyTasksUpgrade::class,
			],
			'1.4.4' => [
				TranslatableEmailSubjectsUpgrade::class,
			],
		],
	];
	}

	/**
	 * Retrieve controllers that should be loaded globally always.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_global_controllers(): array {
		return [
			ApiController::class,
			PrivacyController::class,
		];
	}

	/**
	 * Common admin controllers that must run on any version of the plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_base_admin_controllers_classes(): array {
		return [
			ReferralsAdminController::class,
			MembersAdminController::class,
			RewardsAdminController::class,
			PointsAdminController::class,
			EmailsAdminController::class,
			SettingsAdminController::class,
			ToolsAdminController::class,
			SetupAdminController::class,
			LicenseController::class,
			DashboardAdminController::class,
			ReportsAdminController::class,
			UsersAdminController::class,
			NotificationsAdminController::class,
		];
	}

	/**
	 * Common controllers that must run on any version of the plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_base_main_controllers_classes(): array {
		return [
			TrackingController::class,
			IntegrationController::class,
			AdvocateController::class,
			ClaimController::class,
			PointsController::class,
			EventsController::class,
			OrderLinkingVerificationController::class,
		];
	}

	/**
	 * Common reward types that must run on any version of the plugin.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_base_reward_types_classes(): array {
		return [
			FixedDiscountRewardType::class,
			PercentageDiscountRewardType::class,
			FreeShippingRewardType::class,
			FreeProductRewardType::class,
			PointsRewardType::class,
		];
	}

	/**
	 * Get the API versions classes.
	 *
	 * This allows us to easily add new API versions in the future.
	 *
	 * @since 1.0.0
	 *
	 * @return string[]
	 */
	protected function get_api_versions_classes(): array {
		return [
			Routes::class,
		];
	}

	/**
	 * Get the anti-spam provider classes.
	 *
	 * These providers are injected into AntiSpamService.
	 * ProDefinitions can extend this array to add Pro-specific providers.
	 *
	 * @since 1.2.0
	 *
	 * @return string[]
	 */
	protected function get_base_antispam_providers_classes(): array {
		return [
			HoneypotProvider::class,
		];
	}

	/**
	 * Get the scheduled task classes.
	 *
	 * These task classes implement ScheduledTaskInterface and are dispatched
	 * by TaskSchedulerController based on their recurrence. ProDefinitions
	 * can extend this array to add Pro-specific tasks.
	 *
	 * @since 1.4.0
	 *
	 * @return string[]
	 */
	protected function get_base_scheduled_task_classes(): array {
		return [
			UpdateExpiredRewardsTask::class,
			SyncNotificationsTask::class,
		];
	}

	/**
	 * Retrieve the shared container definitions.
	 *
	 * @since 1.0.0
	 * @return array The array of shared definitions.
	 */
	protected function get_shared_definitions(): array {
		return [
			'plugin_upgrades'                    => $this->get_base_plugin_upgrade_classes(),
			'database_table_classes'             => $this->get_base_database_tables(),
			'global_controllers_classes'         => $this->get_global_controllers(),
			'admin_controllers_classes'          => $this->get_base_admin_controllers_classes(),
			'main_controllers_classes'           => $this->get_base_main_controllers_classes(),
			'reward_types_classes'               => $this->get_base_reward_types_classes(),
			'antispam_providers_classes'         => $this->get_base_antispam_providers_classes(),
			'scheduled_task_classes'             => $this->get_base_scheduled_task_classes(),
			'api_versions'                       => $this->get_api_versions_classes(),
			'plugin_info'                        => $this->get_plugin_info(),
			/*
			 * Automatically set the current site integration so it can be injected properly.
			 */
			IntegrationController::class         => factory( [ $this, 'set_site_integration' ] ),
			IntegrationInterface::class          => factory( [ $this, 'set_site_integration' ] ),
			AbstractIntegrationWithCoupon::class => factory( [ $this, 'set_site_integration' ] ),
			PluginSilentUpgrader::class          => factory(
				function () {
					return new PluginSilentUpgrader( new ConnectSkin() );
				}
			),
			/*
			 * Determine which advocate panel content view to load.
			 * Uses MemberCenterView for frontend, AdvocatePanelView for admin.
			 */
			'advocate_panel_content_view'        => factory( [ $this, 'set_advocate_panel_content_view' ] ),
			/*
			 * Automatically resolve AssetsService parameters when called via auto-wiring.
			 *
			 * Note that this service will automatically point to the same folder whenever it's called via container
			 * or auto-wiring, but you can bypass and point to a different folder if you wish by calling the class
			 * and passing your own parameters.
			 */
			AssetsService::class                 => autowire()->constructor(
				AM_REWARDSWP_PLUGIN_URL . 'dist/',
				AM_REWARDSWP_VERSION
			),
			/*
			 * Explicitly define AdvocateController wiring to use the factory for the panel view.
			 */
			AdvocateController::class            => autowire()
				->constructorParameter( 'advocate_panel_content_view', get( 'advocate_panel_content_view' ) ),
			/*
			 * Configure AntiSpamService to receive the array of provider instances.
			 * Providers are resolved from the 'antispam_providers_classes' array.
			 */
			AntiSpamService::class               => factory( [ $this, 'create_antispam_service' ] ),
			/*
			 * Configure TaskSchedulerController to receive resolved scheduled task instances.
			 * Tasks are resolved from the 'scheduled_task_classes' array.
			 */
			TaskSchedulerController::class       => factory( [ $this, 'create_task_scheduler_controller' ] ),
		];
	}

	/**
	 * Automatically set the site's integration based on the site settings.
	 *
	 * The current integration will always be set automatically, based on the site's settings.
	 * If no integration is set, a null integration class will be used as a placeholder.
	 *
	 * @since 1.0.0
	 *
	 * @param Container $container
	 *
	 * @return EasyDigitalDownloadsIntegration|UnknownIntegration|WooCommerceIntegration|mixed
	 *
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function set_site_integration( Container $container ) {
		$integration_factory = $container->get( IntegrationsFactory::class );

		$woocommerce_integration = $integration_factory->get_integration( WooCommerceIntegration::class );

		if (
			is_subclass_of( $woocommerce_integration, IntegrationInterface::class ) &&
			$woocommerce_integration->is_active()
		) {
			return $woocommerce_integration;
		}

		$edd_integration = $integration_factory->get_integration( EasyDigitalDownloadsIntegration::class );

		if (
			is_subclass_of( $edd_integration, IntegrationInterface::class ) &&
			$edd_integration->is_active()
		) {
			return $edd_integration;
		}

		return $integration_factory->get_integration( UnknownIntegration::class );
	}

	/**
	 * Factory method to determine which advocate panel content view to use.
	 *
	 * Always uses RewardsWidgetView (the new rewards widget).
	 *
	 * @since 1.1.0
	 *
	 * @param Container $container The DI container.
	 *
	 * @return AbstractView The appropriate panel content view instance (will have get_contents method).
	 *
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function set_advocate_panel_content_view( Container $container ): AbstractView {
		// Always use the new RewardsWidgetView (rewards widget).
		return $container->get( RewardsWidgetView::class );
	}

	/**
	 * Factory method to create the AntiSpamService with resolved providers.
	 *
	 * Resolves each provider class from the 'antispam_providers_classes' array
	 * and injects them into the AntiSpamService.
	 *
	 * @since 1.2.0
	 *
	 * @param Container $container The DI container.
	 *
	 * @return AntiSpamService The configured anti-spam service.
	 *
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function create_antispam_service( Container $container ): AntiSpamService {
		$provider_classes = $container->get( 'antispam_providers_classes' );
		$providers        = [];

		foreach ( $provider_classes as $provider_class ) {
			$providers[] = $container->get( $provider_class );
		}

		return new AntiSpamService( $providers );
	}

	/**
	 * Factory method to create the TaskSchedulerController with resolved tasks.
	 *
	 * Resolves each task class from the 'scheduled_task_classes' array
	 * and injects them into the TaskSchedulerController.
	 *
	 * @since 1.4.0
	 *
	 * @param Container $container The DI container.
	 *
	 * @return TaskSchedulerController The configured task scheduler controller.
	 *
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function create_task_scheduler_controller( Container $container ): TaskSchedulerController {
		$task_classes = $container->get( 'scheduled_task_classes' );
		$tasks        = [];

		foreach ( $task_classes as $task_class ) {
			$tasks[] = $container->get( $task_class );
		}

		return new TaskSchedulerController(
			$container->get( TaskSchedulerService::class ),
			$tasks
		);
	}

	/**
	 * Returns some plugin info like version and paths.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_plugin_info(): array {
		return [
			'version'  => AM_REWARDSWP_VERSION,
			'dir'      => AM_REWARDSWP_PLUGIN_DIR,
			'url'      => AM_REWARDSWP_PLUGIN_URL,
			'basename' => AM_REWARDSWP_PLUGIN_BASE_NAME,
			'file'     => AM_REWARDSWP_PLUGIN_FILE,
		];
	}
}
