<?php

declare(strict_types=1);

namespace ShieldX\Licensing;

defined('ABSPATH') || exit;

use ShieldX\Core\RequestCache;
use ShieldX\Support\CryptoBox;

final class LicenseSettings
{
public const OPTION_NAME = 'shieldx_license_settings';
// Intentionally immutable and not filterable: license activation must use the vendor-controlled HTTPS endpoint.
public const SERVER_URL = 'https://xerotact.com';

    public const STATUS_INACTIVE = 'inactive';
    public const STATUS_ACTIVE = 'active';
    public const STATUS_EXPIRED = 'expired';
    public const STATUS_INVALID = 'invalid';
    public const STATUS_ERROR = 'error';

    /** @var array<string,mixed> */
    private $settings;

    /**
     * @param array<string,mixed> $settings
     */
    public function __construct(array $settings = [])
    {
        $this->settings = self::normalize($settings);
    }

    public static function fromWordPress(): self
    {
        $cached = RequestCache::get(self::class);
        if ($cached instanceof self) {
            return $cached;
        }

        $stored = function_exists('get_option') ? get_option(self::OPTION_NAME, []) : [];

        $instance = new self(is_array($stored) ? $stored : []);
        RequestCache::rememberOption(self::class, self::OPTION_NAME, $instance);

        return $instance;
    }

    /**
     * @return array<string,mixed>
     */
    public function all(): array
    {
        return $this->settings;
    }

    /**
     * @param array<string,mixed> $settings
     */
    public function save(array $settings): void
    {
        $merged = array_merge($this->settings, $settings);
        $this->settings = self::normalize($merged);

        if (function_exists('update_option')) {
            update_option(self::OPTION_NAME, $this->settings, false);
        }

        RequestCache::rememberOption(self::class, self::OPTION_NAME, $this);
    }

    public function licenseKey(): string
    {
        return self::decryptLicenseKey((string) ($this->settings['license_key'] ?? ''));
    }

    public function hasStoredKey(): bool
    {
        return $this->licenseKey() !== '';
    }

    public function status(): string
    {
        return self::sanitizeStatus((string) ($this->settings['status'] ?? self::STATUS_INACTIVE));
    }

    public function isActive(): bool
    {
        return $this->status() === self::STATUS_ACTIVE;
    }

    /**
     * @return array<string,mixed>
     */
    public function summary(): array
    {
        $status = $this->status();

        return [
            'status' => $status,
            'label' => self::statusLabel($status),
            'tone' => self::statusTone($status),
            'server_url' => self::SERVER_URL,
            'instance' => (string) ($this->settings['instance'] ?? ''),
            'masked_key' => (string) ($this->settings['license_key_mask'] ?? ''),
            'has_stored_key' => $this->hasStoredKey(),
            'expires' => (string) ($this->settings['expires'] ?? ''),
            'activation_limit' => (int) ($this->settings['activation_limit'] ?? 0),
            'activation_count' => (int) ($this->settings['activation_count'] ?? 0),
            'activation_remaining' => (int) ($this->settings['activation_remaining'] ?? 0),
            'activated_at' => (string) ($this->settings['activated_at'] ?? ''),
            'last_checked_at' => (string) ($this->settings['last_checked_at'] ?? ''),
            'last_message' => (string) ($this->settings['last_message'] ?? ''),
        ];
    }

    public static function currentInstance(): string
    {
        if (function_exists('home_url')) {
            $url = (string) home_url('/');
        } elseif (function_exists('site_url')) {
            $url = (string) site_url('/');
        } else {
            $url = '';
        }

        $url = trim($url);
        if ($url === '') {
            return '';
        }

        if (!function_exists('wp_parse_url')) {
            return rtrim($url, "/ \t\n\r\0\x0B");
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts) || empty($parts['host'])) {
            return rtrim($url, "/ \t\n\r\0\x0B");
        }

        $scheme = strtolower((string) ($parts['scheme'] ?? 'https'));
        $host = strtolower((string) $parts['host']);
        $port = isset($parts['port']) ? ':' . (string) ((int) $parts['port']) : '';
        $path = isset($parts['path']) ? '/' . trim((string) $parts['path'], '/') : '';

