<?php
/**
 * License manager service for handling RewardsWP license state.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Services;

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

use Awesomemotive\Rewardswp\Settings\Options\General\LicenseOption;
use WP_Error;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_admin_url;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_plan_tier_by_price_id;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_plan_tier_names;
use function Awesomemotive\Rewardswp\Helpers\is_url;

class LicenseManagerService {
	/**
	 * The name of the product in our license server.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const PRODUCT_NAME = 'RewardsWP';

	/**
	 * The license server service.
	 *
	 * @since 1.0.0
	 *
	 * @var LicenseServerService
	 */
	private LicenseServerService $license_server_service;

	/**
	 * The license option object.
	 *
	 * @since 1.0.0
	 *
	 * @var LicenseOption
	 */
	private LicenseOption $license_option;

	/**
	 * The feature gating service.
	 *
	 * @since 1.0.0
	 *
	 * @var FeatureGatingService
	 */
	private FeatureGatingService $feature_gating_service;

	/**
	 * Store the license data stored in the Options.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private array $cached_license_data;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param LicenseServerService $license_server_service The license server service.
	 * @param LicenseOption        $license_option         The license option instance.
	 * @param FeatureGatingService $feature_gating_service The feature gating service.
	 */
	public function __construct( LicenseServerService $license_server_service, LicenseOption $license_option, FeatureGatingService $feature_gating_service ) {
		$this->license_server_service = $license_server_service;
		$this->license_option         = $license_option;
		$this->feature_gating_service = $feature_gating_service;
		$this->cached_license_data    = $this->license_option->get_value();
	}

	/**
	 * Retrieve the base Upgrade Site URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	private function get_upgrade_site_base_url(): string {
		if ( ! defined( 'AM_REWARDSWP_UPGRADE_SITE_URL' ) ) {
			define( 'AM_REWARDSWP_UPGRADE_SITE_URL', 'https://upgrade.rewardswp.com/' );
		}

		return is_url( AM_REWARDSWP_UPGRADE_SITE_URL )
			? AM_REWARDSWP_UPGRADE_SITE_URL
			: 'https://upgrade.rewardswp.com/';
	}

	/**
	 * Retrieve the Upgrade Site URL with params.
	 *
	 * @since 1.0.0
	 *
	 * @param string $license_key The license key.
	 *
	 * @return string The upgrade URL.
	 */
	public function get_upgrade_site_url( string $license_key ): string {
		$oth        = hash( 'sha512', wp_rand() );
		$hashed_oth = hash_hmac( 'sha512', $oth, wp_salt() );
		$endpoint   = admin_url( 'admin-ajax.php' );
		$redirect   = get_rewardswp_admin_url( 'rewardswp-settings' );

		update_option( 'rewardswp_connect_token', $oth );
		update_option( 'rewardswp_connect', $license_key );

		return add_query_arg(
			[
				'key'      => $license_key,
				'oth'      => $hashed_oth,
				'endpoint' => $endpoint,
				'version'  => AM_REWARDSWP_VERSION,
				'siteurl'  => admin_url(),
				'homeurl'  => network_home_url(),
				'redirect' => rawurldecode( base64_encode( $redirect ) ), // phpcs:ignore
			],
			$this->get_upgrade_site_base_url()
		);
	}

	/**
	 * Retrieve the current site's license key.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_license_key(): string {
		return $this->cached_license_data['key'];
	}

	/**
	 * Retrieve the current site's license status.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_license_status(): string {
		return $this->cached_license_data['status'];
	}

	/**
	 * Attempt to activate RewardsWP using the license key from the settings.
	 * It also updates the License option with the new status, which it can be any of the allowed statuses.
	 *
	 * @see LicenseServerService::LICENSE_STATUSES
	 *
	 * @since 1.0.0
	 *
	 * @param string|null $license_key The license key to activate, will use the saved license key if none is supplied.
	 *
	 * @return array|WP_Error Array with the license data or WP_Error in case the request fails.
	 */
	public function activate_license( ?string $license_key = null ) {
		$license_key = $license_key ?? $this->get_license_key();

		if ( empty( $license_key ) ) {
			return new WP_Error(
				'empty_license_key',
				esc_html__( 'No license key found', 'rewardswp' ),
			);
		}

		if (
			! $this->license_server_service->activate_product(
				self::PRODUCT_NAME,
				$license_key
			)
		) {
			return new WP_Error(
				$this->license_server_service->get_last_request_code(),
				$this->license_server_service->get_last_request_error(),
				$this->license_server_service->get_license_data()
			);
		}

		$license_data = $this->license_server_service->get_license_data();

		$price_id  = $license_data['price_id'] ?? 0;
		$expires   = $license_data['expires'] ?? '';
		$plan_tier = get_rewardswp_plan_tier_by_price_id( absint( $price_id ) );

		$license_data = array_merge(
			$this->cached_license_data,
			[
				'key'       => $license_key,
				'status'    => $license_data['license'] ?? '',
				'expires'   => $expires,
				'price_id'  => $price_id,
				'plan_name' => get_rewardswp_plan_tier_names()[ $plan_tier ] ?? '',
			]
		);

		// Update the License option with the new data.
		if ( 'valid' === $license_data['status'] ) {
			$this->license_option->update( $license_data );
			// Clear feature cache when license status changes.
			$this->feature_gating_service->clear_cache();
		}

		return $license_data;
	}

	/**
	 * Attempt to deactivate RewardsWP using the license key from the settings.
	 *
	 * @since 1.0.0
	 *
	 * @return array|WP_Error Array with the license data when successfully deactivated or WP_Error in case it fails.
	 */
	public function deactivate_license() {
		if ( empty( $this->get_license_key() ) ) {
			return new WP_Error(
				'empty_license_key',
				esc_html__( 'No license key found', 'rewardswp' ),
			);
		}

		if (
			! $this->license_server_service->deactivate_product(
				self::PRODUCT_NAME,
				$this->get_license_key()
			)
		) {
			return new WP_Error(
				$this->license_server_service->get_last_request_code(),
				$this->license_server_service->get_last_request_error(),
				$this->license_server_service->get_license_data()
			);
		}

		// Reset to the default values.
		$this->license_option->update( $this->license_option->get_default_value() );

		// Clear feature cache when license is deactivated.
		$this->feature_gating_service->clear_cache();

		return $this->license_server_service->get_license_data();
	}

	/**
	 * Check and update the license status.
	 *
	 * The intention of this method is to run periodically or in certain circumstances where the license
	 * must be validated before doing some action.
	 *
	 * Only updates the stored license status if the response contains a known valid status
	 * ('valid', 'expired', or 'invalid'). This prevents accidental license deactivation
	 * due to network issues or unexpected API responses.
	 *
	 * @since 1.0.0
	 *
	 * @return array|WP_Error Array with the license data containing the new status or WP_Error in case the request fails.
	 */
	public function check_license_status() {
		if ( empty( $this->get_license_key() ) ) {
			return [];
		}

		$status = $this->license_server_service->check_product_status(
			self::PRODUCT_NAME,
			$this->get_license_key()
		);

		if ( 'request-failed' === $status ) {
			return new WP_Error(
				$this->license_server_service->get_last_request_code(),
				$this->license_server_service->get_last_request_error(),
				$this->license_server_service->get_license_data()
			);
		}

		// Only update if we got a valid known status to prevent accidental deactivation.
		if ( ! in_array( $status, LicenseServerService::LICENSE_STATUSES, true ) ) {
			// Return current cached data without updating.
			return $this->cached_license_data;
		}

		$license_data = array_merge(
			$this->cached_license_data,
			[
				'status' => $status,
			]
		);

		// Update the License option with the new status if changed.
		if ( $status !== $this->get_license_status() ) {
			$this->license_option->update( $license_data );
			// Clear feature cache when license status changes.
			$this->feature_gating_service->clear_cache();
		}

		return $license_data;
	}
}
