<?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\TeacherImportField;

use const FILTER_VALIDATE_EMAIL;

use function in_array;
use function sprintf;
use function trim;

/**
 * Valide les lignes d'import enseignants après mapping.
 *
 * Vérifie les champs obligatoires (nom, prénom, email),
 * le format email, et l'existence des matières/classes référencées.
 *
 * @see AC2: Mapping spécifique enseignants
 * @see AC3: Gestion matières inexistantes
 * @see AC5: Gestion doublons email
 */
final readonly class TeacherImportRowValidator
{
    private MultiValueParser $multiValueParser;

    /**
     * @param list<string>|null $existingSubjectNames Noms des matières existantes. null = pas de vérification.
     * @param list<string>|null $existingClassNames   Noms des classes existantes. null = pas de vérification.
     */
    public function __construct(
        private ?array $existingSubjectNames = null,
        private ?array $existingClassNames = null,
    ) {
        $this->multiValueParser = new MultiValueParser();
    }

    public function valider(ImportRow $row): ImportRow
    {
        $errors = [];

        $errors = [...$errors, ...$this->validerChampsObligatoires($row)];
        $errors = [...$errors, ...$this->validerEmail($row)];
        $errors = [...$errors, ...$this->validerMatieres($row)];
        $errors = [...$errors, ...$this->validerClasses($row)];

        if ($errors !== []) {
            return $row->avecErreurs(...$errors);
        }

        return $row;
    }

    /**
     * @param list<ImportRow> $rows
     *
     * @return list<ImportRow>
     */
    public function validerTout(array $rows): array
    {
        return array_map($this->valider(...), $rows);
    }

    /**
     * @return list<ImportRowError>
     */
    private function validerChampsObligatoires(ImportRow $row): array
    {
        $errors = [];

        foreach (TeacherImportField::champsObligatoires() as $field) {
            $value = $row->mappedData[$field->value] ?? null;

            if ($value === null || trim($value) === '') {
                $errors[] = new ImportRowError(
                    $field->value,
                    sprintf('Le champ "%s" est obligatoire.', $field->label()),
                );
            }
        }

        return $errors;
    }

    /**
     * @return list<ImportRowError>
     */
    private function validerEmail(ImportRow $row): array
    {
        $email = $row->mappedData[TeacherImportField::EMAIL->value] ?? null;

        if ($email === null || trim($email) === '') {
            return [];
        }

        if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            return [new ImportRowError(
                TeacherImportField::EMAIL->value,
                sprintf('L\'adresse email "%s" est invalide.', $email),
            )];
        }

        return [];
    }

    /**
     * @return list<ImportRowError>
     */
    private function validerMatieres(ImportRow $row): array
    {
        if ($this->existingSubjectNames === null) {
            return [];
        }

        $subjectsRaw = $row->mappedData[TeacherImportField::SUBJECTS->value] ?? null;

        if ($subjectsRaw === null || trim($subjectsRaw) === '') {
            return [];
        }

        $subjects = $this->multiValueParser->parse($subjectsRaw);
        $unknown = [];

        foreach ($subjects as $subject) {
            if (!in_array($subject, $this->existingSubjectNames, true)) {
                $unknown[] = $subject;
            }
        }

        if ($unknown !== []) {
            return [new ImportRowError(
                TeacherImportField::SUBJECTS->value,
                sprintf('Matière(s) inexistante(s) : %s', implode(', ', $unknown)),
            )];
        }

        return [];
    }

    /**
     * @return list<ImportRowError>
     */
    private function validerClasses(ImportRow $row): array
    {
        if ($this->existingClassNames === null) {
            return [];
        }

        $classesRaw = $row->mappedData[TeacherImportField::CLASSES->value] ?? null;

        if ($classesRaw === null || trim($classesRaw) === '') {
            return [];
        }

        $classes = $this->multiValueParser->parse($classesRaw);
        $unknown = [];

        foreach ($classes as $class) {
            if (!in_array($class, $this->existingClassNames, true)) {
                $unknown[] = $class;
            }
        }

        if ($unknown !== []) {
            return [new ImportRowError(
                TeacherImportField::CLASSES->value,
                sprintf('Classe(s) inexistante(s) : %s', implode(', ', $unknown)),
            )];
        }

        return [];
    }
}
