<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Serenity\CoreBundle\Seed;

use Doctrine\ORM\EntityManagerInterface;
use Psr\Container\ContainerInterface;
use Serenity\CoreBundle\Seed\Doctrine\Repository\SeedStateRepository;
use Serenity\CoreBundle\Seed\Entity\SeedState;
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;

readonly class SeedRunner
{
    /** @param iterable<SeederInterface> $seeders */
    public function __construct(
        #[AutowireIterator(SeederInterface::class)]
        private iterable $seeders,
        #[AutowireLocator(SeederInterface::class)]
        private ContainerInterface $seederLocator,
        private EntityManagerInterface $em,
        private SeedStateRepository $seedStateRepository,
    ) {
    }

    public function run(SeedPayload $seedPayload, ?string $saasIdentifier = null): void
    {
        if (null !== $seedPayload->seeder) {
            if (!$this->seederLocator->has($seedPayload->seeder)) {
                throw new \InvalidArgumentException(\sprintf('Seeder "%s" not found.', $seedPayload->seeder));
            }

            /** @var SeederInterface $seeder */
            $seeder = $this->seederLocator->get($seedPayload->seeder);
            $this->runOne($seeder, $seedPayload, $saasIdentifier);

            return;
        }

        foreach ($this->seeders as $seeder) {
            $this->runOne($seeder, $seedPayload, $saasIdentifier);
        }
    }

    public function runOne(SeederInterface $seeder, SeedPayload $seedPayload, ?string $saasIdentifier = null): void
    {
        /** @var SeedState|null $state */
        $state = $this->seedStateRepository->findOneBySaasAndSeed(
            $saasIdentifier,
            $seeder::getDefaultIndex()
        );

        $fromVersion = $state ? $state->version : 0;
        $targetVersion = $seeder->version();

        if ($fromVersion >= $targetVersion) {
            return;
        }

        if ($seedPayload->dryRun) {
            return;
        }

        $seeder->seed($seedPayload, $fromVersion);

        if (!$state) {
            $state = SeedState::create($saasIdentifier, $seeder::getDefaultIndex(), $seedPayload->runId, $targetVersion);
            $this->em->persist($state);
        } else {
            $state->moveTo($targetVersion, $seedPayload->runId);
        }
    }
}
