<?php

use PowerpackElements\Classes\PP_Admin_Settings;

// this is the URL our updater / license checker pings. This should be the URL of the site with EDD installed
define( 'POWERPACK_SL_URL', 'https://powerpackelements.com' ); // you should use your own CONSTANT name, and be sure to replace it throughout this file

// the name of your product. This should match the download name in EDD exactly
define( 'POWERPACK_ITEM_NAME', 'PowerPack Pro for Elementor' ); // you should use your own CONSTANT name, and be sure to replace it throughout this file

define( 'POWERPACK_LICENSE_PAGE', is_network_admin()
	? network_admin_url( '/admin.php?page=powerpack-settings&tab=license' )
	: admin_url( '/admin.php?page=powerpack-settings&tab=license' )
);

if ( ! class_exists( 'PP_Plugin_Updater' ) ) {
	// load our custom updater
	include('class-pp-plugin-updater.php' );
}

function pp_elements_get( $key ) {
	if ( is_network_admin() ) {
		return get_site_option( $key );
	} else {
		return get_option( $key );
	}
}

function pp_elements_update( $key, $value ) {
	if ( is_network_admin() ) {
		return update_site_option( $key, $value );
	} else {
		return update_option( $key, $value );
	}
}

function pp_elements_delete( $key ) {
	if ( is_network_admin() ) {
		return delete_site_option( $key );
	} else {
		return delete_option( $key );
	}
}

function pp_elements_get_license_key() {
	if ( defined( 'PP_ELEMENTS_LICENSE_KEY' ) ) {
		return PP_ELEMENTS_LICENSE_KEY ? trim( PP_ELEMENTS_LICENSE_KEY ) : '';
	} else {
		return trim( pp_elements_get( 'pp_license_key' ) );
	}
}

function pp_elements_license( $action = '', $license_key = '' ) {
	$license = trim( $license_key );

	if ( empty( $license ) ) {
		$license = pp_elements_get_license_key();
	}

	// data to send in our API request
	$api_params = array(
		'edd_action'=> $action,
		'license' 	=> $license,
		'item_name' => urlencode( POWERPACK_ITEM_NAME ), // the name of our product in EDD
		'url'       => network_home_url(),
		'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
	);

	// Call the custom API.
	$response = wp_remote_post(
		POWERPACK_SL_URL,
		array(
			'timeout' => 15,
			//'sslverify' => false,
			'body' => $api_params
		) );

	return $response;
}

/**
 * Whether a URL points at the store this plugin licences against.
 *
 * The download URLs handed to the upgrader come from a remote response, so
 * something has to decide they are ours before a zip is unpacked over the
 * plugin directory. Subdomains of the store are accepted because file delivery
 * commonly sits on one; anything else is not.
 *
 * @since 3.0.0
 * @param string $url URL to test.
 * @return bool
 */
function pp_elements_is_store_url( $url ) {
	$host  = wp_parse_url( $url, PHP_URL_HOST );
	$store = wp_parse_url( POWERPACK_SL_URL, PHP_URL_HOST );

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

	$host  = strtolower( $host );
	$store = strtolower( $store );

	/**
	 * Hosts a rollback package may be downloaded from.
	 *
	 * @since 3.0.0
	 * @param array $hosts Allowed hostnames.
	 */
	$allowed = apply_filters( 'pp_elements_rollback_allowed_package_hosts', [ $store ] );

	foreach ( (array) $allowed as $allowed_host ) {
		$allowed_host = strtolower( (string) $allowed_host );

		if ( '' === $allowed_host ) {
			continue;
		}

		// The host itself, or a subdomain of it. Not a substring match: that
		// would accept "powerpackelements.com.attacker.tld".
		if ( $host === $allowed_host || substr( $host, - ( strlen( $allowed_host ) + 1 ) ) === '.' . $allowed_host ) {
			return true;
		}
	}

	return false;
}

/**
 * Cache key the rollback list is stored under.
 *
 * Keyed by licence: two sites sharing a database but not a licence must not
 * share the list, and a licence change has to invalidate it. The trailing
 * marker is the shape of the stored list — bump it whenever what gets stored
 * changes, so a site holding a list built under the old rules discards it
 * instead of serving it until the transient happens to expire.
 *
 * @since 3.0.1
 * @param string $license Licence key the list belongs to.
 * @param int    $limit   Number of packages the list was fetched with.
 * @return string
 */
