<?php

namespace App\Tests\Controller;

use App\Controller\StatusPageController;
use App\Entity\Service;
use App\Entity\ServiceCategory;
use App\Repository\ServiceCategoryRepository;
use App\Repository\ServiceRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\Environment;

class StatusPageControllerTest extends TestCase
{
    private function addServiceToCategory(ServiceCategory $category, Service $service): void
    {
        $ref = new \ReflectionProperty(ServiceCategory::class, 'services');
        $collection = $ref->getValue($category);
        $collection->add($service);
    }

    private function createContainer(): ContainerInterface
    {
        $twig = $this->createStub(Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturn(true);
        $container->method('get')->willReturnMap([['twig', $twig]]);

        return $container;
    }

    private function createEm(): EntityManagerInterface
    {
        $msgRepo = $this->createStub(EntityRepository::class);
        $msgRepo->method('findBy')->willReturn([]);

        $query = $this->getMockBuilder(Query::class)
            ->disableOriginalConstructor()
            ->onlyMethods(['getResult', 'getSQL', 'execute'])
            ->getMock();
        $query->method('getResult')->willReturn([]);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('select')->willReturn($qb);
        $qb->method('from')->willReturn($qb);
        $qb->method('where')->willReturn($qb);
        $qb->method('andWhere')->willReturn($qb);
        $qb->method('setParameter')->willReturn($qb);
        $qb->method('orderBy')->willReturn($qb);
        $qb->method('getQuery')->willReturn($query);

        $em = $this->createStub(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($msgRepo);
        $em->method('createQueryBuilder')->willReturn($qb);

        return $em;
    }

    public function testIndexEmpty(): void
    {
        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([]);

        $msgRepo = $this->createStub(EntityRepository::class);
        $msgRepo->method('findBy')->willReturn([]);

        $em = $this->createStub(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($msgRepo);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $this->createStub(ServiceRepository::class), $em);
        $this->assertSame(200, $response->getStatusCode());
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testIndexWithUpService(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);
        $this->addServiceToCategory($category, $service);

        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([$category]);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getHistoryForDays')->willReturn([]);
        $svcRepo->method('getDailyStatus')->willReturn([]);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $svcRepo, $this->createEm());
        $this->assertSame(200, $response->getStatusCode());
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testIndexWithDownService(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Down', 'down', $category);
        $service->setStatus('down', 'Crash');
        $this->addServiceToCategory($category, $service);

        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([$category]);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getHistoryForDays')->willReturn([]);
        $svcRepo->method('getDailyStatus')->willReturn([]);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $svcRepo, $this->createEm());
        $this->assertSame(200, $response->getStatusCode());
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testIndexWithDegradedService(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Degraded', 'degraded', $category);
        $service->setStatus('degraded');
        $this->addServiceToCategory($category, $service);

        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([$category]);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getHistoryForDays')->willReturn([]);
        $svcRepo->method('getDailyStatus')->willReturn([]);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $svcRepo, $this->createEm());
        $this->assertSame(200, $response->getStatusCode());
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testIndexWithMaintenanceService(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Maint', 'maint', $category);
        $service->setStatus('maintenance');
        $this->addServiceToCategory($category, $service);

        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([$category]);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getHistoryForDays')->willReturn([]);
        $svcRepo->method('getDailyStatus')->willReturn([]);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $svcRepo, $this->createEm());
        $this->assertSame(200, $response->getStatusCode());
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testIndexMixedStatuses(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $up = new Service('Up', 'up', $category);
        $degraded = new Service('Degraded', 'degraded', $category);
        $degraded->setStatus('degraded');
        $down = new Service('Down', 'down', $category);
        $down->setStatus('down');
        $this->addServiceToCategory($category, $up);
        $this->addServiceToCategory($category, $degraded);
        $this->addServiceToCategory($category, $down);

        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('findBy')->willReturn([$category]);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getHistoryForDays')->willReturn([]);
        $svcRepo->method('getDailyStatus')->willReturn([]);

        $controller = new StatusPageController();
        $controller->setContainer($this->createContainer());

        $response = $controller->index($catRepo, $svcRepo, $this->createEm());
        $this->assertSame(200, $response->getStatusCode());
    }
}
