<?php
/**
 * EDD License Handling
 *
 * Add-ons can register themselves in order to leverage Easy Digital Downloads Software Licensing features.
 *
 * See add-ons.php for the ctc_register_add_on() function. A registered add-on benefits from:
 *
 * 1. License Key field and activate/deactivate buttons in Church Content Settings
 * 2. One-click updates via an Easy Digital Downloads store using Software Licensing add-on
 * 3. Admin notice when add-on license is inactive, expiring soon or expired
 *
 * Future: Possibly turn this into a class
 *
 * @package    Church_Theme_Content
 * @subpackage Admin
 * @copyright  Copyright (c) 2014 - 2019, ChurchThemes.com
 * @link       https://github.com/churchthemes/church-theme-content
 * @license    GPLv2 or later
 * @since      1.2
 */

// No direct access
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/*******************************************
 * SUPPORT
 *******************************************/

/**
 * Add-on supports EDD licensing
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 */
function ctc_edd_license_supported( $add_on_dir ) {

	$supported = false;

	// Store URL is all that is required
	// Everything else can be auto-detected
	if ( ctc_get_add_on( $add_on_dir, 'store_url' ) ) {
		$supported = true;
	}

	return apply_filters( 'ctc_edd_license_supported', $supported, $add_on_dir );

}

/*************************************************
 * SETTINGS
 *************************************************/

/**
 * Add license key fields to settings page
 *
 * A field for each registered add-on will be added to the settings page
 *
 * @since 1.2
 * @param array $fields Settings fields in the section
 * @return array Modified setting fields with license key fields inserted
 */
function ctc_edd_license_settings( $section ) {

	// Get registered add-ons
	$add_ons = ctc_get_add_ons();

	// Any add-ons registered?
	if ( $add_ons ) {

		// Loop add-ons
		$fields = array();
		foreach ( $add_ons as $add_on ) {

			// Add-on supports EDD licensing?
			if ( ! ctc_edd_license_supported( $add_on['plugin_dir'] ) ) {
				continue;
			}

			// Setting key
			$key = $add_on['plugin_dir'] . '_license_key';

			// License Key
			$fields[ $key ] = array(
				'name'				=> $add_on['name'],
				'desc'				=> '',
				'type'				=> 'text', // text, textarea, checkbox, checkbox_multiple, radio, select, number
				'checkbox_label'	=> '', //show text after checkbox //show text after checkbox
				'options'			=> array(), // array of keys/values for radio or select
				'default'			=> '', // value to pre-populate option with (before first save or on reset)
				'no_empty'			=> false, // if user empties value, force default to be saved instead
				'allow_html'		=> false, // allow HTML to be used in the value
				'class'				=> '', // classes to add to input
				'custom_content'	=> 'ctc_edd_license_key_field', // function for custom display of field input
				'custom_sanitize'	=> 'ctc_edd_license_sanitize', // function to do additional sanitization
			);

		}

		// Make fields filterable.
		$fields = apply_filters( 'ctc_edd_license_settings_fields', $fields );

		// Add fields to section.
		$section['fields'] = array_merge( $fields, $section['fields'] );

		// Hide Church Content Pro content field if it is active and showing license field.
		if ( isset( $section['fields'][ 'church_content_pro_note' ] ) && ctc_pro_is_active() ) {
			unset( $section['fields'][ 'church_content_pro_note' ] );
		}

	}

	// No add-ons.
	else {

	}

	return $section;

}

add_filter( 'ctps_section-licenses', 'ctc_edd_license_settings' );

/**
 * Custom settings field for license key
 *
 * Show a custom settings field for license key entry, activation/renewal buttons, etc.
 *
 * This is a callback used in ctc_edd_license_settings()
 *
 * @since 1.2
 * @param array $args Field arguments for the setting
 * @param array $data Field data prepared by field_content(), for use in field output
 * @return string HTML for field output
 */
