<?php

declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_config;
use git2\git_config_entry;
use git2\git_config_level_t;
use git2\git_error_code;
use git2\Internal\Debug\Call;
use git2\Internal\FFI;

final class ConfigTest extends GitTestCase
{

	public function testGetSetPrimitive(): void
	{
		$dir = $this->mkdir('get_set_primitive');
		git::repository_init($repo, $dir, false);

		$this->assertOK(git::repository_config($config, $repo));
		$this->assertInstanceOf(git_config::class, $config);

		$this->assertOK(git::config_set_bool($config, 'core.filemode', false));
		$this->assertOK(git::config_get_bool($coreFilemode, $config, 'core.filemode'));
		$this->assertFalse($coreFilemode);

		$this->assertOK(git::config_set_bool($config, 'core.filemode', true));
		$this->assertOK(git::config_get_bool($coreFilemode, $config, 'core.filemode'));
		$this->assertTrue($coreFilemode);

		$this->assertOK(git::config_set_int64($config, 'core.packedGitWindowSize', 1 << 40));
		$this->assertOK(git::config_get_int64($corePackedGitWindowSize, $config, 'core.packedGitWindowSize'));
		$this->assertSame(1 << 40, $corePackedGitWindowSize);

		$this->assertOK(git::config_set_int32($config, 'core.packedGitWindowSize', 1 << 20));
		$this->assertOK(git::config_get_int32($corePackedGitWindowSize, $config, 'core.packedGitWindowSize'));
		$this->assertSame(1 << 20, $corePackedGitWindowSize);

		$this->assertOK(git::config_set_string($config, 'core.eol', 'lf'));
		$this->assertOK(git::config_get_string_buf($coreEol1, $config, 'core.eol'));
		$this->assertSame('lf', (string) $coreEol1);

		$this->assertOK(git::config_set_string($config, 'core.eol', 'crlf'));
		$this->assertOK(git::config_get_string_buf($coreEol2, $config, 'core.eol'));
		$this->assertSame('crlf', (string) $coreEol2);

		$this->assertOK(git::config_snapshot($snapshot, $config));
		$this->assertOK(git::config_get_string($coreEolStr, $snapshot, 'core.eol'));
		$this->assertSame('crlf', $coreEolStr);

		$this->assertFileExists($dir . '/.git/config');
		$this->assertStringContainsString((string) (1 << 20), file_get_contents($dir . '/.git/config'));
	}

	public function testForeach(): void
	{
		$dir = $this->mkdir('foreach');
		git::repository_init($repo, $dir, false);
		git::repository_config($config, $repo);

		$this->assertSame(1, git::config_foreach($config, function(git_config_entry $entry): int {

			if ($entry->name !== 'core.bare') {
				return 0;
			}

			$this->assertSame('false', $entry->value);
			$this->assertSame(0, $entry->include_depth);
			$this->assertSame(git_config_level_t::LEVEL_LOCAL, $entry->level);

			return 1;

		}));

		$this->assertSame(1, git::config_foreach_match($config, 'core\.bare', function(git_config_entry $entry): int {

			$this->assertSame('core.bare', $entry->name);
			$this->assertSame('false', $entry->value);
			$this->assertSame(0, $entry->include_depth);
			$this->assertSame(git_config_level_t::LEVEL_LOCAL, $entry->level);

			return 1;

		}));
	}

	public function testParse(): void
	{
		$this->assertOK(git::config_parse_bool($true, 'yes'));
		$this->assertTrue($true);

		$this->assertOK(git::config_parse_bool($false, 'off'));
		$this->assertFalse($false);

		$this->assertSame(git_error_code::ERROR, git::config_parse_bool($_bool, 'foo'));

		$this->assertOK(git::config_parse_int32($int32, '5k'));
		$this->assertSame(1024 * 5, $int32);

		$this->assertSame(git_error_code::ERROR, git::config_parse_int32($_int32, 'foo'));

		$this->assertOK(git::config_parse_int64($int64, '5g'));
		$this->assertSame(5368709120, $int64);

		$this->assertSame(git_error_code::ERROR, git::config_parse_int64($_int64, 'foo'));
	}

	public function testAddFileOnDisk(): void
	{
		$filename = $this->mkdir('add_file_ondisk') . '/config';
		file_put_contents($filename, "[core]\npackedGitWindowSize = 123456");

		git::config_new($config);
		$this->assertOK(git::config_add_file_ondisk($config, $filename, git_config_level_t::LEVEL_LOCAL, null, false));
		$this->assertOK(git::config_get_int32($corePackedGitWindowSize, $config, 'core.packedGitWindowSize'));
		$this->assertSame(123456, $corePackedGitWindowSize);
	}

	public function testDelete(): void
	{
		git::config_new($config);
		git::config_add_file_ondisk($config, $this->mkdir('delete') . '/config', git_config_level_t::LEVEL_LOCAL, null, false);

		$this->assertSame(git_error_code::ENOTFOUND, git::config_get_bool($_, $config, 'core.filemode'));
		$this->assertOK(git::config_set_bool($config, 'core.filemode', false));
		$this->assertOK(git::config_get_bool($coreFilemode, $config, 'core.filemode'));
		$this->assertFalse($coreFilemode);
		$this->assertOK(git::config_delete_entry($config, 'core.filemode'));
		$this->assertSame(git_error_code::ENOTFOUND, git::config_get_bool($_, $config, 'core.filemode'));
	}

	public function testFindAndOpen(): void
	{
		git::config_new($config);

		$calls = [];

		FFI::debug(function(Call $call) use(&$calls): void {
			$calls[] = $call->function;
		});

		// This depends on the environment, so can't really check the result.
		git::config_find_global($_);
		git::config_find_programdata($_);
		git::config_find_system($_);
		git::config_find_xdg($_);

		unset($_);

		git::config_open_default($_);
		git::config_open_global($_, $config);
		git::config_open_level($_, $config, git_config_level_t::LEVEL_LOCAL);
		git::config_open_ondisk($_, $this->mkdir('find_and_open') . '/config');

		$this->assertSame([
			'git_config_find_global',
			'git_config_find_programdata',
			'git_config_find_system',
			'git_config_find_xdg',
			'git_config_open_default',
			'git_config_open_global',
			'git_config_open_level',
			'git_config_open_ondisk',
		], $calls);
	}

}