<?php
/**
 * Readabler
 * Web accessibility for Your WordPress site.
 * Exclusively on https://1.envato.market/readabler
 *
 * @encoding        UTF-8
 * @version         2.0.12
 * @copyright       (C) 2018-2025 Merkulove ( https://merkulov.design/ ). All rights reserved.
 * @license         Envato License https://1.envato.market/KYbje
 * @contributors    Dmytro Merkulov (dmitry@merkulov.design)
 * @support         help@merkulov.design
 **/

namespace Merkulove\Readabler\Unity;

use WP_Error;

final class Activator
{

	/**
	 * Transient key for activation status.
	 * @param string $purchase_code
	 * @return string
	 */
	private static function transient_key(string $purchase_code = ''): string
	{
		return 'mdp_' . Plugin::get_slug() . '_pid_' . $purchase_code;
	}

    /**
     * Return Activation Status.
     * @return boolean True if activated.
     */
    public static function status(): bool
    {
	    // Not activated if plugin doesn't have Envato ID
	    if (!EnvatoItem::id()) {
		    return false;
	    }

        // Get actual purchase code
        $purchase_code = Plugin::get_purchase_code();
        if (empty($purchase_code)) {
            return false;
        }

	    // Get activation status from transient
	    $transient_activation = get_transient(self::transient_key($purchase_code));
	    if (!$transient_activation) {

		    // Migration
//		    $migrated_status = self::migrate_status($purchase_code);
//		    if ($migrated_status) {
//			    set_transient(self::transient_key($purchase_code), true, DAY_IN_SECONDS + HOUR_IN_SECONDS);
//			    return true;
//		    }

		    return false;

	    } else {

		    return (bool)$transient_activation;

	    }

    }

	/**
	 * Set activation status.
	 * @param string $purchase_code
	 * @param bool $status True if activated.
	 * @return void
	 */
	private static function set_status( string $purchase_code, bool $status ): void
	{
		if ($status) {
			set_transient(self::transient_key($purchase_code), true, DAY_IN_SECONDS + HOUR_IN_SECONDS);
			self::set_timestamp($purchase_code);
		}
	}

	/**
	 * Set status timestamp.
	 * @param ?string $purchase_code
	 * @return void
	 */
	public static function set_timestamp( ?string $purchase_code = null ): void
	{
		// Get actual purchase code
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_purchase_code();
		}

