<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Fixtures\Doctrine;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Offer;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\OfferDate;
use Ferienpass\CoreBundle\Entity\Participant;
use Ferienpass\CoreBundle\Entity\Participant\ParticipantInterface;
use Ferienpass\CoreBundle\Entity\User;
use PHPUnit\Framework\TestCase;

/**
 * Base class for tests that need a real EntityManager with real rows. See {@see OrmHarness}.
 */
abstract class OrmTestCase extends TestCase
{
    protected EntityManagerInterface $em;

    protected ManagerRegistry $registry;

    private string $timezone;

    protected function setUp(): void
    {
        // SQLite's CURRENT_TIMESTAMP is UTC while Doctrine writes datetimes in PHP's
        // default timezone. Queries here compare the two, so pin both to UTC.
        $this->timezone = date_default_timezone_get();
        date_default_timezone_set('UTC');

        $harness = OrmHarness::boot();

        $this->em = $harness->em;
        $this->registry = $harness->registry;
    }

    protected function tearDown(): void
    {
        date_default_timezone_set($this->timezone);
    }

    /**
     * @param list<string>                                               $status
     * @param list<array{0: \DateTimeInterface, 1: ?\DateTimeInterface}> $dates
     */
    protected function createOffer(array $status = [], array $dates = [], ?Edition $edition = null): OfferInterface
    {
        $offer = new Offer();
        $offer->setName('Test offer');
        // The workflow marking store keys the status by name, which is what the
        // JSON_CONTAINS_PATH conditions in the queries look for.
        $offer->setStatus(array_fill_keys($status, 1));

        if (null !== $edition) {
            $offer->setEdition($edition);
        }

        foreach ($dates as [$begin, $end]) {
            $date = new OfferDate($offer);
            $date->setBegin(\DateTime::createFromInterface($begin));
            $date->setEnd(null === $end ? null : \DateTime::createFromInterface($end));

            $offer->addDate($date);
            $this->em->persist($date);
        }

        $this->em->persist($offer);

        return $offer;
    }

    protected function createEdition(bool $archived = false, ?string $survey = 'survey'): Edition
    {
        $edition = new Edition();
        $edition->setName('Test edition');
        $edition->setAlias('test-edition');
        $edition->setArchived($archived);
        $edition->setSurveyWithApplication($survey);

        $this->em->persist($edition);

        return $edition;
    }

    protected function createUser(string $email = 'host@example.com'): User
    {
        $user = new User();
        $user->setEmail($email);

        $this->em->persist($user);

        return $user;
    }

    protected function createParticipant(string $firstname = 'Test', string $lastname = 'Participant', ?string $email = null): ParticipantInterface
    {
        $participant = new Participant();
        $participant->setFirstname($firstname);
        $participant->setLastname($lastname);

        if (null !== $email) {
            $participant->setEmail($email);
        }

        $this->em->persist($participant);

        return $participant;
    }
}
