<?php
/**
 * Free/Pro gating (SPEC §9).
 *
 * @package EmberPrice
 */

namespace EmberPrice\Core;

defined( 'ABSPATH' ) || exit;

/**
 * Every Free/Pro capability check funnels through is_pro(), so swapping in
 * a real vendor SDK (Freemius/Envato -- channel not chosen yet) later never
 * touches call sites. Until that channel is chosen, activate_license()
 * accepts any plausibly-shaped key and stores it encrypted; it does NOT
 * validate the key against any server. This is a deliberate placeholder,
 * not a security boundary -- the admin screen that calls it already
 * requires manage_woocommerce + a valid nonce, same as every other
 * settings write. `embp_dev_is_pro` (a plain option, toggled manually or
 * via wp-cli) keeps working as an unconditional override, for
 * QA/staging/tests that need to force a state without going through
 * activation.
 */
final class LicenseManager {

	private const OPTION_DEV_IS_PRO  = 'embp_dev_is_pro';
	private const OPTION_LICENSE_KEY = 'embp_license_key';
	private const MIN_LICENSE_LENGTH = 8;

	/**
	 * Whether Pro features are unlocked: the dev override, or a stored
	 * (locally "activated") license.
	 */
	public static function is_pro(): bool {
		if ( (bool) get_option( self::OPTION_DEV_IS_PRO, false ) ) {
			return true;
		}

		return self::has_active_license();
	}

	/**
	 * Whether a license key is currently stored/active.
	 */
	public static function has_active_license(): bool {
		return null !== self::get_license_key();
	}

	/**
	 * Validates and stores a license key, encrypted at rest (same pattern
	 * as Settings::set_llm_api_key()). See the class docblock: this does
	 * not call out to any vendor API yet.
	 *
	 * @param string $license_key The key entered by the user.
	 *
	 * @return bool True if the key was plausible enough to store.
	 */
	public static function activate_license( string $license_key ): bool {
		$license_key = trim( $license_key );

		if ( mb_strlen( $license_key ) < self::MIN_LICENSE_LENGTH ) {
			return false;
		}

		update_option( self::OPTION_LICENSE_KEY, Crypto::for_site()->encrypt( $license_key ), false );

		return true;
	}

	/**
	 * Clears the stored license key.
	 */
	public static function deactivate_license(): void {
		delete_option( self::OPTION_LICENSE_KEY );
	}

	/**
	 * Decrypts and returns the stored license key, or null if none is set
	 * (or it can no longer be decrypted, e.g. the site's salts changed).
	 */
	public static function get_license_key(): ?string {
		$encrypted = get_option( self::OPTION_LICENSE_KEY, '' );

		if ( ! is_string( $encrypted ) || '' === $encrypted ) {
			return null;
		}

		try {
			return Crypto::for_site()->decrypt( $encrypted );
		} catch ( \RuntimeException $e ) {
			return null;
		}
	}

	/**
	 * The stored license key with all but the last 4 characters masked,
	 * for display on the settings screen -- never the raw key.
	 */
	public static function masked_license_key(): ?string {
		$key = self::get_license_key();

		if ( null === $key ) {
			return null;
		}

		$length = mb_strlen( $key );

		return $length <= 4 ? $key : str_repeat( '•', $length - 4 ) . mb_substr( $key, -4 );
	}
}
