<?php

namespace WPSN\Modules\Licensing;

use WPSN\Core\Secrets\EncryptedOption;

defined('ABSPATH') || exit;

/**
 * Stores license secrets separately from main settings so they are NOT exported
 * by Config Sync.
 */
final class LicenseSecretsRepository
{
    public const OPTION_NAME = 'wpsn_license_secrets';

    /**
     * @return array<string,mixed>
     */
    public function getAll(): array
    {
        return EncryptedOption::getArray(self::OPTION_NAME);
    }

    public function getLicenseKey(): string
    {
        $all = $this->getAll();
        $key = isset($all['license_key']) ? (string) $all['license_key'] : '';
        return trim($key);
    }

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

    public function setLicenseKey(string $key): void
    {
        $key = trim($key);
        $all = $this->getAll();
        $all['license_key'] = $key;
        EncryptedOption::updateArray(self::OPTION_NAME, $all);
    }

    public function clearLicenseKey(): void
    {
        $all = $this->getAll();
        unset($all['license_key']);
        EncryptedOption::updateArray(self::OPTION_NAME, $all);
    }

    public function getInstallId(): string
    {
        $all = $this->getAll();
        $installId = isset($all['install_id']) ? trim((string) $all['install_id']) : '';
        if ($installId === '') {
            $installId = function_exists('wp_generate_uuid4') ? (string) wp_generate_uuid4() : md5((string) microtime(true));
            $all['install_id'] = $installId;
            EncryptedOption::updateArray(self::OPTION_NAME, $all);
        }

        return $installId;
    }

    /**
     * @return array<string,string>
     */
    public function getServerBinding(): array
    {
        $all = $this->getAll();
        $binding = [];
        foreach (['site_id', 'account_id', 'client_id', 'request_secret', 'request_kid', 'contract_version'] as $key) {
            if (!isset($all[$key]) || !is_scalar($all[$key])) {
                continue;
            }
            $value = trim((string) $all[$key]);
            if ($value !== '') {
                $binding[$key] = $value;
            }
        }

        return $binding;
    }

    public function getRequestSecret(): string
    {
        $binding = $this->getServerBinding();
        return isset($binding['request_secret']) ? (string) $binding['request_secret'] : '';
    }

    public function getRequestKeyId(): string
    {
        $binding = $this->getServerBinding();
        return isset($binding['request_kid']) ? (string) $binding['request_kid'] : '';
    }

    /**
     * @param array<string,mixed> $binding
     */
    public function updateServerBinding(array $binding): void
    {
        $normalized = $this->normalizeServerBinding($binding);
        if (empty($normalized)) {
            return;
        }

        $all = $this->getAll();
        foreach ($normalized as $key => $value) {
            $all[$key] = $value;
        }

        EncryptedOption::updateArray(self::OPTION_NAME, $all);
    }

    public function clearServerBinding(): void
    {
        $all = $this->getAll();
        foreach (['site_id', 'account_id', 'client_id', 'request_secret', 'request_kid', 'contract_version'] as $key) {
            unset($all[$key]);
        }
        EncryptedOption::updateArray(self::OPTION_NAME, $all);
    }

    /**
     * @param array<string,mixed> $binding
     * @return array<string,string>
     */
    private function normalizeServerBinding(array $binding): array
    {
        $out = [];

        foreach (['site_id', 'account_id', 'client_id', 'request_kid'] as $key) {
            if (!isset($binding[$key]) || !is_scalar($binding[$key])) {
                continue;
            }
            $value = preg_replace('/[^A-Za-z0-9._:@\-]/', '', trim((string) $binding[$key]));
            $value = substr((string) $value, 0, 190);
            if ($value !== '') {
                $out[$key] = $value;
            }
        }

        if (isset($binding['request_secret']) && is_scalar($binding['request_secret'])) {
            $value = trim((string) $binding['request_secret']);
            $value = substr($value, 0, 4096);
            if ($value !== '') {
                $out['request_secret'] = $value;
            }
        }

        if (isset($binding['contract_version']) && is_scalar($binding['contract_version'])) {
            $value = trim((string) $binding['contract_version']);
            $value = substr($value, 0, 40);
            if ($value !== '') {
                $out['contract_version'] = $value;
            }
        }

        return $out;
    }
}