function ctc_edd_license_key_field( $args, $data ) {

	// Get add-on
	$add_on_dir = str_replace( '_license_key', '', $data['id'] );

	// License key and status
	$license = ctc_edd_license_key( $add_on_dir );
	$status = ctc_edd_license_status( $add_on_dir ); // local status

	// Renewal URL
	$renewal_url = ctc_edd_license_renewal_url( $add_on_dir );

	// Text Input.
	$input = '<input type="text" ' . $data['common_atts'] . ' id="' . $data['esc_element_id'] . '" value="' . $data['esc_value'] . '" />';
	$input = apply_filters( 'ctc_edd_license_key_input', $input, $args, $data );
	$html = $input;

	// License info
	$license_info = '';

	// Licensed provided? Show button and information
	if ( $license ) {

		// Show Activate/Deactivate buttons unless expired (in that case show Renew alone)
		if ( ! ctc_edd_license_expired( $add_on_dir ) ) {

			// Activate Button (license is not active)
			if ( ! ctc_edd_license_active( $add_on_dir ) ) {
				$html .= '<input type="submit" class="button button-primary ctc-license-button ctc-license-activate-button" name="ctc_edd_license_activate[' . esc_attr( $add_on_dir ) . ']" value="' . esc_attr__( 'Activate License', 'church-theme-content' ). '" />';
			}

			// Deactivate Button (license is active)
			else {
				$html .= '<input type="submit" class="button button ctc-license-button ctc-license-deactivate-button" name="ctc_edd_license_deactivate[' . esc_attr( $add_on_dir ) . ']" value="' . esc_attr__( 'Deactivate License', 'church-theme-content' ) . '" />';
			}

		}

		// Renew Button
		// Show only if renewal URL provided and license is active or expired
		if ( ctc_edd_license_renewal_url( $add_on_dir ) && ( ctc_edd_license_active( $add_on_dir ) || ctc_edd_license_expired( $add_on_dir ) ) ) {
			$html .= '<a href="' . esc_url( $renewal_url ) . '" class="button button' . ( ctc_edd_license_expired( $add_on_dir ) || ctc_edd_license_expiring_soon( $add_on_dir ) ? '-primary' : '' ) . ' ctc-license-button ctc-license-renew-button" target="_blank" />' . esc_html__( 'Renew License', 'church-theme-content' ) . '</a>';
		}

		// Status label
		//$license_info .= '<span class="ctc-license-info-label ctc-license-status-label">' . esc_html_x( 'Status:', 'license key', 'church-theme-content' ) . '</span> ';

			// Status value
			if ( ctc_edd_license_active( $add_on_dir ) ) {

				// Active
				$license_info .= '<span class="ctc-license-active">' . esc_html_x( 'Active', 'license key', 'church-theme-content' ) . '</span>';

				// Expiring soon
				if ( ctc_edd_license_expiring_soon( $add_on_dir ) ) {
					$license_info .= ' / <span class="ctc-license-expiring-soon">' . esc_html_x( 'Expiring Soon', 'license status', 'church-theme-content' ) . '</span>';
				}

			} elseif ( ctc_edd_license_expired( $add_on_dir ) ) {

				// Expired
				$license_info .= '<span class="ctc-license-expired">' . esc_html_x( 'Expired', 'license key', 'church-theme-content' ) . '</span>';

			} else {

				// Inactive
				$license_info .= '<span class="ctc-license-inactive">' . esc_html_x( 'Inactive', 'license key', 'church-theme-content' ) . '</span>';

			}

		// Expiration
		// Show expiration only if license is active or expired, not just if have the data
		if ( ctc_edd_license_expiration( $add_on_dir ) && ( ctc_edd_license_active( $add_on_dir ) || ctc_edd_license_expired( $add_on_dir ) ) ) {

			// Expiration label
			$license_info .= '<span class="ctc-license-info-label ctc-license-expiration-label">' . esc_html_x( 'Expiration:', 'license key', 'church-theme-content' ) . '</span> ';

			// Expiration value
			$license_info .= '<span class="ctc-license-expiration">' . esc_html( ctc_edd_license_expiration_formatted( $add_on_dir ) ) . '</span>';

		}

	}

	// License key not provided
	else {
		$license_info .= '<p><span class="ctc-license-key-missing">' . esc_html__( 'Key Not Entered', 'church-theme-content' ) . '</span></p>';
	}

	// Append license info
	if ( ! empty( $license_info ) ) {
		$html .= '<p class="ctc-edd-license-info">' . $license_info . '</p>';
	}

	return $html;

}

/**
 * Sanitize license key setting
 *
 * Unset local status and expiration if changing key.
 * This is a callback used in ctc_edd_license_settings()
 *
 * @since 1.2
 * @param string $new Key being saved
 * @param string $field Setting's field data
 * @return string Sanitized key
 */
function ctc_edd_license_sanitize( $new, $field ) {

	// Get add-on
	$add_on_dir = str_replace( '_license_key', '', $field['id'] );

	// Get old license key value
	$old = ctc_edd_license_key( $add_on_dir );

	// Unset local status as active and expiration date when changing key -- need to activate new key
	if ( $old && $old != $new ) {

		// Note: status and expiration stored in their own options
		// Not in plugin settings array (easier management and these are not user settings)
		delete_option( $add_on_dir . '_license_status' );
		delete_option( $add_on_dir . '_license_expiration' );

	}

	return $new;

}

/*******************************************
 * LICENSE DATA (LOCAL)
 *******************************************/

/**
 * License key value
 *
 * License key is stored in plugin settings array
 * Note: license status and expiration are stored in options (they are not plugin settings)
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @param string $append Append string to base option name
 * @return string Option value
 */
function ctc_edd_license_key( $add_on_dir ) {

	// Get license key setting
	$license_key = trim( ctc_setting( $add_on_dir . '_license_key' ) );

	// Return filtered
	return apply_filters( 'ctc_edd_license_key', $license_key, $add_on_dir );

}

/**
 * Get local license status
 *
 * Note if inactive, value is empty
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return string status active, expired or empty (inactive)
 */
function ctc_edd_license_status( $add_on_dir ) {

	$status = get_option( $add_on_dir . '_license_status' );

	return apply_filters( 'ctc_edd_license_status', $status, $add_on_dir );

}

/**
 * License is locally active
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return bool True if active
 */
function ctc_edd_license_active( $add_on_dir ) {

	$active = false;

	if ( 'active' == ctc_edd_license_status( $add_on_dir ) ) {
		$active = true;
	}

	return apply_filters( 'ctc_edd_license_active', $active, $add_on_dir );

}

/**
 * License is locally inactive
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return bool True if inactive
 */
function ctc_edd_license_inactive( $add_on_dir ) {

	$inactive = false;

	if ( ! ctc_edd_license_status( $add_on_dir ) ) {
		$inactive = true;
	}

	return apply_filters( 'ctc_edd_license_inactive', $inactive, $add_on_dir );

}

/**
 * License is locally expired
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return bool True if expired
 */
function ctc_edd_license_expired( $add_on_dir ) {

	$expired = false;

	if ( 'expired' == ctc_edd_license_status( $add_on_dir ) ) {
		$expired = true;
	}

	return apply_filters( 'ctc_edd_license_expired', $expired, $add_on_dir );

}

/**
 * License is expiring soon
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return bool True if expiring within X days
 */
function ctc_edd_license_expiring_soon( $add_on_dir ) {

	$expiring_soon = false;

	$expiration_data = ctc_edd_license_expiration_data( $add_on_dir );

	if ( ! empty( $expiration_data['expiring_soon'] ) ) {
		$expiring_soon = true;
	}

	return apply_filters( 'ctc_edd_license_expiring_soon', $expiring_soon, $add_on_dir );

}