        return rtrim($scheme . '://' . $host . $port . $path, '/');
    }

    public static function now(): string
    {
        if (function_exists('current_time')) {
            $timestamp = strtotime((string) current_time('c', true));
            if ($timestamp !== false) {
                return gmdate('c', $timestamp);
            }
        }

        return gmdate('c');
    }

    public static function sanitizeLicenseKey(string $key): string
    {
        $key = trim($key);
        $key = function_exists('wp_strip_all_tags') ? wp_strip_all_tags($key) : (preg_replace('/<[^>]*>/', '', $key) ?: '');
        $key = preg_replace('/[\x00-\x1F\x7F]+/', '', $key) ?: '';
        $key = trim($key);

        return substr($key, 0, 191);
    }

    public static function protectLicenseKey(string $key): string
    {
        $key = self::sanitizeLicenseKey($key);
        if ($key === '') {
            return '';
        }

        try {
            return CryptoBox::protect($key, self::licenseEncryptionKey());
        } catch (\Throwable $error) {
            return '';
        }
    }

    public static function decryptLicenseKey(string $stored): string
    {
        $stored = trim($stored);
        if (!CryptoBox::isV2($stored)) {
            return '';
        }

        try {
            return self::sanitizeLicenseKey(CryptoBox::reveal($stored, self::licenseEncryptionKey()));
        } catch (\Throwable $error) {
            return '';
        }
    }

    public static function maskLicenseKey(string $key): string
    {
        $key = self::sanitizeLicenseKey($key);
        if ($key === '') {
            return '';
        }

        $tail = substr($key, -4);

        return '****-' . $tail;
    }

    public static function sanitizeStatus(string $status): string
    {
        $status = function_exists('sanitize_key') ? sanitize_key($status) : strtolower(preg_replace('/[^a-z0-9_\-]/', '', $status) ?: '');

        return in_array($status, [
            self::STATUS_INACTIVE,
            self::STATUS_ACTIVE,
            self::STATUS_EXPIRED,
            self::STATUS_INVALID,
            self::STATUS_ERROR,
        ], true) ? $status : self::STATUS_INACTIVE;
    }

    public static function statusLabel(string $status): string
    {
        $status = self::sanitizeStatus($status);
        if ($status === self::STATUS_ACTIVE) {
            return __('Activated', 'xerotact-shield-x-security');
        }

        if ($status === self::STATUS_EXPIRED) {
            return __('Expired', 'xerotact-shield-x-security');
        }

        if ($status === self::STATUS_INACTIVE) {
            return __('License inactive', 'xerotact-shield-x-security');
        }

        if ($status === self::STATUS_INVALID) {
            return __('License invalid', 'xerotact-shield-x-security');
        }

        if ($status === self::STATUS_ERROR) {
            return __('License check error', 'xerotact-shield-x-security');
        }

        return __('Check needed', 'xerotact-shield-x-security');
    }

    public static function statusTone(string $status): string
    {
        $status = self::sanitizeStatus($status);
        if ($status === self::STATUS_ACTIVE) {
            return 'good';
        }

        if ($status === self::STATUS_EXPIRED || $status === self::STATUS_INVALID) {
            return 'warn';
        }

        if ($status === self::STATUS_ERROR) {
            return 'error';
        }

        return 'idle';
    }

    /**
     * @param array<string,mixed> $settings
     * @return array<string,mixed>
     */
    private static function normalize(array $settings): array
    {
        $status = self::sanitizeStatus((string) ($settings['status'] ?? self::STATUS_INACTIVE));
        $licenseKey = (string) ($settings['license_key'] ?? '');
        $mask = self::sanitizeText((string) ($settings['license_key_mask'] ?? ''));

        return [
            'status' => $status,
            'server_url' => self::SERVER_URL,
            'instance' => self::sanitizeInstance((string) ($settings['instance'] ?? '')),
            'license_key' => self::isProtectedLicenseKey($licenseKey) ? $licenseKey : '',
            'license_key_mask' => $mask,
            'expires' => self::sanitizeDate((string) ($settings['expires'] ?? '')),
            'activation_limit' => max(0, (int) ($settings['activation_limit'] ?? 0)),
            'activation_count' => max(0, (int) ($settings['activation_count'] ?? 0)),
            'activation_remaining' => max(0, (int) ($settings['activation_remaining'] ?? 0)),
            'activated_at' => self::sanitizeDate((string) ($settings['activated_at'] ?? '')),
            'last_checked_at' => self::sanitizeDate((string) ($settings['last_checked_at'] ?? '')),
            'last_message' => self::sanitizeMessage((string) ($settings['last_message'] ?? '')),
        ];
    }

    private static function sanitizeInstance(string $instance): string
    {
        $instance = trim($instance);
        $instance = preg_replace('/[\x00-\x1F\x7F]+/', '', $instance) ?: '';

        return substr($instance, 0, 220);
    }

    private static function sanitizeDate(string $date): string
    {
        $date = trim($date);
        if ($date === '' || strtolower($date) === 'never') {
            return $date === '' ? '' : 'never';
        }

        return preg_match('/^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}:\d{2})?/', $date) === 1 ? substr($date, 0, 25) : '';
    }

    private static function sanitizeMessage(string $message): string
    {
        $message = self::sanitizeText($message);

        return substr($message, 0, 220);
    }

    private static function sanitizeText(string $text): string
    {
        $text = function_exists('wp_strip_all_tags') ? wp_strip_all_tags($text) : (preg_replace('/<[^>]*>/', '', $text) ?: '');
        $text = preg_replace('/[\x00-\x1F\x7F]+/', ' ', $text) ?: '';

        return trim($text);
    }

    private static function isProtectedLicenseKey(string $licenseKey): bool
    {
        return CryptoBox::isV2($licenseKey);
    }

    private static function licenseSalt(): string
    {
        $salt = function_exists('wp_salt') ? (string) wp_salt('secure_auth') : '';
        if ($salt === '') {
            throw new \RuntimeException('WordPress secure authentication salts are required for SHIELD-X license key storage.');
        }

        return $salt;
    }

    private static function licenseEncryptionKey(): string
    {
        return hash('sha256', self::licenseSalt(), true);
    }
}