function pp_elements_rollback_versions_cache_key( $license, $limit ) {
	return 'pp_rollback_files_' . md5( $license . '|' . absint( $limit ) . '|v2' );
}

/**
 * Get the rollback packages the current licence is entitled to.
 *
 * The store decides which packages a licence may see. This function decides
 * which of them are safe to hand to the upgrader: a package whose URL is not on
 * the store host is dropped here, so a spoofed or tampered response cannot turn
 * a rollback into "install this arbitrary zip".
 *
 * @since 3.0.0
 * @param int  $limit Number of packages to request.
 * @param bool $force Bypass the cached list.
 * @return array|WP_Error List of [ file_key, name, version, package ], or an error.
 */
function pp_elements_get_rollback_versions( $limit = 5, $force = false ) {
	$limit   = absint( $limit );
	$limit   = $limit > 0 ? $limit : 5;
	$license = pp_elements_get_license_key();

	if ( empty( $license ) ) {
		return new WP_Error(
			'pp_rollback_license_missing',
			__( 'Please activate your PowerPack license to use plugin rollback.', 'powerpack' )
		);
	}

	$cache_key = pp_elements_rollback_versions_cache_key( $license, $limit );

	if ( ! $force ) {
		$cached = get_site_transient( $cache_key );

		if ( is_array( $cached ) && ! empty( $cached ) ) {
			return $cached;
		}
	}

	/*
	 * No item_name, deliberately. The store resolves the product from the
	 * licence for this action, and answers 'invalid_item' when it is sent
	 * POWERPACK_ITEM_NAME — the constant carries the plugin's public name
	 * ("PowerPack Pro for Elementor") while the EDD download it has to match is
	 * titled "PowerPack Elements". Leaving it out is also the shape that keeps
	 * working if either name is ever changed.
	 */
	$api_params = [
		'edd_action'  => 'get_versions',
		'license'     => $license,
		'url'         => network_home_url(),
		'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
		'slug'        => 'powerpack-elements',
		'limit'       => $limit,
	];

	/**
	 * Parameters sent when asking the store for rollback packages.
	 *
	 * @since 3.0.0
	 * @param array $api_params Request body.
	 */
	$api_params = apply_filters( 'pp_elements_rollback_versions_api_params', $api_params );

	$response = wp_remote_post(
		POWERPACK_SL_URL,
		[
			'timeout'   => 15,
			'sslverify' => (bool) apply_filters( 'pp_updater_sl_api_request_verify_ssl', true, null ),
			'body'      => $api_params,
		]
	);

	if ( is_wp_error( $response ) ) {
		return $response;
	}

	if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
		return new WP_Error(
			'pp_rollback_versions_http_error',
			__( 'Could not fetch rollback versions from the update server.', 'powerpack' )
		);
	}

	$data = json_decode( wp_remote_retrieve_body( $response ), true );

	if ( ! is_array( $data ) ) {
		return new WP_Error(
			'pp_rollback_versions_bad_response',
			__( 'The update server returned an unreadable response.', 'powerpack' )
		);
	}

	if ( isset( $data['success'] ) && false === $data['success'] ) {
		$message = isset( $data['message'] ) ? sanitize_text_field( $data['message'] ) : __( 'Unable to fetch rollback versions.', 'powerpack' );

		return new WP_Error( 'pp_rollback_versions_failed', $message );
	}

	$raw_files = isset( $data['files'] ) && is_array( $data['files'] ) ? $data['files'] : [];

	// Older payload shape: versions, each carrying its own files.
	if ( empty( $raw_files ) && isset( $data['versions'] ) && is_array( $data['versions'] ) ) {
		foreach ( $data['versions'] as $version_data ) {
			if ( ! is_array( $version_data ) || empty( $version_data['files'] ) || ! is_array( $version_data['files'] ) ) {
				continue;
			}

			foreach ( $version_data['files'] as $version_file ) {
				if ( is_array( $version_file ) && empty( $version_file['version'] ) && ! empty( $version_data['version'] ) ) {
					$version_file['version'] = $version_data['version'];
				}

				$raw_files[] = $version_file;
			}
		}
	}

	$files    = [];
	$seen     = [];
	$rejected = 0;

	foreach ( $raw_files as $raw_file ) {
		if ( ! is_array( $raw_file ) ) {
			continue;
		}

		$package = isset( $raw_file['package'] ) ? esc_url_raw( $raw_file['package'] ) : '';

		if ( empty( $package ) ) {
			continue;
		}

		if ( ! pp_elements_is_store_url( $package ) ) {
			$rejected++;
			continue;
		}

		$name = isset( $raw_file['name'] ) ? sanitize_text_field( $raw_file['name'] ) : __( 'Rollback package', 'powerpack' );

		// A version number is optional in the response but worth having: it is
		// what tells the screen which entry the site is already running.
		$version = isset( $raw_file['version'] ) ? sanitize_text_field( (string) $raw_file['version'] ) : '';

		if ( '' === $version && preg_match( '/\d+(?:\.\d+)+/', $name, $matches ) ) {
			$version = $matches[0];
		}

		$file_key = isset( $raw_file['file_key'] ) ? sanitize_text_field( (string) $raw_file['file_key'] ) : '';

		if ( '' === $file_key ) {
			$file_key = md5( $name . '|' . $package );
		}

		/*
		 * The store has been seen listing every package twice in a single
		 * response, which put every release in the picker twice. Deduped on
		 * file_key — the identifier a rollback request names, so two entries
		 * sharing one are the same download and the second is nothing the
		 * screen could offer as a distinct choice.
		 */
		if ( isset( $seen[ $file_key ] ) ) {
			continue;
		}

		$seen[ $file_key ] = true;

		$files[] = [
			'file_key' => $file_key,
			'name'     => $name,
			'version'  => $version,
			'package'  => $package,
		];
	}

	// The response is not trusted to have honoured the limit it was asked for
	// either: the same response that repeated itself also came back longer
	// than the limit it was sent.
	if ( count( $files ) > $limit ) {
		$files = array_slice( $files, 0, $limit );
	}

	if ( empty( $files ) ) {
		if ( $rejected > 0 ) {
			return new WP_Error(
				'pp_rollback_versions_untrusted',
				sprintf(
					/* translators: %s: store hostname. */
					__( 'The update server offered download links from outside %s, so they were not used.', 'powerpack' ),
					esc_html( (string) wp_parse_url( POWERPACK_SL_URL, PHP_URL_HOST ) )
				)
			);
		}

		return new WP_Error(
			'pp_rollback_versions_empty',
			__( 'No rollback versions are currently available for this license.', 'powerpack' )
		);
	}

	set_site_transient( $cache_key, $files, HOUR_IN_SECONDS );

	return $files;
}

