<?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 Exception;

/** Exit if accessed directly. */
if ( ! defined( 'ABSPATH' ) ) {
    header( 'Status: 403 Forbidden' );
    header( 'HTTP/1.1 403 Forbidden' );
    exit;
}

/**
 * Class contain information about the envato item
 */
final class EnvatoItem
{
	public static string $host = 'https://s1.merkulove.host';

	/**
	 * Return transient key for Envato Item ID.
	 * @param string $for
	 * @return string
	 */
	public static function transient_key( string $for = '' ): string
	{
		switch ( $for ) {
			case 'id':
				return 'mdp_' . Plugin::get_slug() . '_envato_id';
			case 'info':
				return 'mdp_' . Plugin::get_slug() . '_plugin_info';
			case 'changelog':
				return 'mdp_' . Plugin::get_slug() . '_plugin_changelog';
			default:
				return '';
		}
	}

	/**
	 * Return CodeCanyon Item ID.
	 * @return int
	 **/
	public static function id(): int
	{
		return 30369205;

		$transient = get_transient(self::transient_key('id'));
		if ($transient) {
			return (int)$transient;
		}

		return 0;
	}

	/**
	 * Return cached plugin information.
	 * @return array
	 */
	public static function plugin_info(): array
	{
		$transient = get_transient(self::transient_key('info'));
		if (is_array($transient) && count($transient) > 0) {
			return $transient;
		}

		return [];
	}

	/**
	 * Return cached plugin changelog.
	 * @return array
	 */
	public static function plugin_changelog(): array
	{
		$transient = get_transient(self::transient_key('changelog'));
		if (is_array($transient) && count($transient) > 0) {
			return $transient;
		}

		return [];
	}

	/**
	 * Return CodeCanyon product ID.
	 * @return int
	 **/
	private static function request_remote_plugin_id(): int
	{
		// Get url to request item id
		$route = '/api/v2/get_id';
		$url = self::$host . $route . '?name=' . urlencode(Plugin::get_name());

		// Get Envato item ID
		$remote = wp_remote_get($url, [
			'headers' => self::get_headers($route),
			'sslverify' => ( Settings::get_instance()->options['check_ssl'] ?? 'off' ) === 'on',
		]);

		// Check for errors
		if (is_wp_error($remote)) {
			return 0;
		}

		// Get body
		$body = wp_remote_retrieve_body($remote);
		$response = json_decode($body, true);
		$errors = $response['errors'] ?? [];  // TODO: add logging

		// If code is not 200
		if (wp_remote_retrieve_response_code($remote) !== 200) {
			return 0;
		}

		$item_id = $response['data']['id'] ?? 0;

		return (int)$item_id;
	}

	/**
	 * Get plugin information from remote server.
	 * @return array
	 * @throws Exception
	 */
	private static function request_remote_plugin_info(): array
	{
		$envato_id = (int)READABLER['envato_id'];
		if (!$envato_id) {
			throw new Exception(Plugin::get_name() . ': ' . 'Envato ID is not set or invalid.');
		}

		// Get url to request item info
		$route = '/api/v2/product_info';
		$remote = wp_remote_post(
			EnvatoItem::$host . $route . '?t=' . time(),
			[
				'headers' => EnvatoItem::get_headers($route),
				'body' => [
					'id' => $envato_id,
					'plugin' => Plugin::get_slug(),
					'version' => Plugin::get_version(),
					'pid' => Plugin::get_purchase_code(),
					'domain' => site_url(),
				],
				'sslverify' => (Settings::get_instance()->options['check_ssl'] ?? 'off') === 'on'
			]
		);

		// Check for errors
		if (is_wp_error($remote)) {
			add_action('admin_notices', function () use ($remote) {
				$messages = [];
				foreach ($remote->get_error_messages() as $error) {
					$messages[] = $error;
				}
				throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin info. ' . implode(' ', $messages));
			});
		}

		// Check response status code
		if (wp_remote_retrieve_response_code($remote) !== 200) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin info. Response code is: ' . wp_remote_retrieve_response_code($remote));
		}

