<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Service\Import;

use App\Administration\Application\Service\Import\ImportRowValidator;
use App\Administration\Domain\Model\Import\ImportRow;
use App\Administration\Domain\Model\Import\StudentImportField;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class ImportRowValidatorTest extends TestCase
{
    #[Test]
    public function validRowRemainsValid(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function missingLastNameAddsError(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => '',
            'firstName' => 'Jean',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
        self::assertCount(1, $result->errors);
        self::assertSame('lastName', $result->errors[0]->column);
    }

    #[Test]
    public function missingMultipleFieldsAddsMultipleErrors(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => '',
            'firstName' => '',
            'className' => '',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
        self::assertCount(3, $result->errors);
    }

    #[Test]
    public function invalidEmailAddsError(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'email' => 'not-an-email',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
        self::assertSame('email', $result->errors[0]->column);
    }

    #[Test]
    public function validEmailPasses(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'email' => 'jean.dupont@email.com',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function emptyEmailIsAccepted(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'email' => '',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function invalidDateAddsError(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'birthDate' => 'not-a-date',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
        self::assertSame('birthDate', $result->errors[0]->column);
    }

    #[Test]
    public function frenchDateFormatAccepted(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'birthDate' => '15/03/2014',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function isoDateFormatAccepted(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
            'birthDate' => '2014-03-15',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function unknownClassAddsError(): void
    {
        $validator = new ImportRowValidator(['6ème A', '6ème B']);
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '5ème C',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
        self::assertSame('className', $result->errors[0]->column);
    }

    #[Test]
    public function knownClassPasses(): void
    {
        $validator = new ImportRowValidator(['6ème A', '6ème B']);
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function classValidationSkippedWhenNoClassesProvided(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => 'ClasseInconnue',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
    }

    #[Test]
    public function validerToutValidatesAllRows(): void
    {
        $validator = new ImportRowValidator();
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'className' => '6A']),
            $this->createRow(['lastName' => '', 'firstName' => 'Marie', 'className' => '6B']),
            $this->createRow(['lastName' => 'Bernard', 'firstName' => 'Pierre', 'className' => '5A']),
        ];

        $results = $validator->validerTout($rows);

        self::assertCount(3, $results);
        self::assertTrue($results[0]->estValide());
        self::assertFalse($results[1]->estValide());
        self::assertTrue($results[2]->estValide());
    }

    #[Test]
    public function fullNameExpandsToLastNameAndFirstName(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'fullName' => 'BERTHE Alexandre',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
        self::assertSame('BERTHE', $result->valeurChamp(StudentImportField::LAST_NAME));
        self::assertSame('Alexandre', $result->valeurChamp(StudentImportField::FIRST_NAME));
    }

    #[Test]
    public function fullNameDoesNotOverrideExistingLastNameFirstName(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'fullName' => 'BERTHE Alexandre',
            'lastName' => 'Dupont',
            'firstName' => 'Jean',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertTrue($result->estValide());
        self::assertSame('Dupont', $result->valeurChamp(StudentImportField::LAST_NAME));
        self::assertSame('Jean', $result->valeurChamp(StudentImportField::FIRST_NAME));
    }

    #[Test]
    public function splitFullNameHandlesCompoundLastName(): void
    {
        [$lastName, $firstName] = ImportRowValidator::splitFullName('DE LA FONTAINE Jean');

        self::assertSame('DE LA FONTAINE', $lastName);
        self::assertSame('Jean', $firstName);
    }

    #[Test]
    public function splitFullNameHandlesSimpleName(): void
    {
        [$lastName, $firstName] = ImportRowValidator::splitFullName('DUPONT Marie');

        self::assertSame('DUPONT', $lastName);
        self::assertSame('Marie', $firstName);
    }

    #[Test]
    public function splitFullNameHandlesCompoundFirstName(): void
    {
        [$lastName, $firstName] = ImportRowValidator::splitFullName('OLIVIER Jean-Philippe');

        self::assertSame('OLIVIER', $lastName);
        self::assertSame('Jean-Philippe', $firstName);
    }

    #[Test]
    public function emptyFullNameDoesNotExpand(): void
    {
        $validator = new ImportRowValidator();
        $row = $this->createRow([
            'fullName' => '',
            'className' => '6ème A',
        ]);

        $result = $validator->valider($row);

        self::assertFalse($result->estValide());
    }

    /**
     * @param array<string, string> $mappedData
     */
    private function createRow(array $mappedData, int $lineNumber = 1): ImportRow
    {
        return new ImportRow(
            lineNumber: $lineNumber,
            rawData: $mappedData,
            mappedData: $mappedData,
        );
    }
}