/**
 * Forget the cached rollback list for the current licence.
 *
 * @since 3.0.0
 * @param int $limit Limit the list was fetched with.
 * @return void
 */
function pp_elements_clear_rollback_versions_cache( $limit = 5 ) {
	$license = pp_elements_get_license_key();

	if ( empty( $license ) ) {
		return;
	}

	delete_site_transient( pp_elements_rollback_versions_cache_key( $license, $limit ) );
}

/**
 * Initialize the updater. Hooked into `init` to work with the
 * wp_version_check cron job, which allows auto-updates.
 */
function pp_elements_plugin_updater() {

	// To support auto-updates, this needs to run during the wp_version_check cron job for privileged users.
	$doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
	if ( ! current_user_can( 'manage_options' ) && ! $doing_cron ) {
		return;
	}

	// retrieve our license key from the DB
	$license_key = pp_elements_get_license_key();

	// setup the updater
	$edd_updater = new PP_Plugin_Updater(
		POWERPACK_SL_URL,
		POWERPACK_ELEMENTS_PATH . '/powerpack-elements.php',
		array(
			'version' 	=> POWERPACK_ELEMENTS_VER, 					// current version number
			'license' 	=> $license_key, 						// license key (used pp_elements_get above to retrieve from DB)
			'item_name' => POWERPACK_ITEM_NAME, 			// name of this plugin
			'author' 	=> 'IdeaBox Creations' 	// author of this plugin
		)
	);

}
add_action( 'init', 'pp_elements_plugin_updater' );

