<?php

namespace App\Repository;

use App\Controller\DatabaseConnection;
use App\Controller\DomainController;
use App\Entity\Domain;
use DI\Container;
use DI\ContainerBuilder;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use function DI\autowire;

/**
 *
 */
class DomainRepositoryTest extends TestCase
{
	private Container $container;
	private DomainRepository $domainRepository;
	private DomainController $domainController;
	
	private string $localZoneFile;
	private string $localZonesDir;
	private string $namedConfLocalFile;
	
	private Logger $log;
	
	/**
	 * @param int|string $dataName
	 *
	 * @throws \Exception
	 * @internal This method is not covered by the backward compatibility promise for PHPUnit
	 */
	public function __construct(?string $name = null, array $data = [], $dataName = '')
	{
		parent::__construct(name: $name, data: $data, dataName: $dataName);
		
		$dateFormat = "Y-m-d H:i:s";
		$output = "%datetime% %channel%.%level_name% %message%\n"; // %context% %extra%
		$formatter = new LineFormatter(format: $output, dateFormat: $dateFormat);
		
		$stream = new StreamHandler(stream: dirname(path: __DIR__, levels: 3) . '/bindAPI.test.log');
		$stream->setFormatter(formatter: $formatter);
		
		$this->log = new Logger(name: 'bindAPI');
		$this->log->pushHandler(handler: $stream);
		
		$this->localZoneFile = '/etc/bind/local.zones';
		$this->localZonesDir = '/etc/bind/zones/';
		$this->namedConfLocalFile = '/etc/bind/named.conf.local';
		//$this->zoneCachePath = '/var/cache/bind/';
		
		// read config TODO use .env file instead?
		$configFile = dirname(path: __DIR__, levels: 3) . "/config.json";
		$configJSON = file_get_contents(filename: $configFile);
		$config = json_decode(json: $configJSON, associative: true);
		
		$containerBuilder = new ContainerBuilder();
		$containerBuilder->addDefinitions([
			DatabaseConnection::class => autowire()->constructorParameter(parameter: 'config', value: $config),
			DomainController::class   => autowire()
				->constructorParameter(parameter: 'config', value: $config)
				->constructorParameter(parameter: 'log', value: $this->log),
			DomainRepository::class   => autowire()
				->constructorParameter(parameter: 'config', value: $config)
				->constructorParameter(parameter: 'log', value: $this->log),
		
		]);
		$this->container = $containerBuilder->build();
		
		$this->domainRepository = $this->container->get(name: DomainRepository::class);
		$this->domainController = $this->container->get(name: DomainController::class);
	}
	
	
	public function setUp(): void
	{
		$this->log->info(message: 'Started DomainRepositoryTest');
	}
	
	public function tearDown(): void
	{
		$this->log->info(message: 'Finished DomainRepositoryTest');
	}
	
	/**
	 * @throws \DI\NotFoundException
	 * @throws \DI\DependencyException
	 */
	public function testInsert()
	{
		$domain = new Domain(name: 'inserttest.org', a: '1.2.3.4', aaaa: '1bad::babe');
		$this->domainRepository->insert(domain: $domain);
		$this->domainController->createZoneFile(domain: $domain);
		
		// now get the persisted domain with id
		$domainTest = $this->domainRepository->findByName(name: 'inserttest.org');
		
		$this->assertIsNotBool(actual: $domainTest);
		$this->assertEquals(expected: 'inserttest.org', actual: $domainTest->getName());
		
		
		if ($namedConfLocal = file_get_contents(filename: $this->namedConfLocalFile)) {
			$this->assertStringContainsString(needle: $this->localZoneFile, haystack: $namedConfLocal);
		} else {
			$this->fail(message: 'No permissions: ' . $this->namedConfLocalFile);
		}
		
		$this->assertNotFalse(condition: fileperms(filename: $this->localZoneFile));
		
		$localZones = file_get_contents(filename: $this->localZoneFile);
		$this->assertStringContainsString(needle: $domainTest->getName(), haystack: $localZones);

		$zoneFile = $this->localZonesDir . $domain->getName();
		
		$this->assertFileExists(filename: $zoneFile);
		
		
		// clean up
		$this->domainRepository->delete(domain: $domainTest);
	}
	
	
	/**
	 * @throws \DI\NotFoundException
	 * @throws \DI\DependencyException
	 */
	public function testDelete()
	{
		$domain = new Domain(name: 'inserttest.org', a: '1.2.3.4', aaaa: '1bad::babe');
		$this->domainRepository->insert(domain: $domain);
		$this->domainController->createZoneFile(domain: $domain);
		
		$domainTest = $this->domainRepository->findByName(name: 'inserttest.org');
		$this->assertIsNotBool(actual: $domainTest);
		$this->assertEquals(expected: 'inserttest.org', actual: $domainTest->getName());
		
		// domain is valid and created
		
		// now delete and check for cleanup
		$this->domainRepository->delete(domain: $domainTest);
		
		$this->domainController->deleteZone(domain: $domainTest);
		
		// check zone is removed
		
		$this->assertNotFalse(condition: fileperms(filename: $this->localZoneFile));
		
		$localZones = file_get_contents(filename: $this->localZoneFile);
		$this->assertStringNotContainsString(needle: $domainTest->getName(), haystack: $localZones);
		
		$zoneFile = $this->localZonesDir . $domain->getName();
		
		$this->assertFileDoesNotExist(filename: $zoneFile);
		
	}
	
}