		// Fallback for trial activations
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_slug() . '_trial';
		}

		update_option( self::transient_key($purchase_code) . '_timestamp', time() );
	}

	/**
	 * Get status timestamp.
	 * @param ?string $purchase_code
	 * @return ?int
	 */
	public static function get_timestamp( ?string $purchase_code = null ): ?int{

		// Get actual purchase code
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_purchase_code();
		}

		// Fallback for trial activations
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_slug() . '_trial';
		}

		return get_option( self::transient_key( $purchase_code ) . '_timestamp' );
	}

	/**
	 * Clear status timestamp.
	 * @param ?string $purchase_code
	 * @return void
	 */
	public static function clear_timestamp( ?string $purchase_code = null ): void
	{
		// Get actual purchase code
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_purchase_code();
		}

		// Fallback for trial activations
		if ( empty( $purchase_code ) ) {
			$purchase_code = Plugin::get_slug() . '_trial';
		}

		delete_option( self::transient_key( $purchase_code ) . '_timestamp' );
	}

	/**
	 * Remote validation.
	 * @param string $purchase_code The purchase code to validate.
	 * @param bool $silent If true, do not send email on failure.
	 * @return bool True if the purchase code is valid.
	 */
	public static function remote_validation(string $purchase_code, bool $silent = false ): bool
	{
		// Get actual purchase code
		if (empty($purchase_code)) {
			return false;
		}

		// Get remote status
		$remote_status = self::remote_validation_v2($purchase_code);

		// Send email to admin if the purchase code is invalid
		if (!$remote_status && !$silent) {
			$options = Settings::get_instance()->options;
			if (isset($options['email_on_fail']) && $options['email_on_fail'] === 'on'){
				self::send_email_on_fail();
			}
		}

		// Save activation status in transient
		self::set_status($purchase_code, $remote_status);

		return $remote_status;
    }

	/**
	 * Send email to admin on validation failure.
	 * @return void
	 */
	private static function send_email_on_fail(): void
	{
		$admin_email = get_option( 'admin_email' );
		$subject     = esc_html__('Can\'t validate purchase code for ', 'readabler' ) . Plugin::get_name();
		$message     = esc_html__('Hello,', 'readabler' ) . '<br><br>';
		$message    .= esc_html__('We couldn\'t validate the purchase code for the plugin ', 'readabler' ) . '<strong>' . Plugin::get_name() . '</strong> ' . esc_html__('on your website', 'readabler' ) . ' <strong>' . site_url() . '</strong>.<br>';
		$message    .= esc_html__('We will automatically attempt to revalidate it in 1 hour. Please make sure you have entered a valid purchase code and that your server can connect to our validation server.', 'readabler' ) . '<br><br>';
		$message    .= esc_html__('If you need assistance, please contact our support team at', 'readabler' ) . ' <a href="mailto:help@merkulov.design">help@merkulov.design</a>';
		$headers = array(
			'Content-Type: text/html; charset=UTF-8',
			'From: ' . get_bloginfo( 'name' ) . ' <no-reply@' . wp_parse_url( site_url(), PHP_URL_HOST ) . '>'
		);

		wp_mail( $admin_email, $subject, $message, $headers );
	}

    /**
     * Handles the migration of the activation status.
     *
     * @access private
     * @param string $purchase_code The purchase code used for validation and retrieving activation status.
     * @return bool True if successfully migrated activation status, false otherwise.
     */
    private static function migrate_status(string $purchase_code): bool
    {
        // Migration code
        $cache = new Cache();
        $key = 'activation_' . $purchase_code;
        $cached_activation = $cache->get($key, false);

        // Use activation from cache
        if (!empty($cached_activation)) {
            $cached_activation = json_decode($cached_activation, true);
            // TODO: Remove old cache
            return (bool)$cached_activation[$key];
        } else {
            return false;
        }
    }

    /**
     * Remote validation for version 2.
     *
     * @param string $purchase_code
     * @return bool
     */
    private static function remote_validation_v2(string $purchase_code): bool
    {
        // Get remote validation response
        $response = self::remote_request_v2($purchase_code);

        // Check if the response is valid
        if (is_wp_error($response)) {
	        set_transient(TabActivation::VALIDATION_ERRORS, [$response->get_error_message()], 10);
            return false;
        }

        // Check if the response is valid
        if (wp_remote_retrieve_response_code($response) !== 200) {
	        $body = wp_remote_retrieve_body($response);
	        $response_body = json_decode($body, true);
	        if ( isset($response_body['message']) ){
		        set_transient(
			        TabActivation::VALIDATION_ERRORS,
			        [$response_body['message'] . ' ' . esc_html__('Error code:' , 'readabler') . ' ' . wp_remote_retrieve_response_code($response)],
			        10
		        );
	        } else if ( wp_remote_retrieve_response_code($response) !== 403 ) {
		        set_transient(
			        TabActivation::VALIDATION_ERRORS,
			        [esc_html__('Error code:', 'readabler') . ' ' .wp_remote_retrieve_response_code($response)],
			        10
		        );
	        }
			return false;
        }

        // Decode the response
        $body = wp_remote_retrieve_body($response);
        $response_body = json_decode($body, true);
        if (!$response_body) {
            return false;
        }

        // Check if the response is successful
        if (!$response_body['success']) {
            return false;
        }

        // Check if the response is valid
        $valid = $response_body['data']['valid'] ?? false;

        return (bool)$valid;
    }

	/**
	 * Remote request to Merkulove Host (new server v2)
	 * @param string $purchase_code
	 * @return array|WP_Error
	 */
    private static function remote_request_v2(string $purchase_code): array
    {
        // Get url to request item id
        $route = '/api/v2/verify';

        return wp_remote_post(
            EnvatoItem::$host . $route . '?t=' . time(),
            [
                'headers' => EnvatoItem::get_headers($route),
                'body' => [
                    'id' => READABLER['envato_id'] ?? '',
                    'plugin' => Plugin::get_slug(),
                    'version' => Plugin::get_version(),
                    'pid' => $purchase_code,
                    'domain' => site_url(),
                    'email' => get_option('admin_email') ?? null
                ],
                'sslverify' => (Settings::get_instance()->options['check_ssl'] ?? 'off') === 'on'
            ]
        );
    }

	/**
	 * Check if activation exists for given purchase code.
	 * @param string $purchase_code
	 *
	 * @return bool
	 */
	public static function is_exists( string $purchase_code ): bool
	{
		global $wpdb;

		// Get timeout and value directly from DB
		$timeout_key = '_transient_timeout_' . Activator::transient_key($purchase_code);
		$timeout = $wpdb->get_var(
			$wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s", $timeout_key)
		);

		return (bool)$timeout;
	}

	/**
	 * Flush all activation transients.
	 * @return void
	 */
	public static function flush_activations(): void
	{
		global $wpdb;

		// Get transient key
		$key = self::transient_key();
		if (empty($key)) {
			return;
		}

		$sql = $wpdb->prepare(
			"DELETE FROM $wpdb->options WHERE option_name LIKE %s OR option_name LIKE %s",
			$wpdb->esc_like('_transient_' . $key) . '%',
			$wpdb->esc_like('_transient_timeout_' . $key) . '%'
		);
		$wpdb->query($sql);
	}

}
