<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\StatusController;
use App\Entity\Service;
use App\Entity\ServiceCategory;
use App\Entity\ServiceMessage;
use App\Entity\User;
use App\Repository\ServiceCategoryRepository;
use App\Repository\ServiceRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\String\Slugger\AsciiSlugger;
use Symfony\Component\String\Slugger\SluggerInterface;
use Twig\Environment;

class StatusControllerTest extends TestCase
{
    private function createContainer(array $overrides = []): ContainerInterface
    {
        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

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

        $router = $this->createStub(RouterInterface::class);
        $router->method('generate')->willReturn('/admin/status');

        $defaults = [
            'twig' => $twig,
            'router' => $router,
            'security.authorization_checker' => $this->createStub(AuthorizationCheckerInterface::class),
            'security.token_storage' => $this->createStub(TokenStorageInterface::class),
            'request_stack' => $stack,
            'parameter_bag' => $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class),
        ];

        $services = array_merge($defaults, $overrides);

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturnCallback(fn($id) => isset($services[$id]));
        $container->method('get')->willReturnCallback(fn($id) => $services[$id] ?? null);

        return $container;
    }

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

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

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

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

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

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

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

    public function testIndexWithServices(): 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([]);
        $svcRepo->method('findBy')->willReturn([$service]);

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

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

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

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

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

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

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

    public function testCategoryCreateEmptyName(): void
    {
        $em = $this->createStub(EntityManagerInterface::class);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => '']);
        $request->setMethod('POST');

        $response = $controller->categoryCreate($request, $em, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testCategoryCreateSuccess(): void
    {
        $em = $this->createStub(EntityManagerInterface::class);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => 'New Category', 'position' => '1']);
        $request->setMethod('POST');

        $response = $controller->categoryCreate($request, $em, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testCategoryDelete(): void
    {
        $category = new ServiceCategory('ToDelete', 'to-delete');

        $em = $this->createStub(EntityManagerInterface::class);

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

        $response = $controller->categoryDelete($category, $em);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testServiceCreateEmptyName(): void
    {
        $em = $this->createStub(EntityManagerInterface::class);
        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => '', 'category_id' => '0']);
        $request->setMethod('POST');

        $response = $controller->serviceCreate($request, $em, $catRepo, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testServiceCreateCategoryNotFound(): void
    {
        $em = $this->createStub(EntityManagerInterface::class);
        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('find')->willReturn(null);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => 'TestService', 'category_id' => '99', 'url' => '', 'external_type' => '', 'position' => '0']);
        $request->setMethod('POST');

        $response = $controller->serviceCreate($request, $em, $catRepo, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testServiceCreateSuccess(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $em = $this->createStub(EntityManagerInterface::class);
        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('find')->willReturn($category);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => 'Esy-New', 'category_id' => '1', 'url' => 'https://esy.com', 'external_type' => '', 'position' => '5']);
        $request->setMethod('POST');

        $response = $controller->serviceCreate($request, $em, $catRepo, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testServiceCreateWithExternalType(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $em = $this->createStub(EntityManagerInterface::class);
        $catRepo = $this->createStub(ServiceCategoryRepository::class);
        $catRepo->method('find')->willReturn($category);
        $slugger = new AsciiSlugger();

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

        $request = new Request([], ['name' => 'External', 'category_id' => '1', 'url' => '', 'external_type' => 'http_check', 'position' => '0']);
        $request->setMethod('POST');

        $response = $controller->serviceCreate($request, $em, $catRepo, $slugger);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testServiceDelete(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('ToDelete', 'to-delete', $category);

        $em = $this->createStub(EntityManagerInterface::class);

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

        $response = $controller->serviceDelete($service, $em);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateValidStatus(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['status' => 'down', 'message' => 'Server crash']);
        $request->setMethod('POST');

        $response = $controller->update($service, $request, $em);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateInvalidStatus(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['status' => 'invalid_status', 'message' => '']);
        $request->setMethod('POST');

        $response = $controller->update($service, $request, $em);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateStatusWithEmptyMessage(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['status' => 'up', 'message' => '']);
        $request->setMethod('POST');

        $response = $controller->update($service, $request, $em);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testMessageCreateEmptyFields(): void
    {
        $svcRepo = $this->createStub(ServiceRepository::class);
        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['service_id' => '0', 'title' => '', 'content' => '', 'severity' => 'info']);
        $request->setMethod('POST');

        $response = $controller->messageCreate($request, $em, $svcRepo);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testMessageCreateServiceNotFound(): void
    {
        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('find')->willReturn(null);
        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['service_id' => '99', 'title' => 'Alert', 'content' => 'Something wrong', 'severity' => 'warning']);
        $request->setMethod('POST');

        $response = $controller->messageCreate($request, $em, $svcRepo);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testMessageCreateSuccessNoUser(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('find')->willReturn($service);
        $em = $this->createStub(EntityManagerInterface::class);

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

        $request = new Request([], ['service_id' => '1', 'title' => 'Maintenance', 'content' => 'Scheduled maintenance', 'severity' => 'info']);
        $request->setMethod('POST');

        $response = $controller->messageCreate($request, $em, $svcRepo);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testMessageCreateSuccessWithUser(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $user = new User();
        $user->setEmail('admin@test.com');
        $user->setFirstName('A');
        $user->setLastName('B');
        $user->setPassword('h');

        $token = $this->createStub(TokenInterface::class);
        $token->method('getUser')->willReturn($user);

        $tokenStorage = $this->createStub(TokenStorageInterface::class);
        $tokenStorage->method('getToken')->willReturn($token);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('find')->willReturn($service);
        $em = $this->createStub(EntityManagerInterface::class);

        $controller = new StatusController();
        $controller->setContainer($this->createContainer([
            'security.token_storage' => $tokenStorage,
        ]));

        $request = new Request([], ['service_id' => '1', 'title' => 'Incident', 'content' => 'Server down', 'severity' => 'critical']);
        $request->setMethod('POST');

        $response = $controller->messageCreate($request, $em, $svcRepo);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testMessageResolve(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);
        $message = new ServiceMessage($service, 'Test', 'Content');

        $em = $this->createStub(EntityManagerInterface::class);

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

        $response = $controller->messageResolve($message, $em);
        $this->assertSame(302, $response->getStatusCode());
        $this->assertFalse($message->isActive());
    }

    public function testApiDaily(): void
    {
        $category = new ServiceCategory('Infra', 'infra');
        $service = new Service('Esy-Web', 'esy-web', $category);

        $svcRepo = $this->createStub(ServiceRepository::class);
        $svcRepo->method('getDailyStatus')->willReturn([
            ['date' => '2026-04-01', 'status' => 'up'],
            ['date' => '2026-04-02', 'status' => 'up'],
        ]);

        $controller = new StatusController();

        $response = $controller->apiDaily($service, $svcRepo);
        $this->assertSame(200, $response->getStatusCode());
    }
}
