<?php

declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_error_code;
use git2\git_object_t;
use git2\git_odb;
use git2\git_odb_backend;
use git2\git_odb_expand_id;
use git2\git_odb_object;
use git2\git_odb_stream;
use git2\git_oid;

final class ODBTest extends GitTestCase
{

	public function testNew(): void
	{
		$this->assertOK(git::odb_new($odb));
		$this->assertInstanceOf(git_odb::class, $odb);
		$this->assertSame(0, git::odb_num_backends($odb));
		$this->assertOK(git::odb_refresh($odb));
	}

	public function testOpen(): void
	{
		$dir = $this->mkdir('open');
		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::odb_open($odb, $dir . '/.git/objects'));
		$this->assertSame(2, git::odb_num_backends($odb));
	}

	public function testForeach(): void
	{
		$dir = $this->mkdir('open');
		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::odb_open($odb, $dir . '/.git/objects');

		$this->assertSame(1, git::odb_foreach($odb, function(git_oid $id): int {

			if (stripos(PHP_OS, 'win') === false) {
				$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s($id));
			} else {
				$this->assertSame('4b825dc642cb6eb9a060e54bf8d69288fbee4904', git::oid_tostr_s($id));
			}

			return 1;
		}));
	}

	public function testBackends(): void
	{
		$dir = $this->mkdir('open');
		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::odb_open($odb, $dir . '/.git/objects');

		$this->assertOK(git::odb_get_backend($backend, $odb, 0));
		$this->assertInstanceOf(git_odb_backend::class, $backend);

		$this->assertOK(git::odb_backend_loose($loose, $dir . '/bl', 0, false, 0644, 0644));
		$this->assertInstanceOf(git_odb_backend::class, $loose);

		$this->assertOK(git::odb_backend_pack($loose, $dir . '/bp'));
		$this->assertInstanceOf(git_odb_backend::class, $loose);

		$this->assertOK(git::odb_add_backend($odb, $loose, 3));
		$this->assertSame(3, git::odb_num_backends($odb));

		$this->assertSame(git_error_code::ERROR, git::odb_backend_one_pack($onepack, $dir . '/foo'));
		$this->assertNull($onepack);
	}

	public function testRead(): void
	{
		$dir = $this->mkdir('open');
		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::odb_open($odb, $dir . '/.git/objects');

		git::oid_fromstr($oid, 'fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b');
		$this->assertOK(git::odb_read($object, $odb, $oid));
		$this->assertInstanceOf(git_odb_object::class, $object);
		$this->assertSame(165, git::odb_object_size($object));

		$stream = git::odb_object_data($object);
		$this->assertIsResource($stream);
		$this->assertSame("tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n", fgets($stream));
		fclose($stream);

		$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s(git::odb_object_id($object)));
		$this->assertSame(git_object_t::COMMIT, git::odb_object_type($object));

		$this->assertOK(git::odb_read_header($len, $type, $odb, $oid));
		$this->assertSame(165, $len);
		$this->assertSame(git_object_t::COMMIT, $type);

		$this->assertOK(git::odb_read_prefix($objectPrefix, $odb, $oid, 7));
		$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s(git::odb_object_id($objectPrefix)));
	}

	public function testHash(): void
	{
		$this->assertOK(git::odb_hash($hash, 'foo', git_object_t::BLOB));
		$this->assertSame('19102815663d23f8b75a47e7a01965dcdc96468c', git::oid_tostr_s($hash));
	}

	public function testHashfile(): void
	{
		$dir = $this->mkdir('hashfile');
		file_put_contents($dir . '/foo', 'foo');
		$this->assertOK(git::odb_hashfile($hash, $dir . '/foo', git_object_t::BLOB));
		$this->assertSame('19102815663d23f8b75a47e7a01965dcdc96468c', git::oid_tostr_s($hash));
	}

	public function testExpandIds(): void
	{
		$dir = $this->mkdir('expand_ids');
		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($commit1, $repo, $id1);
		git::commit_create_v($id2, $repo, 'HEAD', $sig, $sig, null, 'bar', $tree, $commit1);
		git::odb_open($odb, $dir . '/.git/objects');

		git::oid_fromstrn($ids1, 'fe61c43');
		git::oid_fromstrn($ids2, 'ab80356');
		git::oid_fromstrn($ids3, 'f00000f');

		$arr = array_map(function(git_oid $oid): git_odb_expand_id {
			$exp = new git_odb_expand_id;
			$exp->id     = $oid;
			$exp->length = 7;
			$exp->type   = git_object_t::ANY;
			return $exp;
		}, [$ids1, $ids2, $ids3]);

		$this->assertOK(git::odb_expand_ids($odb, $arr));

		$this->assertSame('fe61c43f34fcc3bd7e9bfaa4b7739bf2be5aa05b', git::oid_tostr_s($arr[0]->id));
		$this->assertSame(git_oid::HEXSZ, $arr[0]->length);
		$this->assertSame(git_object_t::COMMIT, $arr[0]->type);

		$this->assertSame('ab80356c71a9cfe62bece74415763bd59bdcd7fc', git::oid_tostr_s($arr[1]->id));
		$this->assertSame(git_oid::HEXSZ, $arr[1]->length);
		$this->assertSame(git_object_t::COMMIT, $arr[1]->type);

		$this->assertNull($arr[2]);
	}

}