/***********************************************
* Activate license key.
***********************************************/
function pp_elements_activate_license() {

	if ( ! isset( $_POST['pp_license_activate'] ) ) {
		return;
	}

	// run a quick security check
	if ( ! isset( $_POST['pp_elements_license_nonce'] ) || ! wp_verify_nonce( $_POST['pp_elements_license_nonce'], 'pp_elements_license_nonce' ) ) {
		return; // get out if we didn't click the Activate button
	}

	$license = '';

	if ( isset( $_POST['pp_license_key'] ) ) {
		$license = trim( $_POST['pp_license_key'] );
	} else {
		$license = pp_elements_get_license_key();
	}

	$response = pp_elements_license( 'activate_license', $license );

	// make sure the response came back okay
	if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {

		if ( is_wp_error( $response ) ) {
			$message = $response->get_error_message();
		} else {
			$message = __( 'An error occurred, please try again.', 'powerpack' );
		}
	} else {
		// decode the license data
		$license_data = json_decode( wp_remote_retrieve_body( $response ) );

		if ( false === $license_data->success ) {
			$message = pp_elements_license_messages( $license_data->error );
		}
	}

	// Check if anything passed on a message constituting a failure
	if ( ! empty( $message ) ) {
		$base_url = POWERPACK_LICENSE_PAGE;
		$redirect = add_query_arg( array(
				'sl_activation' => 'false',
				'message' => urlencode( $message ),
		), $base_url );

		wp_redirect( $redirect );
		exit();
	}

	// $license_data->license will be either "valid" or "invalid"

	pp_elements_update( 'pp_license_status', $license_data->license );
	pp_elements_update( 'pp_license_user_action', 'activated' );

	if ( ! empty( $license_data->expires ) ) {
		pp_elements_update( 'pp_license_expires', $license_data->expires );
	}

	wp_redirect( POWERPACK_LICENSE_PAGE );
	exit();
}
add_action( 'admin_init', 'pp_elements_activate_license' );

/***********************************************
* Deactivate license key.
***********************************************/
function pp_elements_deactivate_license() {

	// listen for our activate button to be clicked
	if ( isset( $_POST['pp_license_deactivate'] ) ) {

		// run a quick security check
		if ( ! isset( $_POST['pp_elements_license_nonce'] ) || ! wp_verify_nonce( $_POST['pp_elements_license_nonce'], 'pp_elements_license_nonce' ) ) {
			return; // get out if we didn't click the Activate button
		}

		$license = pp_elements_get_license_key();

		$response = pp_elements_license( 'deactivate_license', $license );

		// make sure the response came back okay
		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {

			if ( is_wp_error( $response ) ) {
				$message = $response->get_error_message();
			} else {
				$message = __( 'An error occurred, please try again.', 'powerpack' );
			}

			$redirect = add_query_arg( array(
				'sl_activation' => 'false',
				'message' => urlencode( $message ),
			), POWERPACK_LICENSE_PAGE );

			wp_redirect( $redirect );
			exit();
		}

		// decode the license data
		$license_data = json_decode( wp_remote_retrieve_body( $response ) );

		// $license_data->license will be either "deactivated" or "failed"
		if ( 'deactivated' === $license_data->license || 'failed' === $license_data->license ) {
			pp_elements_delete( 'pp_license_status' );
			pp_elements_delete( 'pp_license_expires' );
		}

		pp_elements_update( 'pp_license_user_action', 'deactivated' );

		$redirect = add_query_arg( array(
			'status' => $license_data->license,
		), POWERPACK_LICENSE_PAGE );

		wp_redirect( $redirect );
		exit();
	} // End if().
}
add_action( 'admin_init', 'pp_elements_deactivate_license' );

/************************************
* check if
* a license key is still valid
* so this is only needed if we
* want to do something custom
*************************************/