/**
 * Set license expiration date locally
 *
 * Removes seconds so stored value is YYYY-MM-DD.
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @param string $expiration Remove expiration date value
 * @return string Expiration YYYY-MM-DD
 */
function ctc_edd_license_update_expiration( $add_on_dir, $expiration ) {

	// Only if have a value (old value better than no value)
	if ( ! empty( $expiration ) ) {

		// Remove seconds so stored value is YYYY-MM-DD
		list( $expiration ) = explode( ' ', $expiration );
		$expiration = trim( $expiration );

		// Not an invalid key?
		if ( $expiration != '1970-01-01' ) {

			// Update local value
			update_option( $add_on_dir . '_license_expiration', $expiration );

		}

	}

}

/**
 * Get license expiration date (local value)
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return string Expiration YYYY-MM-DD
 */
function ctc_edd_license_expiration( $add_on_dir ) {

	$expiration = get_option( $add_on_dir . '_license_expiration' );

	return apply_filters( 'ctc_edd_license_expiration', $expiration, $add_on_dir );

}

/**
 * Show license expiration date (formatted)
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @param string Text to show if no date found
 * @return string Expiration date formatted
 */
function ctc_edd_license_expiration_formatted( $add_on_dir, $none_text = false ) {

	$expiration = ctc_edd_license_expiration( $add_on_dir );

	$date = '';

	if ( $expiration ) {
		$date = date_i18n( get_option( 'date_format' ), strtotime( $expiration ) );
	} elseif ( ! empty( $none_text ) ) {
		$date = $none_text;
	}

	return apply_filters( 'ctc_edd_license_expiration_formatted', $date, $add_on_dir );

}

/**
 * Get expiration data
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return array date in various formats and whether it is expiring soon or not
 */
function ctc_edd_license_expiration_data( $add_on_dir ) {

	$data = array();

	$data['expiration'] = ctc_edd_license_expiration( $add_on_dir );
	$data['expiration_date'] = ctc_edd_license_expiration_formatted( $add_on_dir, esc_html_x( 'unknown date', 'license expiration', 'church-theme-content' ) );
	$data['expiration_ts'] = ! empty( $data['expiration'] ) ? strtotime( $data['expiration'] ) : '';
	$data['expiring_soon_days'] = ctc_get_add_on( $add_on_dir, 'expiring_soon_days' );
	$data['expiring_soon_ts'] = time() + ( DAY_IN_SECONDS * $data['expiring_soon_days'] );
	$data['expiring_soon'] = ( ! ctc_edd_license_expired( $add_on_dir ) && ! empty( $data['expiration_ts'] ) && $data['expiration_ts'] < $data['expiring_soon_ts'] ) ? true : false;

	return apply_filters( 'ctc_edd_license_expiration_data', $data, $add_on_dir );

}

/*******************************************
 * ACTIVATION / DEACTIVATION
 *******************************************/

/**
 * Activate or deactivate a license key on-demand
 *
 * This function can be run anywhere; it is not hooked (see other functions below)
 * It is primarily for running via action after activate/deactivate button clicked.
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with when not executing via post
 * @param string $action Action when not executing via post (activate_license or deactivate_license)
 * @return string $result Result such as 'activate_success' or 'activate_fail' (not every action has result)
 */
function ctc_edd_license_activation( $add_on_dir, $action ) {

	$result = '';

	// Add-on supports licensing?
	if ( ! ctc_edd_license_supported( $add_on_dir ) ) {
		return $result;
	}

	// Continue to activate or deactivate
	if ( $action && $add_on_dir ) {

		// Get license data
		$license_data = ctc_edd_license_action( $add_on_dir, $action );

		// Call action via API
		if ( $license_data ) {

			// If activated remotely, set local status; or set local status if was already active remotely -- keep in sync
			if ( 'activate_license' == $action ) {

				// Success
				if ( 'valid' == $license_data->license || 'valid' == ctc_edd_license_check( $add_on_dir ) ) {

					update_option( $add_on_dir . '_license_status', 'active' );

					$result = 'activate_success';

				}

				// Failure - note error for next page load
				else {
					$result = 'activate_fail';
				}

			}

			// If deactivated remotely, set local status; or set local status if was already inactive remotely -- keep in sync
			elseif (
				'deactivate_license' == $action
				&& (
					'deactivated' == $license_data->license
					|| 'disabled' == $license_data->license // if disabled would return failed... (leaving this just in case)
					|| 'failed' == $license_data->license // likely means deactivation failed because it's disabled
					|| 'inactive' == ctc_edd_license_check( $add_on_dir )
				)
			) {

				// Success or failure?
				if ( 'deactivated' == $license_data->license ) {
					$result = 'deactivate_success';
				} else {
					$result = 'deactivate_fail';
				}

				// Delete license status
				delete_option( $add_on_dir . '_license_status' );

			}

			// Set current expiration locally
			// Local will be synced to remote daily in case changes
			if ( isset( $license_data->expires ) ) {
				ctc_edd_license_update_expiration( $add_on_dir, $license_data->expires );
			}

		}

	}

	return $result;

}

/**
 * Detect activation/deactivation button click
 *
 * This is run on pre_update_option_* before settings are saved, but after sanitization.
 * update_option_* does not fire unless values have changed so it won't be useful.
 *
 * With this $_POST is available so it can be determined which button clicked.
 * After that is determined, data is passed to ctc_edd_license_activation()
 * for actual activation (which happens after a redirect, so transient is used)
 *
 * @since 1.2
 * @param array $settings New settings values to save
 * @param array $old_settings Settings values presently saved
 * @return array Settings to save
 */
