<?php

namespace Ugcify\License;

defined('ABSPATH') || exit;

/**
 * Handles license activation, deactivation, and remote validation via EDD Software Licensing.
 *
 * Uses the Easy Digital Downloads (EDD) Software Licensing API. The plugin
 * first tries the primary EDD store URL, falling back to the secondary URL
 * when the primary is unavailable.
 *
 * WP Options managed here:
 *   ugcify_pro_license_key    — raw key string
 *   ugcify_pro_license_status — 'active' | 'deactivate'
 *   ugcify_pro_is_premium     — 'yes' | 'no'
 *   ugcify_pro_license_data   — array (key, last_check, start_date, end_date, plan, status)
 *   ugcify_pro_license_plan   — plan slug returned by the license server
 *
 * @since 1.0.0
 * @package Ugcify\License
 */
class Licensing
{

	/**
	 * Singleton instance.
	 *
	 * @var Licensing
	 */
	private static $instance;

	/**
	 * Get the singleton instance.
	 *
	 * @return Licensing
	 */
	public static function get_instance()
	{
		if (null === self::$instance) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Whether a local development license override is enabled.
	 *
	 * Opt-in via wp-config.php using:
	 * - UGCIFY_DEV_LICENSE_OVERRIDE => true
	 * - UGCIFY_DEV_LICENSE_PLAN     => 'growth' | 'pro' | 'business'
	 *
	 * @return bool
	 */
	public static function is_dev_override_enabled()
	{
		if (! \defined('UGCIFY_DEV_LICENSE_OVERRIDE')) {
			return false;
		}

		$value = \constant('UGCIFY_DEV_LICENSE_OVERRIDE');

		if (\is_bool($value)) {
			return $value;
		}

		if (! \is_scalar($value)) {
			return false;
		}

		return \in_array(\strtolower((string) $value), array('1', 'true', 'yes', 'on'), true);
	}

	/**
	 * Get the local override plan.
	 *
	 * @return string
	 */
	private static function get_dev_override_plan()
	{
		$plan = \defined('UGCIFY_DEV_LICENSE_PLAN')
			? \sanitize_key((string) \constant('UGCIFY_DEV_LICENSE_PLAN'))
			: 'business';

		return \in_array($plan, array('growth', 'pro', 'business'), true) ? $plan : 'business';
	}

	/**
	 * Resolve a local development license key to its simulated plan.
	 *
	 * @param string $license_key Raw license key.
	 * @return string Empty string when the key is not recognised.
	 */
	private static function get_local_plan_for_key($license_key)
	{
		$key_plan_map = array(
			'8zM2vLpQ9n5Xk1RwT7b6Yj4GfD3sHv' => 'growth',
			'YR27aZqIBXfsmmMNd8uBuKcnRc5I6H' => 'pro',
			'6y3MFl2DihJU5avqQmYrbpnjDwZgzs' => 'business',
		);

		return isset($key_plan_map[$license_key]) ? $key_plan_map[$license_key] : '';
	}

	/**
	 * Whether the current WordPress site is running in a local/development environment.
	 *
	 * @return bool
	 */
	private static function is_local_environment()
	{
		return false;
		if (self::is_beta_environment()) {
			return true;
		}

		$environment = \defined('WP_ENVIRONMENT_TYPE')
			? \constant('WP_ENVIRONMENT_TYPE')
			: (\function_exists('wp_get_environment_type') ? \wp_get_environment_type() : 'production');

		if ('local' === $environment) {
			return true;
		}

		$site_url = \function_exists('home_url') ? \home_url() : '';
		$host     = $site_url ? (string) \wp_parse_url($site_url, PHP_URL_HOST) : '';

		if (empty($host) && \defined('UGCIFY_INSTANCE')) {
			$host = (string) \wp_parse_url('http://' . \constant('UGCIFY_INSTANCE'), PHP_URL_HOST);
		}

		if (empty($host)) {
			return false;
		}

		$host = \strtolower($host);

		if (\in_array($host, array('localhost', '127.0.0.1', '::1'), true)) {
			return true;
		}

		foreach (array('.local', '.test', '.localhost', '.invalid') as $suffix) {
			if (\strlen($host) >= \strlen($suffix) && 0 === \substr_compare($host, $suffix, -\strlen($suffix))) {
				return true;
			}
		}

		if (1 === \preg_match('/^(10\.|127\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/', $host)) {
			return true;
		}

		return false;
	}

	/**
	 * Whether the current environment is the beta testing environment.
	 *
	 * @return bool
	 */
	private static function is_beta_environment()
	{
		return false;
	}

	/**
	 * Make an EDD Software Licensing API request.
	 *
	 * Tries the primary EDD store first; falls back to the backup URL.
	 *
	 * @param string $action     EDD action (activate_license, deactivate_license, check_license).
	 * @param string $license_key Raw license key.
	 * @return object|null Decoded JSON response object, or null on failure.
	 */
	private function edd_license_api($action, $license_key)
	{
		$api_params = array(
			'edd_action' => $action,
			'license'    => $license_key,
			'item_id'    => UGCIFY_EDD_ITEM_ID,
			'url'        => home_url(),
			'source'     => 'rextheme',
		);

		$request_args = array(
			'timeout'   => 15,
			'sslverify' => false,
		);

		$primary_response = wp_remote_get(
			UGCIFY_EDD_STORE_URL . '?' . http_build_query($api_params),
			$request_args
		);

		if (! is_wp_error($primary_response) && 200 === (int) wp_remote_retrieve_response_code($primary_response)) {
			$data = json_decode(wp_remote_retrieve_body($primary_response));
			if (null !== $data) {
				return $data;
			}
		}

		$backup_response = wp_remote_get(
			UGCIFY_EDD_LICENSE_URL . '?' . http_build_query($api_params),
			$request_args
		);

		if (! is_wp_error($backup_response) && 200 === (int) wp_remote_retrieve_response_code($backup_response)) {
			$data = json_decode(wp_remote_retrieve_body($backup_response));
			if (null !== $data) {
				return $data;
			}
		}

		return null;
	}

	/**
	 * Determine the ugcify plan tier from an EDD license API response.
	 *
	 * Checks price_id first (filterable map), then falls back to item_name parsing.
	 * Default plan is 'growth' when no signal is found.
	 *
	 * @param object $license_data EDD API response object.
	 * @return string Plan slug: 'growth' | 'pro' | 'business'.
	 */
	private function determine_plan_from_edd_response($license_data)
	{
		$price_id_map = apply_filters(
			'ugcify_edd_price_id_plan_map',
			array(
				'1' => 'growth-lifetime',
				'2' => 'pro-lifetime',
				'3' => 'business-lifetime',
				'4' => 'growth-annual',
				'5' => 'pro-annual',
				'6' => 'business-annual',
				'7' => 'business-annual',
				'8' => 'business-lifetime',
			)
		);

		if (isset($license_data->price_id) && isset($price_id_map[(string) $license_data->price_id])) {
			return $price_id_map[(string) $license_data->price_id];
		}

		if (! empty($license_data->item_name)) {
			$name = strtolower((string) $license_data->item_name);
			if (false !== strpos($name, 'business')) {
				return 'business';
			}
			if (false !== strpos($name, 'pro')) {
				return 'pro';
			}
			if (false !== strpos($name, 'growth')) {
				return 'growth';
			}
		}

		return 'growth';
	}

	/**
	 * Return a user-facing error message for an EDD error code.
	 *
	 * @param string $error_code EDD error code from the license response.
	 * @return string
	 */
	private function get_edd_error_message($error_code)
	{
		switch ($error_code) {
			case 'expired':
				return __('Your license key has expired. Please renew your license.', 'ugcify');
			case 'revoked':
				return __('Your license key has been disabled. Please contact support.', 'ugcify');
			case 'missing':
				return __('Invalid license key. Please check your license and try again.', 'ugcify');
			case 'invalid':
			case 'site_inactive':
				return __('Your license is not active for this URL.', 'ugcify');
			case 'item_name_mismatch':
				return __('This license key does not belong to UGCify. Please use the correct license key.', 'ugcify');
			case 'no_activations_left':
				return __('Your license key has reached its activation limit.', 'ugcify');
			case 'license_not_activable':
				return __('This is a bundle license key. Please use your individual product license key.', 'ugcify');
			default:
				return __('License activation failed. Please try again or contact support.', 'ugcify');
		}
	}

	/**
	 * Persist a simulated license state for local development.
	 *
	 * @param string $operation_name 'activate' | 'deactivate'
	 * @param string $license_key    Raw license key.
	 * @return array
	 */
	private function process_local_license_operation($operation_name, $license_key)
	{
		if ('deactivate' === $operation_name) {
			$license_data = $this->clear_license_data();
			do_action('ugcify_pro_license_deactivated', $license_data);

			return array(
				'success' => true,
				'message' => __('Local development license deactivated.', 'ugcify'),
				'status'  => 'deactivate',
				'data'    => $license_data,
			);
		}

		if (self::is_local_environment()) {
			$plan = self::get_local_plan_for_key($license_key);
			if (empty($plan)) {
				return array(
					'success' => false,
					'message' => __('Invalid local development license key.', 'ugcify'),
					'status'  => 'deactivate',
					'data'    => array(),
				);
			}
		}


		$license_data = array(
			'key'             => $license_key,
			'last_check'      => time(),
			'start_date'      => gmdate('Y-m-d'),
			'end_date'        => '',
			'plan'            => $plan,
			'status'          => 'active',
			'is_dev_override' => true,
		);

		update_option('ugcify_pro_license_key', $license_key);
		update_option('ugcify_pro_license_data', $license_data);
		update_option('ugcify_pro_is_premium', 'yes');
		update_option('ugcify_pro_license_status', 'active');
		update_option('ugcify_pro_license_plan', $license_data['plan']);

		do_action('ugcify_pro_license_activated', $license_data);

		return array(
			'success' => true,
			'message' => sprintf(
				/* translators: %s: simulated plan slug. */
				__('Local development license activated for the %s tier without contacting the license server.', 'ugcify'),
				$plan
			),
			'status'  => 'active',
			'data'    => $license_data,
		);
	}

	/**
	 * Get the effective license state from stored options.
	 *
	 * @return array
	 */
	private static function get_effective_license_state()
	{
		if (self::is_dev_override_enabled()) {
			$license_key = \defined('UGCIFY_DEV_LICENSE_KEY')
				? \sanitize_text_field((string) \constant('UGCIFY_DEV_LICENSE_KEY'))
				: '';

			return array(
				'key'        => $license_key,
				'status'     => 'active',
				'is_premium' => true,
				'data'       => array(
					'key'             => $license_key,
					'last_check'      => \time(),
					'start_date'      => '',
					'end_date'        => '',
					'plan'            => self::get_dev_override_plan(),
					'status'          => 'active',
					'is_dev_override' => true,
				),
			);
		}

		return array(
			'key'        => \get_option('ugcify_pro_license_key', ''),
			'status'     => \get_option('ugcify_pro_license_status', 'deactivate'),
			'is_premium' => 'yes' === \get_option('ugcify_pro_is_premium', 'no'),
			'data'       => \get_option('ugcify_pro_license_data', array()),
		);
	}

	/**
	 * Get the effective stored license status.
	 *
	 * @return string
	 */
	public static function get_license_status()
	{
		$state = self::get_effective_license_state();
		return $state['status'];
	}

	/**
	 * Whether the effective license state is premium.
	 *
	 * @return bool
	 */
	public static function is_premium()
	{
		$state = self::get_effective_license_state();
		return $state['is_premium'];
	}

	/**
	 * Whether the installed plugin files are the pro build.
	 *
	 * @return bool
	 */
	public static function is_pro_build_installed()
	{
		$is_pro_build = \file_exists(UGCIFY_PATH . 'includes/License/Updater.php');

		return (bool) \apply_filters('ugcify_is_pro_build_installed', $is_pro_build);
	}

	/**
	 * Whether an active license still needs the pro build installed.
	 *
	 * @return bool
	 */
	public static function needs_pro_install()
	{
		return (bool) \get_option('ugcify_needs_pro_install', false)
			&& self::is_license_valid()
			&& ! self::is_pro_build_installed();
	}

	/**
	 * Clear any pending pro-install state.
	 *
	 * @return void
	 */
	public static function clear_pro_install_flags()
	{
		\delete_option('ugcify_needs_pro_install');
		\delete_option('ugcify_pro_download_url');
		\delete_transient('ugcify_force_edd_install');
	}

	// -------------------------------------------------------------------------
	// Static read helpers
	// -------------------------------------------------------------------------

	/**
	 * Whether a valid, active license is present.
	 *
	 * Checks stored option values set during activation or the weekly cron revalidation.
	 *
	 * @return bool
	 */
	public static function is_license_valid()
	{
		if (self::is_dev_override_enabled()) {
			return true;
		}

		if (self::is_local_environment()) {
			$stored_key  = \get_option('ugcify_pro_license_key', '');
			$stored_plan = self::get_local_plan_for_key($stored_key);
			if (! empty($stored_plan) && 'active' === \get_option('ugcify_pro_license_status', '')) {
				return true;
			}
		}

		return 'active' === \get_option('ugcify_pro_license_status', '')
			&& 'yes' === \get_option('ugcify_pro_is_premium', 'no');
	}

	/**
	 * Get stored license data array.
	 *
	 * @return array
	 */
	public static function get_license_data()
	{
		$state = self::get_effective_license_state();
		return $state['data'];
	}

	/**
	 * Get stored raw license key.
	 *
	 * @return string
	 */
	public static function get_license_key()
	{
		$state = self::get_effective_license_state();
		return $state['key'];
	}

	// -------------------------------------------------------------------------
	// Remote operations
	// -------------------------------------------------------------------------

	/**
	 * Perform a remote status-check against the EDD license server.
	 *
	 * @return bool True when the server reports the license as valid.
	 */
	public function remote_license_check()
	{
		if (self::is_dev_override_enabled()) {
			return true;
		}

		if (self::is_local_environment()) {
			return self::is_license_valid();
		}

		$license_key = self::get_license_key();
		if (empty($license_key)) {
			return false;
		}

		$license_data = $this->edd_license_api('check_license', $license_key);

		if (null === $license_data) {
			return false;
		}

		return isset($license_data->license) && 'valid' === $license_data->license;
	}

	/**
	 * Perform an activate or deactivate operation against the EDD license server
	 * and persist the resulting state in wp_options.
	 *
	 * @param string $operation_name 'activate' | 'deactivate'
	 * @param string $license_key    Raw license key (required for activation).
	 * @return array {
	 *     @type bool   $success
	 *     @type string $message
	 *     @type string $status  'active' | 'deactivate'
	 *     @type array  $data    License data array (may be empty on failure).
	 * }
	 */
	public function process_license_operation($operation_name = 'activate', $license_key = '')
	{
		if (empty($license_key) && 'activate' === $operation_name) {
			return array(
				'success' => false,
				'message' => __('License key is required.', 'ugcify'),
			);
		}

		if (self::is_local_environment()) {
			return $this->process_local_license_operation($operation_name, $license_key);
		}

		if (self::is_dev_override_enabled()) {
			$message = 'deactivate' === $operation_name
				? __('Development license override is enabled. Disable UGCIFY_DEV_LICENSE_OVERRIDE to deactivate locally.', 'ugcify')
				: __('Development license override is enabled for this local environment.', 'ugcify');

			return array(
				'success' => true,
				'message' => $message,
				'status'  => self::get_license_status(),
				'data'    => self::get_license_data(),
			);
		}

		// If the key contains asterisks it is the masked display form — resolve it.
		if (strpos($license_key, '*') !== false) {
			$stored_license  = get_option('ugcify_pro_license_key', '');
			$decrypt_license = ProFunctions::decrypt_key($license_key);
			if ($stored_license === $decrypt_license) {
				$license_key = $stored_license;
			}
		}

		$edd_action = 'activate' === $operation_name ? 'activate_license' : 'deactivate_license';
		$license_data = $this->edd_license_api($edd_action, $license_key);

		if (null === $license_data) {
			return array(
				'success' => false,
				'message' => __('An error occurred communicating with the license server. Please try again.', 'ugcify'),
			);
		}

		// ── Deactivation ───────────────────────────────────────────────────────
		if ('deactivate' === $operation_name) {
			$stored_data = $this->clear_license_data();
			do_action('ugcify_pro_license_deactivated', $stored_data);

			$message = (is_object($license_data) && isset($license_data->success) && $license_data->success)
				? __('License deactivated successfully.', 'ugcify')
				: __('License has been deactivated.', 'ugcify');

			return array(
				'success' => true,
				'message' => $message,
				'status'  => 'deactivate',
				'data'    => $stored_data,
			);
		}

		// ── Activation ─────────────────────────────────────────────────────────
		if (false === $license_data->success) {
			$error_code  = isset($license_data->error) ? (string) $license_data->error : '';
			$stored_data = $this->clear_license_data();
			do_action('ugcify_pro_license_deactivated', $stored_data);

			return array(
				'success' => false,
				'message' => $this->get_edd_error_message($error_code),
				'status'  => 'deactivate',
				'data'    => array(),
			);
		}

		if (isset($license_data->license) && 'valid' === $license_data->license) {
			$plan     = $this->determine_plan_from_edd_response($license_data);
			$end_date = (isset($license_data->expires) && 'lifetime' !== $license_data->expires)
				? $license_data->expires
				: '';

			$stored_data = array(
				'key'        => $license_key,
				'last_check' => time(),
				'start_date' => '',
				'end_date'   => $end_date,
				'plan'       => $plan,
				'status'     => 'active',
			);

			// Capture download_link / package from the EDD activation response if the
			// store's custom enhancer plugin injected them. This lets the Updater skip
			// the separate get_version API call and offer "Install Pro Build" immediately.
			$activation_download_url = '';
			if (! empty($license_data->download_link)) {
				$activation_download_url      = esc_url_raw($license_data->download_link);
				$stored_data['download_link'] = $activation_download_url;
			} elseif (! empty($license_data->package)) {
				$activation_download_url      = esc_url_raw($license_data->package);
				$stored_data['download_link'] = $activation_download_url;
			}

			update_option('ugcify_pro_license_key', $license_key);
			update_option('ugcify_pro_license_data', $stored_data);
			update_option('ugcify_pro_is_premium', 'yes');
			update_option('ugcify_pro_license_status', 'active');
			update_option('ugcify_pro_license_plan', $plan);

			if (self::is_pro_build_installed()) {
				self::clear_pro_install_flags();
			} else {
				// Persist the "needs pro install" flag so the undismissable notice shows on
				// every admin page until the pro ZIP is actually downloaded and installed.
				// Updater::on_upgrade_complete() clears this option after a successful install.
				\update_option('ugcify_needs_pro_install', true);

				// Store the download URL in its own option so the direct installer can
				// replace the free build with the pro ZIP after license activation.
				if (! empty($activation_download_url)) {
					\update_option('ugcify_pro_download_url', $activation_download_url);
				} else {
					\delete_option('ugcify_pro_download_url');
				}
			}

			do_action('ugcify_pro_license_activated', $stored_data);

			return array(
				'success' => true,
				'message' => __('License activated successfully.', 'ugcify'),
				'status'  => 'active',
				'data'    => $stored_data,
			);
		}

		// License status is not 'valid' and success is not false — unexpected state.
		$stored_data = $this->clear_license_data();
		do_action('ugcify_pro_license_deactivated', $stored_data);

		return array(
			'success' => false,
			'message' => __('License activation failed. Please try again or contact support.', 'ugcify'),
			'status'  => 'deactivate',
			'data'    => array(),
		);
	}

	// -------------------------------------------------------------------------
	// Cron revalidation
	// -------------------------------------------------------------------------

	/**
	 * Perform a remote license check and update stored state.
	 *
	 * Called weekly by LicenseCron. On success the last_check timestamp is updated.
	 * On failure the license status is reset to deactivate.
	 *
	 * @return void
	 */
	public static function run_remote_revalidation(): void
	{
		if (self::is_dev_override_enabled()) {
			return;
		}

		if (self::is_local_environment()) {
			return;
		}

		$license_key = self::get_license_key();

		if (empty($license_key)) {
			update_option('ugcify_pro_is_premium', 'no');
			update_option('ugcify_pro_license_status', 'deactivate');
			return;
		}

		$is_valid = (new self())->remote_license_check();

		if ($is_valid) {
			$license_data               = self::get_license_data();
			$license_data['last_check'] = time();
			update_option('ugcify_pro_license_data', $license_data);
		} else {
			update_option('ugcify_pro_is_premium', 'no');
			update_option('ugcify_pro_license_status', 'deactivate');
			do_action('ugcify_pro_license_deactivated', self::get_license_data());
		}
	}

	// -------------------------------------------------------------------------
	// Internal helpers
	// -------------------------------------------------------------------------

	/**
	 * Reset all license-related WP options to their empty/inactive defaults.
	 *
	 * @return array Empty license data array.
	 */
	private function clear_license_data()
	{
		$license_data = array(
			'key'        => '',
			'last_check' => time(),
			'start_date' => '',
			'end_date'   => '',
			'plan'       => '',
			'status'     => 'deactivate',
		);

		update_option('ugcify_pro_license_key', '');
		update_option('ugcify_pro_license_data', $license_data);
		update_option('ugcify_pro_is_premium', 'no');
		update_option('ugcify_pro_license_status', 'deactivate');
		update_option('ugcify_pro_license_plan', '');
		self::clear_pro_install_flags();

		return $license_data;
	}
}
