<?php

declare(strict_types=1);

namespace Signocore\Multilanguage\Application;

if (!defined('ABSPATH')) {
	exit;
}

use Signocore\Multilanguage\Infrastructure\Traits\Singleton;

final class LicenseManager
{
	use Singleton;

	private const TRANSIENT_KEY = Constants::TRANSIENT_PREFIX . 'entitlements';

	private const OPTION_KEY = Constants::OPTION_PREFIX . 'entitlements_fallback';

	private const TTL = 12 * HOUR_IN_SECONDS;

	/**
	 * Ed25519 public key for verifying entitlements signatures.
	 * The corresponding private key is on the update server.
	 */
	private const PUBLIC_KEY = 'QEx8aX0l7YAAYTjc2V+umAW96WvU21EidCD6HZWKeeA=';

	/** @var array{plan: string, product: string, site_url: string}|null */
	private ?array $entitlements = null;

	/** @var array{entitlements: array{plan: string, product: string, site_url: string}, signature: string, cached_at: int}|null */
	private ?array $cachedData = null;

	private ?bool $signatureValid = null;

	protected function init(): void
	{
		$this->cachedData = $this->loadCachedData();
		$this->entitlements = $this->cachedData['entitlements'] ?? null;
	}

	/**
	 * Check if a specific pro feature is available.
	 */
	public function can(string $feature): bool
	{
		if ($this->getPlan() !== 'pro') {
			return false;
		}

		return in_array($feature, Entitlements::PRO_FEATURES, true);
	}

	/**
	 * Get the current plan: 'pro' or 'free'.
	 */
	public function getPlan(): string
	{
		if ($this->entitlements === null) {
			return 'free';
		}

		if (!$this->isSignatureValid()) {
			return 'free';
		}

		if (!$this->isSiteUrlValid()) {
			return 'free';
		}

		return $this->entitlements['plan'] === 'pro' ? 'pro' : 'free';
	}

	public function isPro(): bool
	{
		return $this->getPlan() === 'pro';
	}

	/**
	 * Check if the current free user is eligible for a discount promotion.
	 */
	public static function isDiscountEligible(): bool
	{
		if (self::load()->isPro()) {
			return false;
		}

		$installedAt = (int) get_option(Constants::OPTION_PREFIX . 'installed_at', 0);
		if ($installedAt === 0) {
			return false;
		}

		return (time() - $installedAt) >= DAY_IN_SECONDS;
	}

	/**
	 * Get the upgrade URL, with coupon parameter when eligible.
	 */
	public static function getUpgradeUrl(): string
	{
		$url = 'https://signocore.com/wordpress-plugins/signocore-multilanguage/';
		if (self::isDiscountEligible()) {
			$url .= '?coupon=UPGRADE30';
		}

		return $url;
	}

	/**
	 * Store entitlements received from the update server.
	 *
	 * @param array{plan: string, product: string, site_url: string} $entitlements
	 */
	public function storeEntitlements(array $entitlements, string $signature): void
	{
		$data = [
			'entitlements' => $entitlements,
			'signature' => $signature,
			'cached_at' => time(),
		];

		set_site_transient(self::TRANSIENT_KEY, $data, self::TTL);
		update_option(self::OPTION_KEY, $data, false);

		$this->cachedData = $data;
		$this->entitlements = $entitlements;
		$this->signatureValid = null;
	}

	/**
	 * Clear cached entitlements but keep persistent fallback.
	 */
	public function invalidate(): void
	{
		delete_site_transient(self::TRANSIENT_KEY);
		$this->entitlements = null;
		$this->cachedData = null;
		$this->signatureValid = null;
	}

	/**
	 * Full wipe including persistent fallback.
	 */
	public function purge(): void
	{
		delete_site_transient(self::TRANSIENT_KEY);
		delete_option(self::OPTION_KEY);
		$this->cachedData = null;
		$this->entitlements = null;
		$this->signatureValid = null;
	}

	/**
	 * @return array{entitlements: array{plan: string, product: string, site_url: string}, signature: string, cached_at: int}|null
	 */
	private function loadCachedData(): ?array
	{
		$data = get_site_transient(self::TRANSIENT_KEY);
		if (is_array($data) && isset($data['entitlements'], $data['signature'])) {
			/** @var array{entitlements: array{plan: string, product: string, site_url: string}, signature: string, cached_at: int} $data */
			return $data;
		}

		$data = get_option(self::OPTION_KEY);
		if (is_array($data) && isset($data['entitlements'], $data['signature'])) {
			/** @var array{entitlements: array{plan: string, product: string, site_url: string}, signature: string, cached_at: int} $data */
			return $data;
		}

		return null;
	}

	private function isSignatureValid(): bool
	{
		if ($this->signatureValid !== null) {
			return $this->signatureValid;
		}

		if ($this->cachedData === null) {
			$this->signatureValid = false;
			return false;
		}

		$payload = json_encode($this->cachedData['entitlements']);
		if ($payload === false) {
			$this->signatureValid = false;
			return false;
		}

		$signature = base64_decode($this->cachedData['signature'], true);
		if ($signature === false || $signature === '') {
			$this->signatureValid = false;
			return false;
		}

		$publicKey = sodium_base642bin(self::PUBLIC_KEY, SODIUM_BASE64_VARIANT_ORIGINAL);
		if ($publicKey === '') {
			$this->signatureValid = false;
			return false;
		}

		try {
			$this->signatureValid = sodium_crypto_sign_verify_detached(
				$signature,
				$payload,
				$publicKey
			);
		} catch (\SodiumException) {
			$this->signatureValid = false;
		}

		return $this->signatureValid;
	}

	private function isSiteUrlValid(): bool
	{
		if ($this->entitlements === null) {
			return false;
		}

		$entitlementUrl = rtrim($this->entitlements['site_url'], '/');
		$homeUrl = rtrim((string) get_option('home'), '/');

		return strcasecmp($entitlementUrl, $homeUrl) === 0;
	}
}
