<?php

declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_error_code;
use git2\git_oid;
use git2\git_repository;
use git2\git_revwalk;
use git2\git_sort_t;

final class RevwalkTest extends GitTestCase
{

	public function testDefault(): void
	{
		$walk = $this->revwalk();
		$this->assertOK(git::revwalk_push_head($walk));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('c3b123afeef0b6c82d2e159e628678ca1522a429', git::oid_tostr_s($oid));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('a9e84f3e05a65e6b68ec4a6773e2e18d5d1d1a37', git::oid_tostr_s($oid));

		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testAddHideCB(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);

		$this->assertOK(git::revwalk_add_hide_cb($walk, function(git_oid $id): int {

			if (git::oid_tostr_s($id) === '6a4300ffe0eb59e6262a2474364e9ae514d0f0b7') {
				return 0;
			}

			return 1;

		}));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));

		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testHide(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);

		git::oid_fromstr($id, 'c3b123afeef0b6c82d2e159e628678ca1522a429');
		$this->assertOK(git::revwalk_hide($walk, $id));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));

		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testHideGlob(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);
		$this->assertOK(git::revwalk_hide_glob($walk, '*'));
		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testHideHead(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);
		$this->assertOK(git::revwalk_hide_head($walk));
		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testHideRef(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);
		$this->assertOK(git::revwalk_hide_ref($walk, 'HEAD'));
		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testPush(): void
	{
		$walk = $this->revwalk();
		git::oid_fromstr($id, 'a9e84f3e05a65e6b68ec4a6773e2e18d5d1d1a37');
		git::revwalk_push($walk, $id);

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('a9e84f3e05a65e6b68ec4a6773e2e18d5d1d1a37', git::oid_tostr_s($oid));

		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testPushGlob(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_glob($walk, '*');

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));
	}

	public function testPushRange(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_range($walk, '6a4300f~1..6a4300f');

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));

		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($oid, $walk));
	}

	public function testPushRef(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_ref($walk, 'HEAD');

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));
	}

	public function testRepository(): void
	{
		$dir = $this->mkdir($this->getName());
		git::repository_init($repo, $dir, false);
		git::revwalk_new($walk, $repo);
		$this->assertSame($repo, git::revwalk_repository($walk));
	}

	public function testReset(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));

		$this->assertOK(git::revwalk_reset($walk));
		$this->assertSame(git_error_code::ITEROVER, git::revwalk_next($_, $walk));
	}

	public function testSorting(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);
		$this->assertOK(git::revwalk_sorting($walk, git_sort_t::REVERSE | git_sort_t::TOPOLOGICAL));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('a9e84f3e05a65e6b68ec4a6773e2e18d5d1d1a37', git::oid_tostr_s($oid));
	}

	public function testSimplifyFirstParent(): void
	{
		$walk = $this->revwalk();
		git::revwalk_push_head($walk);
		$this->assertOK(git::revwalk_simplify_first_parent($walk));

		$this->assertOK(git::revwalk_next($oid, $walk));
		$this->assertInstanceOf(git_oid::class, $oid);
		$this->assertSame('6a4300ffe0eb59e6262a2474364e9ae514d0f0b7', git::oid_tostr_s($oid));
	}

	private function revwalk(): git_revwalk
	{
		$dir = $this->mkdir($this->getName());
		git::repository_init($repo, $dir, false);
		git::repository_index($index, $repo);
		git::index_write_tree($treeId, $index);
		git::tree_lookup($tree, $repo, $treeId);
		git::signature_new($sig, 'John Doe', 'john.doe@example.com', 1640165957, 0);

		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'Hello world!', $tree);
		git::commit_lookup($commit, $repo, $id);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'Hello world!', $tree, $commit);
		git::commit_lookup($commit, $repo, $id);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'Hello world!', $tree, $commit);

		git::revwalk_new($walk, $repo);
		return $walk;
	}

}