<?php

namespace MintyPHP\Tests\Architecture;

use MintyPHP\App\Module\Contracts\EventListener;
use MintyPHP\App\Module\Contracts\LayoutContextProvider;
use MintyPHP\App\Module\Contracts\ModuleDeactivationHandler;
use MintyPHP\App\Module\Contracts\SearchResourceProvider;
use MintyPHP\App\Module\Contracts\SessionProvider;
use MintyPHP\Service\Access\AuthorizationPolicyInterface;

final class ModuleProviderContractTest extends AbstractModuleStructureContractTestCase
{
    public function testContainerRegistrarClassesExist(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->containerRegistrars as $registrar) {
                self::assertTrue(
                    class_exists($registrar),
                    "Module '{$module['id']}' declares container_registrar '{$registrar}' but class does not exist"
                );
            }
        }
    }

    public function testSearchProviderClassesImplementContract(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->searchResources as $providerClass) {
                self::assertTrue(
                    class_exists($providerClass),
                    "Module '{$module['id']}' search provider '{$providerClass}' class not found"
                );
                $provider = new $providerClass();
                self::assertInstanceOf(
                    SearchResourceProvider::class,
                    $provider,
                    "Module '{$module['id']}' search provider must implement SearchResourceProvider"
                );
            }
        }
    }

    public function testLayoutContextProviderClassesImplementContract(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->layoutContextProviders as $providerClass) {
                self::assertTrue(
                    class_exists($providerClass),
                    "Module '{$module['id']}' layout context provider '{$providerClass}' class not found"
                );
                $provider = new $providerClass();
                self::assertInstanceOf(
                    LayoutContextProvider::class,
                    $provider,
                    "Module '{$module['id']}' layout context provider must implement LayoutContextProvider"
                );
            }
        }
    }

    public function testSessionProviderClassesImplementContract(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->sessionProviders as $providerClass) {
                self::assertTrue(
                    class_exists($providerClass),
                    "Module '{$module['id']}' session provider '{$providerClass}' class not found"
                );
                $provider = new $providerClass();
                self::assertInstanceOf(
                    SessionProvider::class,
                    $provider,
                    "Module '{$module['id']}' session provider must implement SessionProvider"
                );
            }
        }
    }

    public function testAuthorizationPolicyClassesImplementContract(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->authorizationPolicies as $policyClass) {
                self::assertTrue(
                    class_exists($policyClass),
                    "Module '{$module['id']}' authorization policy '{$policyClass}' class not found"
                );
                self::assertTrue(
                    is_subclass_of($policyClass, AuthorizationPolicyInterface::class)
                    || in_array(AuthorizationPolicyInterface::class, class_implements($policyClass) ?: [], true),
                    "Module '{$module['id']}' authorization policy must implement AuthorizationPolicyInterface"
                );
            }
        }
    }

    public function testEventListenerClassesImplementContract(): void
    {
        foreach (self::$modules as $module) {
            foreach ($module['manifest']->eventListeners as $eventName => $handlers) {
                foreach ($handlers as $index => $handler) {
                    $class = $handler['class'];
                    self::assertTrue(
                        class_exists($class),
                        "Module '{$module['id']}' event_listeners['{$eventName}'][{$index}] class '{$class}' not found"
                    );
                    self::assertTrue(
                        is_subclass_of($class, EventListener::class)
                        || in_array(EventListener::class, class_implements($class) ?: [], true),
                        "Module '{$module['id']}' event_listeners['{$eventName}'][{$index}] class '{$class}' "
                        . "must implement EventListener interface"
                    );
                }
            }
        }

        self::addToAssertionCount(1);
    }

    public function testDeactivationHandlerClassImplementsContract(): void
    {
        foreach (self::$modules as $module) {
            $handler = $module['manifest']->deactivationHandler;
            if ($handler === null) {
                continue;
            }
            self::assertTrue(
                class_exists($handler),
                "Module '{$module['id']}' deactivation_handler class '{$handler}' not found"
            );
            self::assertTrue(
                is_subclass_of($handler, ModuleDeactivationHandler::class)
                || in_array(ModuleDeactivationHandler::class, class_implements($handler) ?: [], true),
                "Module '{$module['id']}' deactivation_handler class '{$handler}' "
                . "must implement ModuleDeactivationHandler interface"
            );
        }

        self::addToAssertionCount(1);
    }
}
