<?php

declare(strict_types=1);

namespace App\Administration\Application\Service\Import;

use App\Administration\Domain\Model\Import\ImportRow;
use App\Administration\Domain\Model\Import\ImportRowError;
use App\Administration\Domain\Model\Import\StudentImportField;

use function mb_strtolower;
use function sprintf;
use function trim;

/**
 * Détecte les doublons parmi les lignes d'import :
 * - contre les élèves existants en base (email, numéro élève, nom+prénom+classe)
 * - au sein du fichier lui-même (lignes identiques).
 */
final readonly class DuplicateDetector
{
    /**
     * @param list<ImportRow>                                                                                              $rows
     * @param list<array{firstName: string, lastName: string, email: ?string, studentNumber: ?string, className: ?string}> $existingStudents
     *
     * @return list<ImportRow>
     */
    public function detecter(array $rows, array $existingStudents): array
    {
        $byEmail = [];
        $byStudentNumber = [];
        $byNameClass = [];

        foreach ($existingStudents as $student) {
            if ($student['email'] !== null && trim($student['email']) !== '') {
                $byEmail[mb_strtolower(trim($student['email']))] = true;
            }
            if ($student['studentNumber'] !== null && trim($student['studentNumber']) !== '') {
                $byStudentNumber[trim($student['studentNumber'])] = true;
            }
            $key = $this->nameClassKey(
                $student['firstName'],
                $student['lastName'],
                $student['className'] ?? '',
            );
            if ($key !== null) {
                $byNameClass[$key] = true;
            }
        }

        $result = [];

        foreach ($rows as $row) {
            $match = $this->findMatch($row, $byEmail, $byStudentNumber, $byNameClass);

            if ($match !== null) {
                $row = $row->avecErreurs(new ImportRowError(
                    '_duplicate',
                    sprintf('Cet élève existe déjà (correspondance : %s).', $match),
                ));
            } else {
                $this->indexRow($row, $byEmail, $byStudentNumber, $byNameClass);
            }

            $result[] = $row;
        }

        return $result;
    }

    /**
     * @param array<string, true> $byEmail
     * @param array<string, true> $byStudentNumber
     * @param array<string, true> $byNameClass
     */
    private function findMatch(
        ImportRow $row,
        array $byEmail,
        array $byStudentNumber,
        array $byNameClass,
    ): ?string {
        $email = $row->valeurChamp(StudentImportField::EMAIL);
        if ($email !== null && trim($email) !== '') {
            $normalized = mb_strtolower(trim($email));
            if (isset($byEmail[$normalized])) {
                return 'email';
            }
        }

        $studentNumber = $row->valeurChamp(StudentImportField::STUDENT_NUMBER);
        if ($studentNumber !== null && trim($studentNumber) !== '') {
            if (isset($byStudentNumber[trim($studentNumber)])) {
                return 'numéro élève';
            }
        }

        $firstName = $row->valeurChamp(StudentImportField::FIRST_NAME);
        $lastName = $row->valeurChamp(StudentImportField::LAST_NAME);
        $className = $row->valeurChamp(StudentImportField::CLASS_NAME);
        $key = $this->nameClassKey($firstName ?? '', $lastName ?? '', $className ?? '');

        if ($key !== null && isset($byNameClass[$key])) {
            return 'nom + classe';
        }

        return null;
    }

    /**
     * @param array<string, true> $byEmail
     * @param array<string, true> $byStudentNumber
     * @param array<string, true> $byNameClass
     */
    private function indexRow(
        ImportRow $row,
        array &$byEmail,
        array &$byStudentNumber,
        array &$byNameClass,
    ): void {
        $email = $row->valeurChamp(StudentImportField::EMAIL);
        if ($email !== null && trim($email) !== '') {
            $byEmail[mb_strtolower(trim($email))] = true;
        }

        $studentNumber = $row->valeurChamp(StudentImportField::STUDENT_NUMBER);
        if ($studentNumber !== null && trim($studentNumber) !== '') {
            $byStudentNumber[trim($studentNumber)] = true;
        }

        $firstName = $row->valeurChamp(StudentImportField::FIRST_NAME);
        $lastName = $row->valeurChamp(StudentImportField::LAST_NAME);
        $className = $row->valeurChamp(StudentImportField::CLASS_NAME);
        $key = $this->nameClassKey($firstName ?? '', $lastName ?? '', $className ?? '');

        if ($key !== null) {
            $byNameClass[$key] = true;
        }
    }

    private function nameClassKey(string $firstName, string $lastName, string $className): ?string
    {
        $first = mb_strtolower(trim($firstName));
        $last = mb_strtolower(trim($lastName));
        $class = mb_strtolower(trim($className));

        if ($first === '' || $last === '' || $class === '') {
            return null;
        }

        return $last . '|' . $first . '|' . $class;
    }
}
