<?php

declare(strict_types=1);

namespace MultipleChain\SolanaSDK\Tests\Unit;

use PHPUnit\Framework\TestCase;
use MultipleChain\SolanaSDK\Keypair;
use MultipleChain\SolanaSDK\PublicKey;
use MultipleChain\SolanaSDK\Util\Buffer;

class PublicKeyTest extends TestCase
{
    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_correctly_encodes_string_to_buffer(): void
    {
        $publicKey = new PublicKey('2ZC8EZduQGavJB9duMUgpdjNj7TQUiMawb52CLXBH5yc');

        $this->assertEquals([
            // @phpcs:ignore
            23, 26, 218, 1, 26, 7, 253, 202, 19, 162, 251, 121, 172, 0, 65, 219, 142, 20, 252, 217, 6, 150, 142, 0, 54, 146, 245, 140, 155, 194, 42, 131,
        ], $publicKey->toBuffer()->toArray());

        $this->assertEquals('2ZC8EZduQGavJB9duMUgpdjNj7TQUiMawb52CLXBH5yc', $publicKey->toBase58());
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_correctly_evaluates_equality(): void
    {
        $publicKey1 = new PublicKey('2ZC8EZduQGavJB9duMUgpdjNj7TQUiMawb52CLXBH5yc');
        $publicKey2 = new PublicKey('2ZC8EZduQGavJB9duMUgpdjNj7TQUiMawb52CLXBH5yc');

        $this->assertEquals($publicKey1, $publicKey2);
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_correctly_handles_public_key_in_constructor(): void
    {
        $publicKey1 = new PublicKey('2ZC8EZduQGavJB9duMUgpdjNj7TQUiMawb52CLXBH5yc');
        $publicKey2 = new PublicKey($publicKey1->toBinaryString());

        $this->assertEquals($publicKey1, $publicKey2);
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_equals(): void
    {
        $arrayKey = new PublicKey([
            3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        ]);

        $base58Key = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');

        $this->assertEquals($base58Key, $arrayKey);
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_toBase58(): void
    {
        $key1 = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
        $this->assertEquals('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3', $key1->toBase58());
        $this->assertEquals('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3', $key1);

        $key2 = new PublicKey('1111111111111111111111111111BukQL');
        $this->assertEquals('1111111111111111111111111111BukQL', $key2->toBase58());
        $this->assertEquals('1111111111111111111111111111BukQL', $key2);

        $key3 = new PublicKey('11111111111111111111111111111111');
        $this->assertEquals('11111111111111111111111111111111', $key3->toBase58());

        $key4 = new PublicKey([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        ]);
        $this->assertEquals('11111111111111111111111111111111', $key4->toBase58());
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_createWithSeed(): void
    {
        $defaultPublicKey = new PublicKey('11111111111111111111111111111111');
        $derivedKey = PublicKey::createWithSeed($defaultPublicKey, 'limber chicken: 4/45', $defaultPublicKey);

        $this->assertEquals(new PublicKey('9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq'), $derivedKey);
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_createProgramAddress(): void
    {
        $programId = new PublicKey('BPFLoader1111111111111111111111111111111111');
        $publicKey = new PublicKey('SeedPubey1111111111111111111111111111111111');

        $programAddress = PublicKey::createProgramAddress([
            '',
            [1],
        ], $programId);
        $this->assertEquals(new PublicKey('3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT'), $programAddress);

        $programAddress = PublicKey::createProgramAddress([
            '☉',
        ], $programId);
        $this->assertEquals(new PublicKey('7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7'), $programAddress);

        $programAddress = PublicKey::createProgramAddress([
            'Talking',
            'Squirrels',
        ], $programId);
        $this->assertEquals(new PublicKey('HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds'), $programAddress);

        $programAddress = PublicKey::createProgramAddress([
            $publicKey->toBytes(),
        ], $programId);
        $this->assertEquals(new PublicKey('GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K'), $programAddress);
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_findProgramAddress(): void
    {
        $programId = new PublicKey('BPFLoader1111111111111111111111111111111111');

        list($programAddress, $nonce) = PublicKey::findProgramAddress(
            [Buffer::from('')],
            $programId
        );

        $this->assertEquals(
            PublicKey::createProgramAddress([
                Buffer::from(''),
                [$nonce],
            ], $programId),
            $programAddress
        );
    }

    /**
     * @test
     * @return void
     */
    // @phpcs:ignore
    public function it_isOnCurve(): void
    {
        $onCurvePublicKey = Keypair::generate()->getPublicKey();
        $this->assertTrue(PublicKey::isOnCurve($onCurvePublicKey));

        // A program address, yanked from one of the above tests. This is a pretty
        // poor test vector since it was created by the same code it is testing.
        // Unfortunately, I've been unable to find a golden negative example input
        // for curve25519 point decompression :/
        $offCurve = new PublicKey('12rqwuEgBYiGhBrDJStCiqEtzQpTTiZbh7teNVLuYcFA');
        $this->assertFalse(PublicKey::isOnCurve($offCurve));

        /**
         * @see https://github.com/multiplechain/solana-sdk/issues/31
         */
        $onCurve = new PublicKey('q5xfHVuAsGmKRTmJNshERHHkVnHKxRX3cKzz5cYnFRn');
        $this->assertTrue(PublicKey::isOnCurve($onCurve));
    }
}