function ctc_edd_license_detect_user_action( $settings, $old_settings ) {

	// Activate button clicked
	if ( ! empty( $_POST['ctc_edd_license_activate'] ) ) {
		$action = 'activate_license'; // for EDD Software Licensing API
		$add_on_dir = key( $_POST['ctc_edd_license_activate'] );
	}

	// Deactivate button clicked
	elseif ( ! empty( $_POST['ctc_edd_license_deactivate'] ) ) {
		$action = 'deactivate_license'; // for EDD Software Licensing API
		$add_on_dir = key( $_POST['ctc_edd_license_deactivate'] );
	}

	// Has action, add-on and license key?
	if ( ! empty( $action ) && ! empty( $settings[$add_on_dir . '_license_key'] ) ) {

		// Set transient with license key data
		// ctc_edd_license_activation() will run this after redirect occurs
		// It is done after everything is saved in case license key value changed
		set_transient( 'ctc_edd_license_activation', array(
			'action'		=> $action,
			'add_on_dir'	=> $add_on_dir,
		), 30 ); // expires when first run or after 30 seconds in case of timeout

	}

	// Always return value to be saved
	// Otherwise plugin settings will be wiped
	return $settings;

}

// Using pre_update_option instead of update_option action because update_option
// will not fire unless values changed -- and we need $_POST to detect button action
add_filter( 'pre_update_option_ctc_settings', 'ctc_edd_license_detect_user_action', 10, 2 );

/**
 * Do activation/deactivation after user's button click
 *
 * This runs after settings have been saved.
 * It uses transient data set when ctc_edd_license_detect_user_action() is triggered.
 * This data is used to run the activation/deactivation routine.
 *
 * Not using update_option_* hook because it only runs when values changed.
 *
 * @since 1.2
 * @global object $ctc_settings
 */
function ctc_edd_license_activation_after_user_action() {

	global $ctc_settings;

	// Is this plugin settings page?
	if ( ! $ctc_settings->is_settings_page() ) {
		return;
	}

	// Get transient with add-on to activate license for
	$activation_data = get_transient( 'ctc_edd_license_activation' );

	// Is a an add-on's license to be activated/deactivated?
	if ( ! empty( $activation_data ) ) {

		// Delete transient so this is run once
		delete_transient( 'ctc_edd_license_activation' );

		// Prepare data
		$add_on_dir = $activation_data['add_on_dir'];
		$action = $activation_data['action'];

		// Attempt activation/deactivation remotely and set the result locally
		$result = ctc_edd_license_activation( $add_on_dir, $action );

		// Activation result
		if ( $result ) {

			// Set transient so notice shows on next settings page load
			set_transient( 'ctc_edd_license_activation_result', array(
				'result'		=> $result,
				'add_on_dir'	=> $add_on_dir,
			), 15 ); // will be deleted after shown or in 15 seconds

		}

	}

}

add_action( 'ctps_after_save', 'ctc_edd_license_activation_after_user_action' );

/**
 * Show notice on activation/deactivation success/failure
 *
 * @since 1.2
 * @global object $ctc_settings
 */
function ctc_edd_license_activation_result_notice() {

	global $ctc_settings;

	// Only on plugin settings
	if ( ! $ctc_settings->is_settings_page() ) {
		return;
	}

	// Have a result transient?
	$result = get_transient( 'ctc_edd_license_activation_result' );
	if ( $result ) {

		// Get result data?
		$add_on_dir = $result['add_on_dir'];
		$result = $result['result'];

		// Have result data?
		if ( ! empty( $add_on_dir ) && ! empty( $result ) ) {

			// Get notice message
			$notice_message = ctc_get_add_on( $add_on_dir, $result . '_notice' );

			// Output notice
			if ( $notice_message ) {

				// Show notice and hide "Settings saved." notice beneath
				?>

				<div id="ctc-license-notice-<?php echo esc_attr( $result ); ?>" class="notice notice-<?php if ( preg_match( '/success/', $result ) ) : ?>success<?php else : ?>error<?php endif; ?>">
					<p>
						<?php
						echo wp_kses(
							$notice_message,
							array(
								'strong' => array(),
								'b' => array(),
								'em' => array(),
								'i' => array(),
								'a' => array(
									'href' => array(),
									'target' => array(),
								),
							)
						);
						?>
					</p>
				</div>

				<style type="text/css">
				#setting-error-settings_updated {
					display: none;
				}
				</style>

				<?php

			}

		}

		// Delete transient after showing notice once
		delete_transient( 'ctc_edd_license_activation_result' );

	}

}

add_action( 'admin_notices', 'ctc_edd_license_activation_result_notice' );

/*******************************************
 * STATUS NOTICES
 *******************************************/

/**
 * Show inactive, expiring soon and expired license notices
 *
 * @since 1.2
 * @global object $ctc_settings
 */
