<?php
/**
 * License controller for managing plugin license.
 *
 * @package Awesomemotive\Rewardswp\Controllers\Admin
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\Controllers\Admin;

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

use Awesomemotive\Rewardswp\Interfaces\ControllerInterface;
use Awesomemotive\Rewardswp\Pluggable\PluginSilentUpgrader;
use Awesomemotive\Rewardswp\Services\LicenseManagerService;
use WP_Error;
use function Awesomemotive\Rewardswp\Helpers\format_date_from_timestamp;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_lite_plan;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_pro_plan;
use function Awesomemotive\Rewardswp\Helpers\is_rewardswp_pro_installed_but_inactive;

class LicenseController implements ControllerInterface {
	/**
	 * AJAX nonce for license management.
	 *
	 * @since 1.0.0
	 */
	const AJAX_NONCE = 'rewardswp_license_management_nonce';

	/**
	 * AJAX nonce for activating the Pro plugin.
	 *
	 * @since 1.1.0
	 */
	const AJAX_ACTIVATE_PRO_NONCE = 'rewardswp_activate_pro_nonce';

	/**
	 * RewardsWP Pro plugin basename.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const PRO_PLUGIN = 'rewardswp-pro/rewardswp.php';

	/**
	 * RewardsWP Lite plugin basename.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const LITE_PLUGIN = 'rewardswp/rewardswp.php';

	/**
	 * License manager service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var LicenseManagerService
	 */
	private LicenseManagerService $license_manager_service;

	/**
	 * Silent upgrader/installer instance.
	 *
	 * @since 1.0.0
	 *
	 * @var PluginSilentUpgrader
	 */
	private PluginSilentUpgrader $installer;

	/**
	 * Construct.
	 *
	 * @since 1.0.0
	 *
	 * @param LicenseManagerService $license_manager_service
	 * @param PluginSilentUpgrader  $installer
	 */
	public function __construct( LicenseManagerService $license_manager_service, PluginSilentUpgrader $installer ) {
		$this->license_manager_service = $license_manager_service;
		$this->installer               = $installer;
	}

	/**
	 * Init.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_ajax_rewardswp_connect_license', [ $this, 'ajax_connect_license' ] );
		add_action( 'wp_ajax_rewardswp_disconnect_license', [ $this, 'ajax_disconnect_license' ] );
		add_action( 'wp_ajax_rewardswp_activate_pro_plugin', [ $this, 'ajax_activate_pro_plugin' ] );
		add_action( 'wp_ajax_nopriv_rewardswp_connect_process', [ $this, 'ajax_handle_upgrade' ] );
	}

	/**
	 * License connect ajax handler.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function ajax_connect_license(): void {
		check_ajax_referer( self::AJAX_NONCE );

		$license_key   = sanitize_text_field( filter_input( INPUT_POST, 'license_key' ) );
		$force_refresh = absint( filter_input( INPUT_POST, 'force_refresh' ) );

		if ( $force_refresh ) {
			$connection = $this->license_manager_service->deactivate_license();

			if ( is_wp_error( $connection ) ) {
				wp_send_json_error(
					[
						'message' => esc_html__( 'Failed attempting to refresh license data.', 'rewardswp' ),
					]
				);

				wp_die();
			}
		}

		// If Lite is currently active, check if Pro is installed but inactive.
		if ( is_rewardswp_lite_plan() ) {
			// Try activating the license silently first.
			$license_data = $this->license_manager_service->activate_license( $license_key );

			if ( is_rewardswp_pro_installed_but_inactive() ) {
				if ( ! is_wp_error( $license_data ) && 'valid' === $license_data['status'] ) {
					// Pro is installed, inactive, but the license key is valid. Prompt user to activate Pro.
					wp_send_json_success(
						array_merge(
							$license_data,
							[
								'status'            => 'pro_installed_inactive',
								'expires_formatted' => format_date_from_timestamp( $license_data['expires'] ?? 0 ),
							]
						)
					);
					wp_die();
				} // If license invalid or error, fall through to upgrade URL.
			}

			// If Pro not installed or license was invalid for installed Pro, redirect to upgrade.
			wp_send_json_success(
				[
					'upgrade'    => true,
					'upgradeUrl' => $this->license_manager_service->get_upgrade_site_url( $license_key ),
				]
			);

			wp_die();
		}

		// This part executes only if it's the Pro version already.
		$license_data = $this->license_manager_service->activate_license( $license_key );

		if ( is_wp_error( $license_data ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'Failed to activate the license.', 'rewardswp' ),
				]
			);

			wp_die();
		}

		$expires           = $license_data['expires'] ?? 0;
		$expires_timestamp = is_numeric( $expires ) ? (int) $expires : 0;

		wp_send_json_success(
			array_merge(
				$license_data,
				[
					'expires_formatted' => $expires_timestamp > 0
						? format_date_from_timestamp( $expires_timestamp )
						: '',
				]
			)
		);

		wp_die();
	}

	/**
	 * License disconnect ajax handler.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function ajax_disconnect_license(): void {
		check_ajax_referer( self::AJAX_NONCE );

		$connection = $this->license_manager_service->deactivate_license();

		if ( is_wp_error( $connection ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'Failed to activate the license.', 'rewardswp' ),
				]
			);

			wp_die();
		}

		wp_send_json_success();

		wp_die();
	}

	/**
	 * Activate Pro plugin ajax handler.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function ajax_activate_pro_plugin(): void {
		check_ajax_referer( self::AJAX_ACTIVATE_PRO_NONCE );

		if ( ! current_user_can( 'activate_plugins' ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'You do not have permission to activate plugins.', 'rewardswp' ),
				]
			);
			wp_die();
		}

		// Ensure the plugin file exists before trying to activate.
		$all_plugins = get_plugins();

		if ( ! isset( $all_plugins[ self::PRO_PLUGIN ] ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'RewardsWP Pro plugin file not found.', 'rewardswp' ),
				]
			);
			wp_die();
		}

		// Deactivate Lite plugin first.
		deactivate_plugins( self::LITE_PLUGIN );

		// Flush cache to help ensure activation state is recognized.
		wp_cache_flush();

		// Activate the Pro plugin.
		$result = activate_plugin( self::PRO_PLUGIN );

		if ( is_wp_error( $result ) ) {
			// Attempt to reactivate Lite if Pro activation failed.
			activate_plugin( self::LITE_PLUGIN, '', false, true );

			wp_send_json_error(
				[
					'message' => sprintf(
						/* translators: %s: Error message */
						esc_html__( 'Failed to activate RewardsWP Pro: %s', 'rewardswp' ),
						$result->get_error_message()
					),
				]
			);
			wp_die();
		}

		wp_send_json_success(
			[
				'message' => esc_html__( 'RewardsWP Pro activated successfully. Redirecting...', 'rewardswp' ),
			]
		);

		wp_die();
	}

	/**
	 * Handle upgrade from Lite to Pro.
	 *
	 * @since 1.0.0
	 */
	public function ajax_handle_upgrade() {

		$error = esc_html__( 'There was an error while installing an upgrade. Please download the plugin from rewardswp.com and install it manually.', 'rewardswp' );

		// Verify params present (oth & download link).
		$post_oth = ! empty( $_REQUEST['oth'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['oth'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
		$post_url = ! empty( $_REQUEST['file'] ) ? esc_url_raw( wp_unslash( $_REQUEST['file'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification

		if ( empty( $post_oth ) || empty( $post_url ) ) {
			wp_send_json_error( $error );
		}

		// Verify oth.
		$oth = get_option( 'rewardswp_connect_token' );

		if ( empty( $oth ) ) {
			wp_send_json_error( $error );
		}

		if ( hash_hmac( 'sha512', $oth, wp_salt() ) !== $post_oth ) {
			wp_send_json_error( $error );
		}

		// Delete so cannot replay.
		delete_option( 'rewardswp_connect_token' );

		// Set the current screen to avoid undefined notices.
		set_current_screen( 'rewardswp_page_rewardswp-settings' );

		// Prepare variables.
		$url = esc_url_raw(
			add_query_arg(
				[ 'page' => 'rewardswp-settings' ],
				admin_url( 'admin.php' )
			)
		);

		// Verify pro not activated.
		if ( is_rewardswp_pro_plan() ) {
			wp_send_json_success( esc_html__( 'Plugin installed & activated.', 'rewardswp' ) );
		}

		// Verify pro not installed.
		$active = activate_plugin( self::PRO_PLUGIN, $url, false, true );

		if ( ! is_wp_error( $active ) ) {
			$plugin = plugin_basename( AM_REWARDSWP_PLUGIN_FILE );

			deactivate_plugins( $plugin );

			do_action( 'rewardswp_plugin_deactivated', $plugin );

			wp_send_json_success( esc_html__( 'Plugin installed & activated.', 'rewardswp' ) );
		}

		$creds = request_filesystem_credentials( $url, '', false, false );

		// Check for file system permissions.
		if ( $creds === false || ! WP_Filesystem( $creds ) ) {
			wp_send_json_error(
				esc_html__( 'There was an error while installing an upgrade. Please check file system permissions and try again. Also, you can download the plugin from rewardswp.com and install it manually.', 'rewardswp' )
			);
		}

		/*
		 * We do not need any extra credentials if we have gotten this far, so let's install the plugin.
		 */

		// Do not allow WordPress to search/download translations, as this will break JS output.
		remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );

		// Check the license key.
		$license_key = get_option( 'rewardswp_connect', false );

		// We don't need to store the key anymore.
		delete_option( 'rewardswp_connect' );

		if ( empty( $license_key ) ) {
			wp_send_json_error(
				new WP_Error(
					'403',
					esc_html__( 'No key provided.', 'rewardswp' )
				)
			);
		}

		$this->installer->install( $post_url );

		// Flush the cache and return the newly installed plugin basename.
		wp_cache_flush();

		$plugin_basename = $this->installer->plugin_info();

		if ( $plugin_basename ) {

			// Deactivate the lite version first.
			$plugin = plugin_basename( AM_REWARDSWP_PLUGIN_FILE );

			deactivate_plugins( $plugin );

			do_action( 'rewardswp_plugin_deactivated', $plugin );

			// Activate the plugin silently.
			$activated = activate_plugin( $plugin_basename, '', false, true );

			if ( ! is_wp_error( $activated ) ) {
				add_option( 'rewardswp_install', 1 );
				wp_send_json_success( esc_html__( 'Plugin installed & activated.', 'rewardswp' ) );
			} else {
				// Reactivate the lite plugin if pro activation failed.
				activate_plugin( plugin_basename( AM_REWARDSWP_PLUGIN_FILE ), '', false, true );
				wp_send_json_error( esc_html__( 'Pro version installed but needs to be activated on the Plugins page inside your WordPress admin.', 'rewardswp' ) );
			}
		}

		wp_send_json_error( $error );
	}
}
