<?php

declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_repository;
use git2\git_submodule;
use git2\git_submodule_ignore_t;
use git2\git_submodule_recurse_t;
use git2\git_submodule_status_t;
use git2\git_submodule_update_options;
use git2\git_submodule_update_t;

final class SubmoduleTest extends GitTestCase
{

	public function testAdd(): void
	{
		git::repository_open($repo, $dir = $this->createRepository('add'));
		$subDir = $this->createRepository('add_submodule');

		$this->assertOK(git::submodule_add_setup($submodule, $repo, $subDir, 'foo', false));
		$this->assertInstanceOf(git_submodule::class, $submodule);
		$this->assertSame($repo, git::submodule_owner($submodule));
		$this->assertNull(git::submodule_branch($submodule));

		$this->assertOK(git::submodule_update_options_init($opts, git_submodule_update_options::VERSION));
		$this->assertOK(git::submodule_clone($sRepo, $submodule, $opts));
		$this->assertOK(git::submodule_add_finalize($submodule));

		$this->assertFileExists($dir . '/.gitmodules');

		$this->assertOK(git::submodule_set_branch($repo, 'foo', 'master'));
		$this->assertOK(git::submodule_sync($submodule));

		$this->assertOK(git::submodule_add_to_index($submodule, true));

		git::repository_index($index, $repo);
		$this->assertSame(2, git::index_entrycount($index));

		$modulesFileEntry = git::index_get_bypath($index, '.gitmodules', 0);
		$this->assertNotNull($modulesFileEntry);

		$moduleEntry = git::index_get_bypath($index, 'foo', 0);
		$this->assertNotNull($moduleEntry);

		$this->assertSame(realpath($subDir), realpath(git::submodule_url($submodule)));
		$this->assertSame('foo', git::submodule_path($submodule));
		$this->assertOK(git::submodule_reload($submodule, false));
		$this->assertOK(git::submodule_status($status, $repo, 'foo', git_submodule_ignore_t::NONE));
		$this->assertSame(git_submodule_status_t::IN_INDEX | git_submodule_status_t::IN_CONFIG | git_submodule_status_t::IN_WD | git_submodule_status_t::INDEX_ADDED, $status);
		$this->assertSame('e30873a3517b3034fcf3e0f329560671c56a1f47', git::oid_tostr_s(git::submodule_wd_id($submodule)));
		$this->assertSame(git_submodule_update_t::CHECKOUT, git::submodule_update_strategy($submodule));
		$this->assertSame('foo', git::submodule_name($submodule));
		$this->assertNull(git::submodule_head_id($submodule));
		$this->assertSame(git_submodule_recurse_t::NO, git::submodule_fetch_recurse_submodules($submodule));
		$this->assertSame('e30873a3517b3034fcf3e0f329560671c56a1f47', git::oid_tostr_s(git::submodule_index_id($submodule)));
		$this->assertOK(git::submodule_location($locationStatus, $submodule));
		$this->assertSame(14, $locationStatus);
		$this->assertSame(git_submodule_ignore_t::NONE, git::submodule_ignore($submodule));
	}

	public function testDup(): void
	{
		git::repository_open($repo, $this->createRepository('dup'));
		$subDir = $this->createRepository('dup_submodule');
		git::submodule_add_setup($submodule, $repo, $subDir, 'foo', false);
		$this->assertOK(git::submodule_dup($dup, $submodule));
		$this->assertInstanceOf(git_submodule::class, $dup);
	}

	public function testForeach(): void
	{
		git::repository_open($repo, $this->createRepository('foreach'));
		$subDir = $this->createRepository('foreach_submodule');
		git::submodule_add_setup($submodule, $repo, $subDir, 'foo', false);

		$submodules = [];

		$this->assertOK(git::submodule_foreach($repo, function(git_submodule $submodule, string $name) use(&$submodules): void {
			$submodules[] = $name;
		}));

		$this->assertSame(['foo'], $submodules);
	}

	public function testInit(): void
	{
		git::repository_open($repo, $this->createRepository('init'));
		git::submodule_add_setup($submodule, $repo, $this->createRepository('init_submodule'), 'foo', false);
		$this->assertOK(git::submodule_init($submodule, false));
	}

	public function testLookup(): void
	{
		git::repository_open($repo, $this->createRepository('lookup'));
		git::submodule_add_setup($submodule, $repo, $this->createRepository('lookup_submodule'), 'foo', false);
		$this->assertOK(git::submodule_lookup($lookup, $repo, 'foo')); // TODO: Does this really returns a different pointer?
		$this->assertInstanceOf(git_submodule::class, $lookup);
	}

	public function testOpen(): void
	{
		git::repository_open($repo, $this->createRepository('open'));
		git::submodule_add_setup($submodule, $repo, $this->createRepository('open_submodule'), 'foo', false);
		$this->assertOK(git::submodule_open($subRepo, $submodule));
		$this->assertInstanceOf(git_repository::class, $subRepo);
	}

	public function testResolveUrl(): void
	{
		git::repository_open($repo, $this->createRepository('resolve_url'));
		$subDir = $this->createRepository('resolve_url_submodule');
		$this->assertOK(git::submodule_resolve_url($resolved, $repo, '../resolve_url_submodule'));
		$this->assertSame(realpath($subDir), realpath((string) $resolved));
	}

	public function testSet(): void
	{
		git::repository_open($repo, $this->createRepository('set'));
		git::submodule_add_setup($submodule, $repo, $subDir = $this->createRepository('set_submodule'), 'foo', false);

		$this->assertSame(git_submodule_recurse_t::NO, git::submodule_fetch_recurse_submodules($submodule));
		$this->assertSame(git_submodule_ignore_t::NONE, git::submodule_ignore($submodule));
		$this->assertSame(git_submodule_update_t::CHECKOUT, git::submodule_update_strategy($submodule));
		$this->assertSame(realpath($subDir), realpath(git::submodule_url($submodule)));

		$this->assertOK(git::submodule_set_fetch_recurse_submodules($repo, 'foo', git_submodule_recurse_t::YES));
		$this->assertOK(git::submodule_set_ignore($repo, 'foo', git_submodule_ignore_t::ALL));
		$this->assertOK(git::submodule_set_update($repo, 'foo', git_submodule_update_t::NONE));
		$this->assertOK(git::submodule_set_url($repo, 'foo', $subDir));

		git::submodule_lookup($submodule, $repo, 'foo');

		$this->assertSame(git_submodule_recurse_t::YES, git::submodule_fetch_recurse_submodules($submodule));
		$this->assertSame(git_submodule_ignore_t::ALL, git::submodule_ignore($submodule));
		$this->assertSame(git_submodule_update_t::NONE, git::submodule_update_strategy($submodule));
	}

	public function testUpdate(): void
	{
		git::repository_open($repo, $this->createRepository('update'));
		git::submodule_add_setup($submodule, $repo, $this->createRepository('update_submodule'), 'foo', false);
		git::submodule_update_options_init($opts, git_submodule_update_options::VERSION);
		git::submodule_clone($sRepo, $submodule, $opts);
		git::submodule_add_finalize($submodule);
		git::submodule_reload($submodule, false);
		$this->assertOK(git::submodule_update($submodule, false, null));
	}

	private function createRepository(string $name): string
	{
		$dir = $this->mkdir($name);
		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, $name, $tree);
		return $dir;
	}

}