function ctc_edd_license_notices() {

	global $ctc_settings;

	// User can manage plugins?
	// Keeps notices from showing to non-admin users
	if ( ! current_user_can( 'install_plugins' ) ) {
		return;
	}

	// Show only on relevant pages as not to overwhelm the admin
	// Don't show on settings page (redundant on Licenses tab and irrelevants/distracting on others)
	$screen = get_current_screen();
	if ( ! in_array( $screen->base, array( 'dashboard', 'plugins', 'update-core' ) ) ) {
		return;
	}

	// Get add-ons
	$add_ons = ctc_get_add_ons();

	// Have add-ons?
	if ( $add_ons ) {

		// Collect add-ons requiring a notice
		$inactive_add_ons = array();
		$expiring_soon_add_ons = array();
		$expired_add_ons = array();

		// Loop add-ons
		foreach( $add_ons as $add_on_dir => $add_on ) {

			// Add-on supports EDD licensing?
			if ( ! ctc_edd_license_supported( $add_on_dir ) ) {
				continue;
			}

			// Get expiration data
			$expiration_data = ctc_edd_license_expiration_data( $add_on_dir );

			// Active But Expiring Soon
			// Show a reminder notice 30 days before expiration
			if ( ctc_edd_license_active( $add_on_dir ) && $expiration_data['expiring_soon'] ) {
				$expiring_soon_add_ons[$add_on_dir] = $add_on;
			}

			// Expired
			// This shows as error not notice, since it has come to pass
			elseif ( ctc_edd_license_expired( $add_on_dir ) ) {
				$expired_add_ons[$add_on_dir] = $add_on;
			}

			// Inactive
			elseif ( ! ctc_edd_license_active( $add_on_dir ) ) {
				$inactive_add_ons[$add_on_dir] = $add_on;
			}

		}

		// Get notices.
		$notices = '';
		$notices .= ctc_edd_license_notice_content( 'inactive', $inactive_add_ons );
		$notices .= ctc_edd_license_notice_content( 'expired', $expired_add_ons );
		$notices .= ctc_edd_license_notice_content( 'expiring_soon', $expiring_soon_add_ons );

		// Filter notices.
		$notices = apply_filters( 'ctc_edd_license_notices', $notices );

		// Output notices.
		echo $notices;

	}

}

add_action( 'admin_notices', 'ctc_edd_license_notices', 8 ); // higher priority than regular notices (10) but lower than theme license notice (7)

/**
 * Content for inactive, expiring soon and expired license notices
 *
 * @since 1.2
 * @param string $notice Type of notice (inactive, expiring_soon, expired)
 * @param array $add_ons Add-ons to show notice for
 * @return string HTML output for admin_notice
 */
