<?php

declare(strict_types=1);

namespace App\Module\Commercial\Infrastructure\DataFixtures;

use App\Module\Commercial\Domain\Entity\Bank;
use App\Module\Commercial\Domain\Entity\Country;
use App\Module\Commercial\Domain\Entity\PaymentDelay;
use App\Module\Commercial\Domain\Entity\PaymentType;
use App\Module\Commercial\Domain\Entity\TvaMode;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

/**
 * Fixtures du module Commercial : re-seed des 4 referentiels comptables
 * (tva_mode, payment_delay, payment_type, bank) seedes par la migration M1
 * (Version20260601000000) + du referentiel pays (country) seede par la
 * migration ERP-116 (Version20260609100000).
 *
 * Pourquoi cette fixture EN PLUS du seed de la migration : ces tables sont des
 * entites managees par l'ORM, donc le purger Doctrine les
 * vide avant chaque `doctrine:fixtures:load`. Sans cette fixture, les
 * referentiels seedes par la migration disparaitraient apres `make db-reset`
 * (0 ligne en dev/test) — cassant les FK Client -> referentiels et les tests
 * RG-1.12/1.13. Le seed migration couvre la prod (ou les fixtures ne tournent
 * pas) ; cette fixture re-aligne dev et test. Memes valeurs des deux cotes.
 *
 * Idempotence : lookup par `code` avant insertion (sur le modele de
 * CategoryTypeFixtures). Rejouable sans doublon meme si le purger est desactive.
 */
class CommercialReferentialFixtures extends Fixture
{
    /**
     * Source unique des referentiels : classe d'entite => [code => [label, position]].
     * Doit rester aligne sur le seed de la migration Version20260601000000.
     *
     * @var array<class-string, array<string, array{string, int}>>
     */
    private const REFERENTIALS = [
        TvaMode::class => [
            'FRANCE_VENTES'   => ['France (ventes)', 10],
            'EXPORT_VENTES'   => ['Export (ventes)', 20],
            'INTRACOM_VENTES' => ['Intracom (ventes)', 30],
        ],
        PaymentDelay::class => [
            'J15'         => ['15 jours', 10],
            'J30'         => ['30 jours', 20],
            'A_RECEPTION' => ['À réception', 30],
        ],
        PaymentType::class => [
            'VIREMENT'    => ['Virement', 10],
            'LCR'         => ['LCR', 20],
            'NON_SOUMISE' => ['Non soumise', 30],
            'CHEQUE'      => ['Chèque', 40],
        ],
        Bank::class => [
            'SG'  => ['Société Générale', 10],
            'CIC' => ['CIC', 20],
            'CA'  => ['Crédit Agricole', 30],
        ],
    ];

    /**
     * Referentiel pays (ERP-116) : code ISO alpha-2 => [name, position].
     * Doit rester aligne sur le seed de la migration Version20260609100000.
     * Traite a part car Country porte `name` (et non `label`).
     *
     * @var array<string, array{string, int}>
     */
    private const COUNTRIES = [
        'FR' => ['France', 10],
        'DE' => ['Allemagne', 20],
        'BE' => ['Belgique', 30],
        'ES' => ['Espagne', 40],
        'IT' => ['Italie', 50],
        'GB' => ['Royaume-Uni', 60],
        'CH' => ['Suisse', 70],
    ];

    public function load(ObjectManager $manager): void
    {
        foreach (self::REFERENTIALS as $entityClass => $rows) {
            $this->seedReferential($manager, $entityClass, $rows);
        }

        $this->seedCountries($manager);

        $manager->flush();
    }

    /**
     * Upsert idempotent du referentiel pays (lookup par code). Distinct de
     * seedReferential car Country utilise setName au lieu de setLabel.
     */
    private function seedCountries(ObjectManager $manager): void
    {
        $existingByCode = [];
        foreach ($manager->getRepository(Country::class)->findAll() as $country) {
            $existingByCode[$country->getCode()] = $country;
        }

        foreach (self::COUNTRIES as $code => [$name, $position]) {
            $country = $existingByCode[$code] ?? new Country();
            $country->setCode($code);
            $country->setName($name);
            $country->setPosition($position);
            $manager->persist($country);
        }
    }

    /**
     * Upsert idempotent d'un referentiel : indexe l'existant par code puis
     * cree/met a jour chaque entree. Les 4 entites partagent le meme contrat
     * setCode/setLabel/setPosition.
     *
     * @param class-string                      $entityClass
     * @param array<string, array{string, int}> $rows
     */
    private function seedReferential(ObjectManager $manager, string $entityClass, array $rows): void
    {
        $existingByCode = [];
        foreach ($manager->getRepository($entityClass)->findAll() as $entity) {
            $existingByCode[$entity->getCode()] = $entity;
        }

        foreach ($rows as $code => [$label, $position]) {
            $entity = $existingByCode[$code] ?? new $entityClass();
            $entity->setCode($code);
            $entity->setLabel($label);
            $entity->setPosition($position);
            $manager->persist($entity);
        }
    }
}