function pp_elements_check_license() {
	global $wp_version;

	$license = pp_elements_get_license_key();

	$api_params = array(
		'edd_action'  => 'check_license',
		'license' 	  => $license,
		'item_name'   => urlencode( POWERPACK_ITEM_NAME ),
		'url'         => network_home_url(),
		'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
	);

	// Call the custom API.
	$response = wp_remote_post(
		POWERPACK_SL_URL,
		array(
			'timeout'   => 15,
			//'sslverify' => false,
			'body'      => $api_params,
		)
	);

	if ( is_wp_error( $response ) ) {
		return array(
			'message' => $response->get_error_message(),
		);
	}

	$license_data = json_decode( wp_remote_retrieve_body( $response ) );

	if ( ! empty( $license_data->expires ) ) {
		pp_elements_update( 'pp_license_expires', $license_data->expires );
	}

	$license_action = pp_elements_get( 'pp_license_user_action' );

	if ( 'activated' !== $license_action && 'valid' === $license_data->license ) {
		$response = pp_elements_license( 'activate_license' );

		if ( 'valid' === $license_data->license ) {
			pp_elements_update( 'pp_license_status', $license_data->license );
			pp_elements_update( 'pp_license_user_action', 'activated' );
		}
	}
	if ( 'activated' === $license_action && 'valid' !== $license_data->license ) {
		pp_elements_delete( 'pp_license_status' );
		pp_elements_delete( 'pp_license_user_action' );
		pp_elements_delete( 'pp_license_expires' );
	}
	// Delete the license key if the site is inactive or deactivated from remote.
	if ( 'site_removed' === $license_data->license && ! isset( $_REQUEST['pp_license_key'] ) ) { 
		pp_elements_delete( 'pp_license_key' );
	}

	// if ( 'valid' === $license_data->license ) {
	// 	return 'valid';
	// 	// this license is still valid.
	// } elseif ( 'deactivated' !== $license_data->license ) {
	// 	// this license is no longer valid
	// 	// delete license status.
	// 	pp_elements_delete( 'pp_license_status' );

	// 	if ( in_array( $license_data->license, array( 'site_inactive' ) ) ) {
	// 		$response = pp_elements_license( 'activate_license' );

	// 		if ( ! is_wp_error( $response ) ) {
	// 			// decode the license data
	// 			$license_data = json_decode( wp_remote_retrieve_body( $response ) );

	// 			if ( 'valid' === $license_data->license ) {
	// 				pp_elements_update( 'pp_license_status', $license_data->license );
	// 			}
	// 		}
	// 	}
	// }

	return $license_data->license;
}

 /**
  * Show update message on plugins page
  */
function pp_elements_update_message( $plugin_data, $response ) {
	$status = pp_elements_check_license();

	if ( 'valid' != $status ) {

		if ( is_array( $status ) ) {
			$main_msg = $status['message'];
		} else {
			$main_msg = sprintf( __( 'Please activate the license to enable automatic updates for this plugin. License status: %s', 'powerpack' ), $status );
		}

		$message  = '';
		$message .= '<div style="padding: 5px 10px; margin-top: 10px; background: #d54e21; color: #fff; margin-bottom: 10px;">';
		$message .= __( '<strong>UPDATE UNAVAILABLE!</strong>', 'powerpack' );
		$message .= '&nbsp;&nbsp;&nbsp;';
		$message .= $main_msg;
		$message .= ' <a href="' . POWERPACK_SL_URL . '" target="_blank" style="color: #fff; text-decoration: underline;">';
		$message .= __( 'Buy Now', 'powerpack' );
		$message .= ' &raquo;</a>';
		$message .= '</div>';
		$message .= '<style>#pp-elements-update .notice p:empty {display:none;}</style>';

		echo $message;
	}
}
add_action( 'in_plugin_update_message-' . POWERPACK_ELEMENTS_BASE, 'pp_elements_update_message', 1, 2 );

function pp_elements_license_messages( $status ) {
	$message = '';

	switch ( $status ) {

		case 'expired' :

			$message = sprintf(
				__('Your license key expired on %s.', 'powerpack'),
				date_i18n(get_option('date_format'), strtotime($license_data->expires, current_time('timestamp')))
			);
			break;

		case 'revoked' :
		case 'disabled':

			$message = __('Your license key has been disabled.', 'powerpack');
			break;

		case 'missing' :
		case 'invalid' :

			$message = __('Invalid license.', 'powerpack');
			break;

		case 'site_inactive' :

			$message = __('Your license is not active for this URL.', 'powerpack');
			break;

		case 'item_name_mismatch' :

			$message = sprintf(__('This appears to be an invalid license key for %s.', 'powerpack'), POWERPACK_ITEM_NAME);
			break;

		case 'no_activations_left':

			$message = __('Your license key has reached its activation limit.', 'powerpack');
			break;

		default :
			// translators: %s for license status.
			$message = sprintf( __('An error occurred, please try again. Status: %s', 'powerpack'), $status );
			break;
	}

	return $message;
}