function ctc_edd_license_notice_content( $notice, $add_ons ) {

	$content = '';

	// Count add-ons
	$count = count( $add_ons );

	// Have at least one?
	if ( $count ) {

		// Empty vars.
		$add_on_dir = '';
		$expiration_date = '';
		$renewal_url = '';
		$renewal_info_url = '';
		$pro_license_key = '';
		$theme_name = '';
		$theme_license_key = '';
		$hide_theme_expiration_notice = '';

		// One add-on
		if ( 1 == $count ) {

			// Message to use.
			$notice_key = $notice;

			// Get first and only add-on in array.
			$values = array_values( $add_ons );
			$first_add_on = array_shift( $values );

			// Add-on data.
			$add_on_dir = $first_add_on['plugin_dir'];
			$add_on_names = $first_add_on['name']; // single name
			$expiration_data = ctc_edd_license_expiration_data( $add_on_dir );
			$expiration_date = $expiration_data['expiration_date'];

			// URLs for add-on overriding default notices during registration
			$renewal_url = ctc_edd_license_renewal_url( $add_on_dir );
			$renewal_info_url = ctc_get_add_on( $add_on_dir, 'renewal_info_url' );

			// Is this Pro add-on? Check if theme uses same license key.

			if ( 'church-content-pro' === $add_on_dir ) {

				// Get Pro license key.
				$pro_license_key = ctc_edd_license_key( $add_on_dir );

				// Get ChurchThemes.com theme license key.
				$active_theme = wp_get_theme();
				if ( function_exists( 'ctfw_edd_license_key' ) ) {
					$theme_license_key = ctfw_edd_license_key();
				}

				// Have theme from ChurchTheme.com and license key is same as Pro.
				if ( $theme_license_key && $theme_license_key === $pro_license_key ) {

					// Message to use
					$notice_key = $notice . '_theme_pro';

					// Theme name.
					$theme_name = $active_theme->get( 'Name' );

					// Prevent the theme's notice from showing.
					$hide_theme_expiration_notice = true; // will hide expired or expiring notice (not inactive).

				}

			}

		}

		// Multiple add-ons
		else {

			// Message to use
			$notice_key = $notice . '_multiple';

			// Make list of add-on names
			$i = 0;
			$add_on_names = '';
			foreach ( $add_ons as $add_on ) {

				$i++;

				// Separate with comma or "and"
				if ( $i == $count ) {

					/* translators: separator between last and second to last add-ons in inactive/expired admin notice (instead of comma) */
					$add_on_names .= esc_html_x( ' and ', 'license notice', 'church-theme-content' );

				} elseif ( 1 != $i ) {

					/* translators: separator between add-on names in inactive/expired admin notice */
					$add_on_names .= esc_html_x( ', ', 'license notice', 'church-theme-content' );

				}

				// Append name to list
				$add_on_names .= '<strong>' . $add_on['name'] . '</strong>';

			}

		}

		// Notice Messages
		// These are generic for multiple add-ons from different providers
		// They link to the Add-on Licenses settings page where the Renew buttons are highlighted
		// Those with _agency_mode appended are used when Agency Mode is enabled (Pro).
		$notices =  array(

			// The used message is escaped with wp_kses in loop below

			/* translators: %1$s is URL to add-on license settings, %2$s is name of add-on */
			'inactive'						=> ctc_get_add_on( $add_on_dir, 'inactive_notice' ),

			/* translators: %1$s is URL to add-on license settings, %2$s is names of add-ons */
			'inactive_multiple'				=> __( '<strong>Add-on Licenses Inactive:</strong> <a href="%1$s">Activate Your Add-on Licenses</a> to enable updates for %2$s.', 'church-theme-content' ),

			/* translators: %1$s is URL to add-on license settings, %2$s is name of add-on, %3$s is expiration date, %7$s agency name. */
			'expired'						=> ctc_get_add_on( $add_on_dir, 'expired_notice' ),
			'expired_agency_mode'           => __( '<strong>Add-on License Expired:</strong> Ask %7$s to renew their <a href="%1$s">Add-on License</a> for <strong>%2$s</strong> to re-enable updates for your site (expired <strong>%3$s</strong>). <a href="%5$s" target="_blank">Learn More</a>', 'church-content-pro' ),

			/* translators: %1$s is URL to add-on license settings, %2$s is names of add-ons, %7$s agency name. */
			'expired_multiple'				=> __( '<strong>Add-on Licenses Expired:</strong> <a href="%1$s">Renew Your Add-on Licenses</a> for %2$s to re-enable updates.', 'church-theme-content' ),
			'expired_multiple_agency_mode'	=> __( '<strong>Add-on Licenses Expired:</strong> Ask %7$s to renew their <a href="%1$s">Add-on Licenses</a> for %2$s to re-enable updates for your site.', 'church-theme-content' ),

			/* translators: %2$s is name of Pro plugin, %3$s is expiration date, %4$s is link to renew, %5$s is link to learn more, %6$s is name of theme sharing same license as add-on, %7$s agency name. */
			// This shows only for ChurchThemes.com themes when license key is same for theme and Pro plugin.
			'expired_theme_pro'				=> __( '<strong>License Expired:</strong> <a href="%4$s">Renew Your License</a> for the <b>%6$s</b> theme and <b>%2$s</b> add-on to reactivate updates and support (expired <b>%3$s</b>). <a href="%5$s" target="_blank">Learn More</a>', 'church-theme-content' ),
			'expired_theme_pro_agency_mode'	=> __( '<strong>License Expired:</strong> Ask %7$s to renew their license for the <b>%6$s</b> theme and <b>%2$s</b> add-on to reactivate updates for your site (expired <b>%3$s</b>). <a href="%5$s" target="_blank">Learn More</a>', 'church-theme-content' ),

			/* translators: %1$s is URL to add-on license settings, %2$s is name of add-on, %3$s is expiration date */
			'expiring_soon'					=> ctc_get_add_on( $add_on_dir, 'expiring_soon_notice' ),

			/* translators: %1$s is URL to add-on license settings, %2$s is names of add-ons */
			'expiring_soon_multiple'		=> __( '<strong>Add-on Licenses Expiring Soon:</strong> <a href="%1$s">Renew Your Add-on Licenses</a> for %2$s to continue receiving updates.', 'church-theme-content' ),

			/* translators: %2$s is name of Pro plugin, %3$s is expiration date, %4$s is link to renew, %5$s is link to learn more, %6$s is name of theme sharing same license as add-on. */
			// This shows only for ChurchThemes.com themes when license key is same for theme and Pro plugin.
			// Note that "Expiring Soon" notices not shown when Agency Mode is enabled.
			'expiring_soon_theme_pro'		=> __( '<strong>License Expiring Soon:</strong> <a href="%4$s">Renew Your License</a> for the <b>%6$s</b> theme and <b>%2$s</b> add-on before expiration on <b>%3$s</b>. <a href="%5$s" target="_blank">Learn More</a>', 'church-theme-content' ),

		);

		// Filter notice_key.
		// Pro add-on can do this to append _agency_mode to an expired notice.
		$notice_key = apply_filters( 'ctc_edd_license_notice_key', $notice_key, $notices );

		// Have notice message.
		if ( isset( $notices[$notice_key] ) ) {

			// Notice message.
			$message = $notices[$notice_key];

			// Class to use.
			// notice-warning (yellow) for expiring soon.
			// notice-error (red) for inactive and expired (everything else).
			$notice_class = preg_match( '/expiring_soon/', $notice_key ) ? 'notice-warning' : 'notice-error';

			// Class to denote license status.
			$notice_class .= ' ctc-license-notice-' . str_replace( '_', '-', $notice_key );

			// Agency name.
			/* translators: This is used in place of agency name for Agency Mode when no agency name available */
			$agency_name = __( 'your website provider', 'church-theme-content');
			if ( function_exists( 'ccp_agency_mode_name_linked' ) ) {
				$agency_name = ccp_agency_mode_name_linked();
			}

			// Build notice HTML.
			$content .= '<div class="ctc-license-status-notice notice ' . esc_attr( $notice_class ) . '">';
			$content .= '	<p>';
			$content .= sprintf(

							wp_kses(
								$message,
								array(
									'strong' => array(),
									'b' => array(),
									'em' => array(),
									'i' => array(),
									'a' => array(
										'href' => array(),
										'target' => array(),
									),
								)
							),

							esc_url( admin_url( 'options-general.php?page=' . CTC_DIR ) . '#licenses' ),
							$add_on_names, // or single name

							// These are available only when the notice is for a single add-on
							// Default notices should not use renewal URLs because they may not be available
							// An add-on can, however, use the renewal URLs it provides
							$expiration_date,
							$renewal_url,
							$renewal_info_url,
							$theme_name,
							$agency_name

						);
			$content .= '	</p>';
			$content .= '</div>';

			// Hide theme expired/expiring notice.
			if ( ! empty( $hide_theme_expiration_notice ) ) {
				$content .= '<style type="text/css">';
				$content .= '#ctfw-license-notice {';
				$content .= '	display: none';
				$content .= '}';
				$content .= '</style>';
			}

		}

	}

	return apply_filters( 'ctc_edd_license_notice_content', $content, $notice, $add_ons );

}

/*******************************************
 * LICENSE RENEWAL
 *******************************************/

/**
 * Construct license renewal URL
 *
 * Replace {license_key} with license key
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return string Renewal URl with license key replaced
 */
function ctc_edd_license_renewal_url( $add_on_dir ) {

	// Get raw renewal URL
	$renewal_url = ctc_get_add_on( $add_on_dir, 'renewal_url' );

	// Have URL
	if ( $renewal_url ) {

		// Replace {license_key} with license key
		$renewal_url = str_replace( '{license_key}', ctc_edd_license_key( $add_on_dir ), $renewal_url );

	}

	// Return filtered
	return apply_filters( 'ctc_edd_license_renewal_url', $renewal_url, $add_on_dir );

}

