<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Tests\ApplicationSystem;

use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystemInterface;
use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystems;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\EditionTask;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Exception\AmbiguousApplicationSystemException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ServiceLocator;

class ApplicationSystemsTest extends TestCase
{
    public function testOfferWithoutEditionReturnsFirstcomeSystem(): void
    {
        $firstcome = $this->createMock(ApplicationSystemInterface::class);

        $locator = $this->createMock(ServiceLocator::class);
        $locator->method('has')->with('firstcome')->willReturn(true);
        $locator->method('get')->with('firstcome')->willReturn($firstcome);

        $offer = $this->createMock(OfferInterface::class);
        $offer->method('getEdition')->willReturn(null);

        $systems = new ApplicationSystems($locator);

        $this->assertSame($firstcome, $systems->findApplicationSystem($offer));
    }

    public function testOfferWithoutEditionAndNoFirstcomeThrowsException(): void
    {
        $locator = $this->createMock(ServiceLocator::class);
        $locator->method('has')->with('firstcome')->willReturn(false);

        $offer = $this->createMock(OfferInterface::class);
        $offer->method('getEdition')->willReturn(null);

        $systems = new ApplicationSystems($locator);

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage('Application system "firstcome" is unknown');

        $systems->findApplicationSystem($offer);
    }

    public function testEditionWithNoActiveApplicationSystemTasksReturnsNull(): void
    {
        $locator = $this->createMock(ServiceLocator::class);

        $edition = $this->createMock(Edition::class);

        // Task with wrong type
        $task = $this->createMock(EditionTask::class);
        $task->method('getType')->willReturn('allocation');
        $task->method('getApplicationSystem')->willReturn(null);
        $task->method('isActive')->willReturn(true);

        $edition->method('getTasks')->willReturn(new \Doctrine\Common\Collections\ArrayCollection([$task]));

        $systems = new ApplicationSystems($locator);

        $this->assertNull($systems->findApplicationSystem($edition));
    }

    public function testEditionWithOneActiveApplicationSystemTaskReturnsSystem(): void
    {
        $task = $this->createMock(EditionTask::class);
        $task->method('getType')->willReturn('application_system');
        $task->method('getApplicationSystem')->willReturn('firstcome');
        $task->method('isActive')->willReturn(true);

        $systemWithTask = $this->createMock(ApplicationSystemInterface::class);

        $system = $this->createMock(ApplicationSystemInterface::class);
        $system->method('withTask')->with($task)->willReturn($systemWithTask);

        $locator = $this->createMock(ServiceLocator::class);
        $locator->method('get')->with('firstcome')->willReturn($system);

        $edition = $this->createMock(Edition::class);
        $edition->method('getTasks')->willReturn(new \Doctrine\Common\Collections\ArrayCollection([$task]));

        $systems = new ApplicationSystems($locator);

        $this->assertSame($systemWithTask, $systems->findApplicationSystem($edition));
    }

    public function testEditionWithMultipleActiveApplicationSystemTasksThrowsException(): void
    {
        $task1 = $this->createMock(EditionTask::class);
        $task1->method('getType')->willReturn('application_system');
        $task1->method('getApplicationSystem')->willReturn('firstcome');
        $task1->method('isActive')->willReturn(true);

        $task2 = $this->createMock(EditionTask::class);
        $task2->method('getType')->willReturn('application_system');
        $task2->method('getApplicationSystem')->willReturn('lot');
        $task2->method('isActive')->willReturn(true);

        $locator = $this->createMock(ServiceLocator::class);

        $edition = $this->createMock(Edition::class);
        $edition->method('getTasks')->willReturn(new \Doctrine\Common\Collections\ArrayCollection([$task1, $task2]));
        $edition->method('getId')->willReturn(42);

        $systems = new ApplicationSystems($locator);

        $this->expectException(AmbiguousApplicationSystemException::class);

        $systems->findApplicationSystem($edition);
    }

    public function testTaskWithNoApplicationSystemIsFilteredOut(): void
    {
        $task = $this->createMock(EditionTask::class);
        $task->method('getType')->willReturn('application_system');
        $task->method('getApplicationSystem')->willReturn(null);
        $task->method('isActive')->willReturn(true);

        $locator = $this->createMock(ServiceLocator::class);

        $edition = $this->createMock(Edition::class);
        $edition->method('getTasks')->willReturn(new \Doctrine\Common\Collections\ArrayCollection([$task]));

        $systems = new ApplicationSystems($locator);

        $this->assertNull($systems->findApplicationSystem($edition));
    }

    public function testInactiveTaskIsFilteredOut(): void
    {
        $task = $this->createMock(EditionTask::class);
        $task->method('getType')->willReturn('application_system');
        $task->method('getApplicationSystem')->willReturn('firstcome');
        $task->method('isActive')->willReturn(false);

        $locator = $this->createMock(ServiceLocator::class);

        $edition = $this->createMock(Edition::class);
        $edition->method('getTasks')->willReturn(new \Doctrine\Common\Collections\ArrayCollection([$task]));

        $systems = new ApplicationSystems($locator);

        $this->assertNull($systems->findApplicationSystem($edition));
    }
}
