<?php

declare(strict_types=1);

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

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

final class TeacherDuplicateDetectorTest extends TestCase
{
    private TeacherDuplicateDetector $detector;

    protected function setUp(): void
    {
        $this->detector = new TeacherDuplicateDetector();
    }

    #[Test]
    public function detectsDuplicateByEmail(): void
    {
        $existing = [
            $this->teacher('Jean', 'Dupont', 'jean@example.com'),
        ];
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean@example.com']),
        ];

        $result = $this->detector->detecter($rows, $existing);

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

    #[Test]
    public function detectsDuplicateByEmailCaseInsensitive(): void
    {
        $existing = [
            $this->teacher('Jean', 'Dupont', 'Jean.Dupont@Example.COM'),
        ];
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean.dupont@example.com']),
        ];

        $result = $this->detector->detecter($rows, $existing);

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

    #[Test]
    public function detectsIntraFileDuplicateByEmail(): void
    {
        $existing = [];
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean@example.com'], 1),
            $this->createRow(['lastName' => 'Martin', 'firstName' => 'Pierre', 'email' => 'jean@example.com'], 2),
        ];

        $result = $this->detector->detecter($rows, $existing);

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

    #[Test]
    public function doesNotFlagDifferentEmails(): void
    {
        $existing = [
            $this->teacher('Jean', 'Dupont', 'jean@example.com'),
        ];
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean.d@other.com']),
        ];

        $result = $this->detector->detecter($rows, $existing);

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

    #[Test]
    public function preservesExistingValidationErrors(): void
    {
        $existing = [
            $this->teacher('Jean', 'Dupont', 'jean@example.com'),
        ];

        $row = new ImportRow(
            lineNumber: 1,
            rawData: ['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean@example.com'],
            mappedData: ['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean@example.com'],
            errors: [new ImportRowError('subjects', 'Matière inexistante.')],
        );

        $result = $this->detector->detecter([$row], $existing);

        self::assertCount(2, $result[0]->errors);
        self::assertSame('subjects', $result[0]->errors[0]->column);
        self::assertSame('_duplicate', $result[0]->errors[1]->column);
    }

    #[Test]
    public function multipleRowsMixedDuplicatesAndValid(): void
    {
        $existing = [
            $this->teacher('Jean', 'Dupont', 'jean@example.com'),
        ];
        $rows = [
            $this->createRow(['lastName' => 'Dupont', 'firstName' => 'Jean', 'email' => 'jean@example.com'], 1),
            $this->createRow(['lastName' => 'Martin', 'firstName' => 'Pierre', 'email' => 'pierre@example.com'], 2),
            $this->createRow(['lastName' => 'Bernard', 'firstName' => 'Claire', 'email' => 'claire@example.com'], 3),
        ];

        $result = $this->detector->detecter($rows, $existing);

        self::assertFalse($result[0]->estValide());
        self::assertTrue($result[1]->estValide());
        self::assertTrue($result[2]->estValide());
    }

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

    /**
     * @return array{firstName: string, lastName: string, email: string}
     */
    private function teacher(string $firstName, string $lastName, string $email): array
    {
        return [
            'firstName' => $firstName,
            'lastName' => $lastName,
            'email' => $email,
        ];
    }
}
