<?php
namespace Pix;

class PixGenerator {
    public static function gerarPixCode(string $nome, string $cidade, string $chave, ?float $valor = null): string {
        $nome = strtoupper($nome);
        $cidade = strtoupper($cidade);

        $payloadFormat = '000201';
        $merchantAccount = self::montarMerchantAccount($chave);
        $merchantCategoryCode = '52040000';
        $transactionCurrency = '5303986';
        $transactionAmount = $valor ? '54' . str_pad(strlen(number_format($valor, 2, '.', '')), 2, '0', STR_PAD_LEFT) . number_format($valor, 2, '.', '') : '';
        $countryCode = '5802BR';
        $merchantName = '59' . str_pad(strlen($nome), 2, '0', STR_PAD_LEFT) . $nome;
        $merchantCity = '60' . str_pad(strlen($cidade), 2, '0', STR_PAD_LEFT) . $cidade;
        $txid = '62070503***';

        $fullPix = $payloadFormat . $merchantAccount . $merchantCategoryCode . $transactionCurrency . $transactionAmount . $countryCode . $merchantName . $merchantCity . $txid;
        $fullPix .= '6304';
        return $fullPix . strtoupper(self::crc16($fullPix));
    }

    private static function montarMerchantAccount(string $chave): string {
        $gui = 'BR.GOV.BCB.PIX';
        $chaveLength = str_pad(strlen($chave), 2, '0', STR_PAD_LEFT);
        $value = '0014' . $gui . '01' . $chaveLength . $chave;
        return '26' . str_pad(strlen($value), 2, '0', STR_PAD_LEFT) . $value;
    }

    private static function crc16(string $payload): string {
        $polynom = 0x1021;
        $result = 0xFFFF;
        for ($offset = 0; $offset < strlen($payload); $offset++) {
            $result ^= (ord($payload[$offset]) << 8);
            for ($bitwise = 0; $bitwise < 8; $bitwise++) {
                if (($result <<= 1) & 0x10000) $result ^= $polynom;
                $result &= 0xFFFF;
            }
        }
        return strtoupper(str_pad(dechex($result), 4, '0', STR_PAD_LEFT));
    }
}