		// Decode the response
		$body = wp_remote_retrieve_body($remote);
		$remote_body = json_decode($body, true);
		if (!$remote_body) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin info. Response body is empty or not JSON.');
		}

		// Check if the response is successful
		if (!$remote_body['success']) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin info. ' . ($remote_body['message'] ?? 'Unknown error.'));
		}

		return $remote_body['data'] ?? [];
	}

	/**
	 * Get plugin changelog from remote server.
	 *
	 * @return array
	 * @throws Exception
	 */
	private static function request_remote_plugin_changelog(): array
	{
		// Get Envato item ID
		$envato_id = EnvatoItem::id();
		if ( ! $envato_id ) {
			throw new Exception( Plugin::get_name() . ': ' . 'Envato ID is not set or invalid.' );
		}

		// Get url to request item info
		$route = '/api/v2/changelog/' . Plugin::get_slug();
		$remote = wp_remote_get(
			EnvatoItem::$host . $route . '?t=' . time(),
			[
				'headers' => EnvatoItem::get_headers($route),
				'sslverify' => (Settings::get_instance()->options['check_ssl'] ?? 'off') === 'on'
			]
		);

		// Check for errors
		if (is_wp_error($remote)) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin changelog. ' . implode(' ', $remote->get_error_messages()));
		}

		// Check response status code
		if (wp_remote_retrieve_response_code($remote) !== 200) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin changelog. Response code is: ' . wp_remote_retrieve_response_code($remote));
		}

		// Decode the response
		$body = wp_remote_retrieve_body($remote);
		$remote_body = json_decode($body, true);
		if (!$remote_body) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin changelog. Response body is empty or not JSON.');
		}

		// Check if the response is successful
		if (!$remote_body['changelog']) {
			throw new Exception(Plugin::get_name() . ': ' . 'Can\'t get plugin changelog. ' . ($remote_body['message'] ?? 'Unknown error.'));
		}

		return $remote_body['changelog'];
	}

	/**
	 * Set transient with item id.
	 *
	 * @return int The item ID retrieved from the remote server.
	 */
	public static function get_remote_plugin_id(): int
	{
		$item_id = self::request_remote_plugin_id();
		if (!$item_id) {
			$item_id = 0;
		}
		set_transient(self::transient_key('id'), $item_id, MONTH_IN_SECONDS + HOUR_IN_SECONDS);

		return $item_id;
	}

	/**
	 * Get remote plugin information and cache it in a transient.
	 *
	 * @return array The plugin information retrieved from the remote server or an empty array on failure.
	 */
	public static function get_remote_plugin_info(): array
	{
		try {
			$plugin_info = self::request_remote_plugin_info();
			if ($plugin_info && count($plugin_info) > 0) {
				set_transient(self::transient_key('info'), $plugin_info, DAY_IN_SECONDS + HOUR_IN_SECONDS);
			}
			return $plugin_info;
		} catch (Exception $e) {
			error_log($e->getMessage());
			return [];
		}
	}

	/**
	 * Get remote plugin changelog and cache it in a transient.
	 *
	 * @return array The plugin changelog retrieved from the remote server or an empty array on failure.
	 */
	public static function get_remote_plugin_changelog(): array
	{
		try {
			$changelog = self::request_remote_plugin_changelog();
			if ($changelog && count($changelog) > 0) {
				set_transient(self::transient_key('changelog'), $changelog, DAY_IN_SECONDS + HOUR_IN_SECONDS);
			}
			return $changelog;
		} catch (Exception $e) {
			error_log($e->getMessage());
			return [];
		}
	}

	/**
	 * Generates and returns an array of headers for a given route.
	 *
	 * @param string $route The route for which headers are being generated.
	 * @return array An associative array representing the headers, including 'Accept', 'Authorization', and 'X-Timestamp'.
	 */
	public static function get_headers(string $route): array
	{
		if (empty($route)) {
			return [];
		}

		$timestamp = time();

		return [
			'Accept' => 'application/json',
			'Authorization' => hash_hmac('sha256', EnvatoItem::$host . $route, $timestamp),
			'X-Timestamp' => $timestamp,
		];
	}

}
