<?php
declare(strict_types=1);

require_once __DIR__ . '/plan_guard.php';

function golder_license_key_id(): string
{
    return 'main-ed25519-v1';
}

function golder_license_keypair(): array
{
    if (!function_exists('sodium_crypto_sign_seed_keypair')) {
        return ['public' => '', 'secret' => ''];
    }

    $seedInput = golder_env('GOLDER_LICENSE_SEED', 'golder-license-seed-v1');
    $seed = hash('sha256', $seedInput, true);
    $seed = substr($seed, 0, SODIUM_CRYPTO_SIGN_SEEDBYTES);
    $pair = sodium_crypto_sign_seed_keypair($seed);

    return [
        'public' => sodium_crypto_sign_publickey($pair),
        'secret' => sodium_crypto_sign_secretkey($pair),
    ];
}

function golder_license_public_key_b64(): string
{
    $kp = golder_license_keypair();
    $public = (string)($kp['public'] ?? '');
    return $public === '' ? '' : base64_encode($public);
}

function golder_license_hardware_binding_algorithm(): string
{
    return 'GolderHWID-v1';
}

function golder_license_hardware_binding_secret(): string
{
    return golder_env('GOLDER_HW_BINDING_KEY', 'golder-hardware-binding-v1');
}

function golder_license_hardware_binding_keys(): array
{
    $root = hash('sha256', golder_license_hardware_binding_secret(), true);

    return [
        'enc' => hash_hmac('sha256', 'enc', $root, true),
        'mac' => hash_hmac('sha256', 'mac', $root, true),
    ];
}

function golder_base64url_encode(string $data): string
{
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

function golder_license_hardware_binding_xor(string $data, string $key, string $nonce): string
{
    if ($data === '') {
        return '';
    }

    $stream = '';
    $counter = 0;
    while (strlen($stream) < strlen($data)) {
        $stream .= hash('sha256', $key . $nonce . pack('N', $counter), true);
        $counter++;
    }

    return $data ^ substr($stream, 0, strlen($data));
}

function golder_encrypt_license_hardware_binding(string $hardwareId): string
{
    $hardwareId = trim($hardwareId);
    if ($hardwareId === '') {
        return '';
    }

    $keys = golder_license_hardware_binding_keys();
    $header = 'ghb1';
    $nonce = random_bytes(16);
    $ciphertext = golder_license_hardware_binding_xor($hardwareId, (string)$keys['enc'], $nonce);
    $mac = hash_hmac('sha256', $header . $nonce . $ciphertext, (string)$keys['mac'], true);

    return golder_base64url_encode($header . $nonce . $ciphertext . $mac);
}

function golder_license_sign_payload(array $payload): array
{
    $json = golder_canonical_json($payload);
    $kp = golder_license_keypair();
    $secret = (string)($kp['secret'] ?? '');
    $signature = '';

    if ($secret !== '' && function_exists('sodium_crypto_sign_detached')) {
        $signature = base64_encode(sodium_crypto_sign_detached($json, $secret));
    }

    return [
        'payload' => $json,
        'signature' => $signature,
        'key_id' => golder_license_key_id(),
        'algorithm' => 'Ed25519',
        'public_key' => golder_license_public_key_b64(),
    ];
}

function golder_license_verify_payload(string $payloadJson, string $signatureB64, string $publicKeyB64 = ''): bool
{
    if ($payloadJson === '' || $signatureB64 === '') {
        return false;
    }
    if (!function_exists('sodium_crypto_sign_verify_detached')) {
        return false;
    }

    $signature = base64_decode($signatureB64, true);
    if (!is_string($signature) || $signature === '') {
        return false;
    }

    if ($publicKeyB64 === '') {
        $publicKeyB64 = golder_license_public_key_b64();
    }
    $public = base64_decode($publicKeyB64, true);
    if (!is_string($public) || $public === '') {
        return false;
    }

    return sodium_crypto_sign_verify_detached($signature, $payloadJson, $public);
}

function golder_license_payload_from_customer(array $customer, string $hardwareId = ''): array
{
    $effective = golder_customer_effective_plan($customer);
    $hardwareBinding = golder_encrypt_license_hardware_binding($hardwareId);

    return [
        'customer_id' => (int)($customer['id'] ?? 0),
        'username' => (string)($customer['username'] ?? ''),
        'plan_type' => (string)$effective['plan_type'],
        'max_pc_devices' => (int)$effective['max_pc_devices'],
        'max_web_sessions' => (int)$effective['max_web_sessions'],
        'desktop_expiry_date' => (string)($effective['desktop_expiry_date'] ?? ''),
        'sync_expiry_date' => (string)($effective['sync_expiry_date'] ?? ''),
        'expiry_date' => (string)($effective['expiry_date'] ?? ''),
        'license_status' => (string)($customer['license_status'] ?? 'active'),
        'sync_status' => (string)($effective['sync_status'] ?? 'disabled'),
        'web_mobile_enabled' => !empty($effective['web_mobile_enabled']) ? 1 : 0,
        'hardware_binding' => $hardwareBinding,
        'hardware_binding_algorithm' => $hardwareBinding !== '' ? golder_license_hardware_binding_algorithm() : '',
        'issued_at' => golder_utc_now(),
        'nonce' => bin2hex(random_bytes(8)),
    ];
}

function golder_issue_customer_license(PDO $master, array $customer, string $hardwareId = ''): array
{
    $payload = golder_license_payload_from_customer($customer, $hardwareId);
    $signed = golder_license_sign_payload($payload);

    $customerId = (int)($customer['id'] ?? 0);
    if ($customerId > 0) {
        try {
            $master->prepare(
                'UPDATE customers
                 SET license_key_id = :kid
                 WHERE id = :id'
            )->execute([':kid' => $signed['key_id'], ':id' => $customerId]);
        } catch (Throwable $_ignored) {
        }
    }

    return $signed;
}

function golder_ensure_license_public_keys_table(PDO $master): void
{
    $master->exec(
        "CREATE TABLE IF NOT EXISTS license_public_keys (
            key_id VARCHAR(80) PRIMARY KEY,
            algorithm VARCHAR(30) NOT NULL,
            public_key TEXT NOT NULL,
            is_active TINYINT(1) NOT NULL DEFAULT 1,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
    );

    $st = $master->prepare(
        'INSERT INTO license_public_keys (key_id, algorithm, public_key, is_active)
         VALUES (:id, :alg, :pub, 1)
         ON DUPLICATE KEY UPDATE algorithm = VALUES(algorithm), public_key = VALUES(public_key), is_active = VALUES(is_active)'
    );
    $st->execute([
        ':id' => golder_license_key_id(),
        ':alg' => 'Ed25519',
        ':pub' => golder_license_public_key_b64(),
    ]);
}