/*******************************************
 * EDD SL API
 *******************************************/

/**
 * Call API with specific action
 *
 * https://easydigitaldownloads.com/docs/software-licensing-api/
 * activate_license, deactivate_license or check_license
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @param string $action EDD API action: activate_license, deactivate_license or check_license
 * @return object License data from remote server
 */
function ctc_edd_license_action( $add_on_dir, $action ) {

	$license_data = array();

	// Add-on supports EDD licensing
	if ( ctc_edd_license_supported( $add_on_dir ) ) {

		// Valid action?
		$actions = array( 'activate_license', 'deactivate_license', 'check_license' );
		if ( in_array( $action, $actions ) ) {

			// Get license
			$license = ctc_edd_license_key( $add_on_dir );

			// Have license
			if ( $license ) {

				// Data to send in API request
				$api_params = array(
					'edd_action'	=> $action,
					'license' 		=> $license,
					'item_name'		=> urlencode( ctc_get_add_on( $add_on_dir, 'item_name' ) ), // name of download in EDD
					'url'			=> urlencode( home_url() ) // URL of this site activated for license
				);

				// Call the API
				$response = wp_remote_get( esc_url_raw( add_query_arg( $api_params, ctc_get_add_on( $add_on_dir, 'store_url' ) ) ), array( 'timeout' => 15, 'sslverify' => false ) );

				// Got a valid response?
				if ( ! is_wp_error( $response ) ) {

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

				}

			}

		}

	}

	return apply_filters( 'ctc_edd_license_action', $license_data, $add_on_dir, $action );

}

/**
 * Get remote license data
 *
 * Get status, expiration, etc. from remote
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @param string Optional key to get value for
 * @return array License data array or single value for key
 */
function ctc_edd_license_check_data( $add_on_dir, $key = false ) {

	// Get remote license data
	$data = ctc_edd_license_action( $add_on_dir, 'check_license' );

	// Convert data to array
	$data = (array) $data;

	// Get value for specific key?
	if ( isset( $data[$key] ) ) { // key is given

		// Value exists for key in object
		if ( ! empty( $data[$key] ) ) {
			$data = $data[$key];
		}

		// If key or value not found, return nothing
		// (instead of full license data from above)
		else {
			$data = '';
		}

	}

	return apply_filters( 'ctc_edd_license_check_data', $data, $add_on_dir, $key );

}

/**
 * Check license key status
 *
 * Check if license is valid on remote end.
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 * @return string Remote license status
 */
function ctc_edd_license_check( $add_on_dir ) {

	$status = ctc_edd_license_check_data( $add_on_dir, 'license' );

	return apply_filters( 'ctc_edd_license_check', $status, $add_on_dir );

}

/**
 * Sync remote/local status
 *
 * It's handy to run this periodically in case license has been remotely activated, renewed or deactivated.
 * An expired license could have been renewed or a site URL addded remotely.
 * The license could have been expired, refunded or the URL no longer matches (whole site move).
 *
 * This also updates the expiration date locally.
 *
 * Otherwise, they may think they are up to date when they are not.
 *
 * @since 1.2
 * @param string $add_on_dir Add-on to work with
 */
function ctc_edd_license_sync( $add_on_dir ) {

	// Plugin supports EDD licensing?
	if ( ! ctc_edd_license_supported( $add_on_dir ) ) {
		return;
	}

	// Get remote license data
	$license_data = ctc_edd_license_check_data( $add_on_dir );

	// Continue only if got a response
	if ( ! empty( $license_data ) ) { // don't do anything if times out

		// Get remote status
		$status = isset( $license_data['license'] ) ? $license_data['license'] : false;

		// Active remotely
		// This will activate locally if had been inactive or expired locally
		if ( 'valid' == $status ) {

			// Activate locally
			update_option( $add_on_dir . '_license_status', 'active' );

		}

		// Inactive remotely
		elseif ( in_array( $status, array( 'inactive', 'site_inactive', 'disabled' ) ) ) { // status is not valid

			// Deactivate locally
			delete_option( $add_on_dir . '_license_status' );

		}

		// Expired remotely
		elseif ( 'expired' == $status ) {

			// Set status expired locally
			update_option( $add_on_dir . '_license_status', 'expired' );

		}

		// Update expiration data
		// This helps the user know when to renew
		if ( isset( $license_data['expires'] ) ) {
			ctc_edd_license_update_expiration( $add_on_dir, $license_data['expires'] );
		}

	}

}

/**
 * Sync remote/local status automatically
 *
 * Check for remote status change periodically on relevant pages: Dashboard, Updates, Plugins, Plugin Settings (CTC)
 * Check in real-time on Plugin Settings page so if remote change was made, they see it immediately as if in account.
 *
 * Once daily is enough to keep notice on dashboard and updates up to date without hammering remote server.
 *
 * @since 1.2
 * @global object $ctc_settings
 */
function ctc_edd_license_auto_sync() {

	global $ctc_settings;

	// Admin only
	if ( ! is_admin() ) {
		return;
	}

	// Periodically in relevant areas or always on Plugin Settings page (where license key status shown)
	$screen = get_current_screen();
	if ( ! $ctc_settings->is_settings_page() && ! in_array( $screen->base, array( 'dashboard', 'update-core', 'plugins' ) ) ) {
		return;
	}

	// Get add-ons
	$add_ons = ctc_get_add_ons();

	// Had add-ons?
	// No need to run if no add-ons registered
	if ( ! $add_ons ) {
		return;
	}

	// Has this been checked in last day or is it plugin settings page?
	// Settings page always runs this
	if ( get_transient( 'ctc_edd_license_auto_sync' ) && ! $ctc_settings->is_settings_page() ) {
		return;
	} else {

		// Set transient to prevent check until next day
		// Once per day is enough to keep notice on dashboard and updates pages without hammering remote server
		set_transient( 'ctc_edd_license_auto_sync', true, DAY_IN_SECONDS );

	}

	// Loop add-ons
	foreach( $add_ons as $add_on_dir => $add_on ) {

		// Add-on supports EDD licensing?
		if ( ! ctc_edd_license_supported( $add_on_dir ) ) {
			return;
		}

		// Check remote status and sync both ways if necessary
		ctc_edd_license_sync( $add_on_dir );

	}

}

