<?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\Tests\Entity;

use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\AttendanceExtraFile;
use Ferienpass\CoreBundle\Fixtures\Doctrine\OrmTestCase;

/**
 * The file answers of an attendance: the mapping (a staged upload has no attendance
 * yet, so the FK must be nullable) and the read path the whole feature is built on —
 * {@see Attendance::getExtraFile()}.
 */
class AttendanceExtraFileTest extends OrmTestCase
{
    private const QUESTION = '0192d1f0-0000-7000-8000-000000000001';
    private const OTHER_QUESTION = '0192d1f0-0000-7000-8000-000000000002';

    public function testAssociationIsTheInverseSideOfTheFilesAttendance(): void
    {
        $mapping = $this->em->getClassMetadata(Attendance::class)->getAssociationMapping('extraFiles');

        self::assertSame(AttendanceExtraFile::class, $mapping->targetEntity);
        self::assertSame('attendance', $mapping->mappedBy);
        self::assertFalse($mapping->isOwningSide());
    }

    public function testStagedUploadPersistsWithoutAnAttendance(): void
    {
        // The application form stores a file before the application exists, so the
        // FK has to accept null — otherwise the whole staging flow cannot work.
        $file = new AttendanceExtraFile(self::QUESTION, 'application/a.png', 'a.png', 'image/png', 12);

        $this->em->persist($file);
        $this->em->flush();
        $this->em->clear();

        $reloaded = $this->em->find(AttendanceExtraFile::class, $file->getUuid());

        self::assertInstanceOf(AttendanceExtraFile::class, $reloaded);
        self::assertNull($reloaded->getAttendance());
        self::assertSame(self::QUESTION, $reloaded->getQuestion());
    }

    public function testFileIsReadBackForItsOwnQuestionOnly(): void
    {
        $attendance = $this->createAttendance();
        $mine = new AttendanceExtraFile(self::QUESTION, 'application/a.png', 'a.png', 'image/png', 12, $attendance);
        $this->em->persist($mine);
        $this->em->persist(new AttendanceExtraFile(self::OTHER_QUESTION, 'application/b.pdf', 'b.pdf', 'application/pdf', 12, $attendance));
        $this->em->flush();
        $this->em->clear();

        $reloaded = $this->em->find(Attendance::class, $attendance->getId());

        self::assertSame('a.png', $reloaded->getExtraFile(self::QUESTION)?->getName());
        self::assertSame('b.pdf', $reloaded->getExtraFile(self::OTHER_QUESTION)?->getName());
        self::assertNull($reloaded->getExtraFile('0192d1f0-0000-7000-8000-000000000003'));
    }

    public function testTheNewestFileWinsSoAStaleDuplicateCannotShadowTheAnswer(): void
    {
        $attendance = $this->createAttendance();
        $old = new AttendanceExtraFile(self::QUESTION, 'application/old.png', 'alt.png', 'image/png', 12, $attendance);
        $new = new AttendanceExtraFile(self::QUESTION, 'application/new.png', 'neu.png', 'image/png', 12, $attendance);

        self::assertSame($new, $attendance->getExtraFile(self::QUESTION), 'expected the newer of the two rows');
        self::assertGreaterThanOrEqual($old->getCreatedAt(), $new->getCreatedAt());
    }

    public function testSetExtraValueWritesAndClearsASingleAnswer(): void
    {
        $attendance = $this->createAttendance();

        $attendance->setExtraValue(self::QUESTION, 'a.png');
        self::assertSame('a.png', $attendance->getExtra(self::QUESTION));

        // Removing a file must clear its answer, not leave an empty string that reads
        // as "answered" to the exports and the submit gate.
        $attendance->setExtraValue(self::QUESTION, null);
        self::assertNull($attendance->getExtra(self::QUESTION));
    }

    private function createAttendance(): Attendance
    {
        $attendance = new Attendance($this->createOffer(), $this->createParticipant());

        $this->em->persist($attendance);
        $this->em->flush();

        return $attendance;
    }
}
