<?php

declare(strict_types=1);

use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\NotDependsOnTheseNamespaces;
use Arkitect\Expression\ForClasses\NotHaveDependencyOutsideNamespace;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;

return static function (Config $config): void {
    $domainClassSet = ClassSet::fromDir(__DIR__ . '/src/Domain');
    $businessDomains = ['Manga', 'Reader', 'Scraping', 'Conversion'];

    // Classes PHP standards et utilitaires
    $standardExceptions = [
        'DateTimeImmutable',
        'RuntimeException',
        'Exception',
        'DomainException',
        'Symfony\Component\HttpKernel\Exception',
        'Throwable',
        'InvalidArgumentException',
        'App\Domain\Shared\Domain\Model\AggregateRoot',
    ];

    // Dépendances externes autorisées
    $externalDependencies = [
        'Symfony\Component\Messenger',
        'Ramsey\Uuid'
    ];

    // Règle pour le namespace cohérent
    $namespaceRule = Rule::allClasses()
        ->that(new ResideInOneOfTheseNamespaces('App\Domain'))
        ->should(new HaveNameMatching('*'))
        ->because('nous voulons un namespace cohérent pour notre domaine');

    $rules = [$namespaceRule];

    // Règles spécifiques pour le domaine Shared
    $rules[] = Rule::allClasses()
        ->that(new ResideInOneOfTheseNamespaces('App\Domain\Shared'))
        ->should(new NotHaveDependencyOutsideNamespace(
            'App\Domain\Shared',
            $standardExceptions
        ))
        ->because('le domaine Shared ne doit dépendre de personne');

    // Génération des règles pour chaque domaine métier
    foreach ($businessDomains as $domain) {
        // Règle pour la couche Domain
        $rules[] = Rule::allClasses()
            ->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Domain"))
            ->should(new NotHaveDependencyOutsideNamespace(
                "App\Domain\\$domain\Domain",
                $standardExceptions
            ))
            ->because("la couche Domain de $domain ne doit dépendre que de ses propres classes, des contrats partagés et des exceptions autorisées");

        // Règle pour la couche Application
        $rules[] = Rule::allClasses()
            ->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Application"))
            ->should(new NotHaveDependencyOutsideNamespace(
                "App\Domain\\$domain",
                array_merge($standardExceptions, $externalDependencies, ['App\Domain\Shared\Domain\Contract', 'App\Domain\Shared\Domain\Event'])
            ))
            ->because("la couche Application de $domain ne peut dépendre que de son propre domaine, des contrats partagés et des dépendances autorisées");

        // Interdiction explicite pour l'Application d'accéder à l'Infrastructure
        $rules[] = Rule::allClasses()
            ->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Application"))
            ->should(new NotDependsOnTheseNamespaces("App\Domain\\$domain\Infrastructure"))
            ->because("la couche Application de $domain ne doit jamais dépendre de l'Infrastructure, même au sein de son propre domaine");
    }

    $config->add($domainClassSet, ...$rules);
};
