<?php

declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_error_code;
use git2\git_object;
use git2\git_object_t;
use git2\git_reference;
use git2\git_reference_format_t;
use git2\git_reference_t;
use git2\git_repository;
use git2\git_strarray;

final class ReferenceTest extends GitTestCase
{

	public function testForeach(): void
	{
		$dir = $this->mkdir('foreach');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertSame(1, git::reference_foreach($repo, function(git_reference $reference): int {
			$this->assertSame('refs/heads/master', git::reference_name($reference));
			return 1;
		}));

		$this->assertSame(1, git::reference_foreach_glob($repo, 'refs/heads/*', function(string $name): int {
			$this->assertSame('refs/heads/master', $name);
			return 1;
		}));

		$this->assertOK(git::reference_foreach_glob($repo, 'refs/heads/foo', $this->fail(...)));

		$this->assertSame(1, git::reference_foreach_name($repo, function(string $name): int {
			$this->assertSame('refs/heads/master', $name);
			return 1;
		}));

		$this->assertOK(git::reference_foreach_name($repo, fn () => null));
	}

	public function testLookup(): void
	{
		$dir = $this->mkdir('lookup');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_lookup($ref, $repo, 'refs/heads/master'));
		$this->assertInstanceOf(git_reference::class, $ref);
		$this->assertTrue(git::reference_has_log($repo, 'refs/heads/master'));
		$this->assertTrue(git::reference_is_branch($ref));
		$this->assertFalse(git::reference_is_note($ref));
		$this->assertFalse(git::reference_is_remote($ref));
		$this->assertFalse(git::reference_is_tag($ref));

		$this->assertSame(0, git::reference_cmp($ref, $ref));

		$this->assertSame(git_reference_t::DIRECT, git::reference_type($ref));
		$this->assertInstanceOf(git_repository::class, git::reference_owner($ref));

		$this->assertOK(git::reference_name_to_id($id, $repo, 'refs/heads/master'));
		$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s($id));

		$this->assertOK(git::reference_peel($peeled, $ref, git_object_t::COMMIT));
		$this->assertInstanceOf(git_object::class, $peeled);
	}

	public function testNameIsValid(): void
	{
		$this->assertOK(git::reference_name_is_valid($valid, 'refs/heads/master'));
		$this->assertTrue($valid);
		$this->assertOK(git::reference_name_is_valid($valid, 'foo'));
		$this->assertFalse($valid);
	}

	public function testList(): void
	{
		$dir = $this->mkdir('list');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_list($list, $repo));
		$this->assertInstanceOf(git_strarray::class, $list);
		$this->assertSame(1, $list->count);
		$this->assertSame(1, count($list));
		$this->assertSame('refs/heads/master', $list[0]);
	}

	public function testCreate(): void
	{
		$dir = $this->mkdir('create');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_create($ref, $repo, 'refs/tags/foo', $id, false, null));
		$this->assertInstanceOf(git_reference::class, $ref);
		$this->assertOK(git::reference_dup($dup, $ref));
		$this->assertInstanceOf(git_reference::class, $dup);
		$this->assertSame('refs/tags/foo', git::reference_name($ref));
		$this->assertOK(git::reference_ensure_log($repo, 'refs/tags/foo'));

		$this->assertOK(git::reference_dwim($dwim, $repo, 'foo'));
		$this->assertInstanceOf(git_reference::class, $dwim);

		$this->assertOK(git::reference_delete($ref));
	}

	public function testIterator(): void
	{
		$dir = $this->mkdir('iterator');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_iterator_new($it, $repo));
		$this->assertOK(git::reference_next($ref1, $it));
		$this->assertSame('refs/heads/master', git::reference_name($ref1));
		$this->assertSame(git_error_code::ITEROVER, git::reference_next($_, $it));

		$this->assertOK(git::reference_iterator_glob_new($git, $repo, 'refs/heads/*'));
		$this->assertOK(git::reference_next_name($refname, $git));
		$this->assertSame('refs/heads/master', $refname);
		$this->assertSame(git_error_code::ITEROVER, git::reference_next_name($_, $git));
	}

	public function testNormalizeName(): void
	{
		$this->assertOK(git::reference_normalize_name($buf1, 'refs/foo/a', git_reference_format_t::NORMAL));
		$this->assertSame('refs/foo/a', (string) $buf1);

		$this->assertOK(git::reference_normalize_name($buf2, 'refs//foo//a', git_reference_format_t::NORMAL));
		$this->assertSame('refs/foo/a', (string) $buf2);
	}

	public function testRename(): void
	{
		$dir = $this->mkdir('rename');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_lookup($master, $repo, 'refs/heads/master'));
		$this->assertOK(git::reference_rename($foo, $master, 'refs/heads/foo', false, null));
		$this->assertSame('refs/heads/foo', git::reference_name($foo));
	}

	public function testResolve(): void
	{
		$dir = $this->mkdir('resolve');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);

		$this->assertOK(git::reference_symbolic_create($sym, $repo, 'refs/symref', 'refs/heads/master', false, null));
		$this->assertSame('refs/heads/master', git::reference_symbolic_target($sym));
		$this->assertOK(git::reference_resolve($master, $sym));
		$this->assertSame('master', git::reference_shorthand($master));
		$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s(git::reference_target($master)));
		$this->assertNull(git::reference_target_peel($master));

		$this->assertOK(git::reference_remove($repo, 'refs/symref'));
	}

	public function testSetTarget(): void
	{
		$dir = $this->mkdir('set_target');
		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', 1640263000, 60);
		git::commit_create_v($id1, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);
		git::commit_lookup($commit, $repo, $id1);
		git::commit_create_v($id2, $repo, 'HEAD', $sig, $sig, null, 'bar', $tree, $commit);

		git::repository_head($head, $repo);
		$this->assertOK(git::reference_set_target($prev, $head, $id1, null));
		$this->assertInstanceOf(git_reference::class, $prev);
	}

	public function testSymbolicSetTarget(): void
	{
		$dir = $this->mkdir('symbolic_set_target');
		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', 1640263000, 60);
		git::commit_create_v($id, $repo, 'HEAD', $sig, $sig, null, 'foo', $tree);
		git::reference_symbolic_create($sym, $repo, 'refs/symref', 'refs/heads/master', false, null);

		$this->assertOK(git::reference_symbolic_set_target($sym2, $sym, 'refs/heads/master', null));
		$this->assertInstanceOf(git_reference::class, $sym2);
	}

}