add_action( 'current_screen', 'ctc_edd_license_auto_sync' );

/*******************************************
 * AUTOMATIC UPDATES
 *******************************************/

/**
 * Plugin updater
 *
 * Enable one-click updates for add-on plugins
 *
 * @since 1.2
 */
function ctc_edd_license_updater() {

	// Get add-ons
	$add_ons = ctc_get_add_ons();

	// Have add-ons?
	if ( $add_ons ) {

		// Include Easy Digital Downloads Software Licensing plugin updater class
		if ( ! class_exists( 'CTC_EDD_SL_Plugin_Updater' ) ) {
			require CTC_PATH . '/' . CTC_CLASS_DIR . '/CTC_EDD_SL_Plugin_Updater.php';
		}

		// Loop add-ons
		foreach ( $add_ons as $add_on_dir => $add_on ) {

			// Supports licensing and updates?
			if ( ! ctc_edd_license_supported( $add_on_dir ) || empty( $add_on['updates'] ) ) {
				continue;
			}

			// License is active?
			if ( ! ctc_edd_license_active( $add_on_dir ) ) {
				continue;
			}

			// Get license key
			$license_key = ctc_edd_license_key( $add_on_dir );

			// Make sure we have the data necessary for using the updater class
			if (
				$license_key
				&& ! empty( $add_on['store_url'] )
				&& ! empty( $add_on['item_name'] )
				&& ! empty( $add_on['author'] )
			) {

				// Activate one-click updates
				$edd_updater = new CTC_EDD_SL_Plugin_Updater(
					$add_on['store_url'],						// Store URL running EDD with Software Licensing extension
					$add_on['plugin_file'],						// Full path to main plugin file (ie. __FILE__ in that file)
					array(
						'version' 	=> $add_on['version'], 		// Current version of the add-on plugin
						'license' 	=> $license_key, 			// The license key entered by user in plugin settings
						'item_name' => $add_on['item_name'], 	// The name of the add-on plugin (must match title of download in EDD exactly)
						'author' 	=> $add_on['author'],  		// The plugin author's name
					)
				);

			}

		}

	}

}

add_action( 'admin_init', 'ctc_edd_license_updater', 0 ); // after add-on registration on plugins_loaded, but before other things

/**
 * Prevent updating an add-on with another having same name on WordPress.org.
 *
 * This only affects Church Content add-ons that are using the EDD Software Licensing updater.
 *
 * Based on this:
 * https://wpscholar.com/blog/exclude-plugin-theme-from-wordpress-updates/ (updated)
 * https://markjaquith.wordpress.com/2009/12/14/excluding-your-plugin-or-theme-from-update-checks/ (old)
 *
 * @since 1.8
 * @param array $r Request data.
 * @param string $url Request URL.
 * @return array Modified request.
 */
function ctc_prevent_wporg_plugin_update( $r, $url ) {

	// Stop if it's not a plugin update request.
	if ( 0 !== strpos( $url, 'https://api.wordpress.org/plugins/update-check/' ) ) {
		return $r;
	}

	// Get add-ons and stop if don't have any.
	$add_ons = ctc_get_add_ons();
	if ( ! $add_ons ) {
		return $r;
	}

	// Require response with plugins.
	if ( ! isset( $r['body']['plugins'] ) ) {
		return $r;
	}

	// Decode the JSON response.
	$plugins = json_decode( $r['body']['plugins'], true ); // as array.

	// Have plugins array.
	if ( ! empty( $plugins ) && is_array( $plugins ) ) {

		// Loop to remove add-on plugins that use EDD Software Licensing from the check.
		foreach ( $add_ons as $add_on_dir => $add_on ) {

			// Skip if doesn't support licensing and updates.
			if ( ! ctc_edd_license_supported( $add_on_dir ) || empty( $add_on['updates'] ) ) {
				continue;
			}

			// Make sure have necessary data (prevent errors if response ever changes).
			if ( empty( $add_on['plugin_file'] ) || empty( $plugins['active'] ) || ! is_array( $plugins['active'] ) || empty( $plugins['plugins'] ) || ! is_array( $plugins['plugins'] ) ) {
				continue;
			}

			// Plugin basename.
			$plugin_basename = plugin_basename( $add_on['plugin_file'] );

			// Plugin key.
			$plugin_key = '';
			if ( $plugin_basename ) {
				$plugin_key  = array_search( $plugin_basename, $plugins['active'] );
			} else {
				continue; // basename and key required to continue.
			}

			// Remove add-on from the plugins object.
			if ( isset( $plugins['plugins'][ $plugin_basename ] ) ) {
				unset( $plugins['plugins'][ $plugin_basename ] );
			}

			// Remove add-on from the active plugins array.
			if ( isset( $plugins['active'][ $plugin_key ] ) ) {
				unset( $plugins['active'][ $plugin_key ] );
			}

		}

	}

	// Encode the updated JSON response.
	$r['body']['plugins'] = wp_json_encode( $plugins );

	// Return updated response.
	return $r;

}

add_filter( 'http_request_args', 'ctc_prevent_wporg_plugin_update', 5, 2 );
