<?php

namespace App\Helpers;

use App\Helpers\TronHelper;

class TronRawSerializer
{
    public static function serializeRawData(array $rawData): string
    {
        $bin = '';

        // timestamp (field number = 1, wire type = 0)
        $bin .= self::encodeKey(1, 0);
        $bin .= self::encodeVarint($rawData['timestamp']);
        

        // ref_block_bytes (2, wire type = 2)
        $bin .= self::encodeKey(2, 2);
        $refBlockBytes = hex2bin($rawData['ref_block_bytes']);
        $bin .= self::encodeLengthDelimited($refBlockBytes);
        

        // ref_block_hash (3, wire type = 2)
        $bin .= self::encodeKey(3, 2);
        $refBlockHash = hex2bin($rawData['ref_block_hash']);
        $bin .= self::encodeLengthDelimited($refBlockHash);
        

        // expiration (4, wire type = 0)
        $bin .= self::encodeKey(4, 0);
        $bin .= self::encodeVarint($rawData['expiration']);
        

        // contract (5, repeated embedded message)
        foreach ($rawData['contract'] as $contract) {
            $bin .= self::encodeKey(5, 2);
            $contractBin = self::serializeContract($contract);
            $bin .= self::encodeLengthDelimited($contractBin);
        }

        // fee_limit (18, wire type = 0) — optional
        if (isset($rawData['fee_limit'])) {
            $bin .= self::encodeKey(18, 0);
            $bin .= self::encodeVarint($rawData['fee_limit']);
        }

        return $bin;
    }

    protected static function serializeContract(array $contract): string
    {
        $bin = '';

        // type (1, wire type = 0)
        $bin .= self::encodeKey(1, 0);
        $bin .= self::encodeVarint($contract['type']);

        // parameter (2, embedded)
        $bin .= self::encodeKey(2, 2);
        $parameterBin = self::serializeParameter($contract['parameter']);
        $bin .= self::encodeLengthDelimited($parameterBin);

        return $bin;
    }

    protected static function serializeParameter(array $param): string
    {
        $bin = '';

        if (isset($param['type_url'])) {
            // type_url (1)
            $bin .= self::encodeKey(1, 2);
            $bin .= self::encodeLengthDelimited($param['type_url']);
        }

        if (isset($param['value'])) {
            // value (2)
            $bin .= self::encodeKey(2, 2);
            $valueBin = self::serializeTriggerSmartContract($param['value']);
            $bin .= self::encodeLengthDelimited($valueBin);
        }

        return $bin;
    }

    protected static function serializeTriggerSmartContract(array $value): string
    {
        $bin = '';

        // owner_address (1)
        $bin .= self::encodeKey(1, 2);
        $bin .= self::encodeLengthDelimited(hex2bin($value['owner_address']));
        

        // contract_address (2)
        $bin .= self::encodeKey(2, 2);
        $bin .= self::encodeLengthDelimited(hex2bin($value['contract_address']));
 
        // call_value (3) — not used in TRC20
        // data (4)
        if (isset($value['data'])) {
            $bin .= self::encodeKey(4, 2);
            $bin .= self::encodeLengthDelimited(hex2bin($value['data']));
        }

        return $bin;
    }

    protected static function encodeKey($fieldNumber, $wireType): string
    {
        return self::encodeVarint(($fieldNumber << 3) | $wireType);
    }

    protected static function encodeVarint($value): string
    {
        $value = is_numeric($value) ? (int)$value : 0;
        $bin = '';
        while (true) {
            if (($value & ~0x7F) === 0) {
                $bin .= chr($value);
                break;
            } else {
                $bin .= chr(($value & 0x7F) | 0x80);
                $value >>= 7;
            }
        }
        return $bin;
    }

    protected static function encodeLengthDelimited(string $data): string
    {
        return self::encodeVarint(strlen($data)) . $data;
    }
}
