<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Api\Controller;

use App\Administration\Application\Command\ImportTeachers\ImportTeachersCommand;
use App\Administration\Application\Service\Import\ImportReport;
use App\Administration\Application\Service\Import\TeacherImportOrchestrator;
use App\Administration\Domain\Exception\FichierImportInvalideException;
use App\Administration\Domain\Exception\ImportBatchNotFoundException;
use App\Administration\Domain\Exception\MappingIncompletException;
use App\Administration\Domain\Model\Import\ImportBatchId;
use App\Administration\Domain\Model\Import\ImportRow;
use App\Administration\Domain\Model\Import\ImportRowError;
use App\Administration\Domain\Model\Import\KnownImportFormat;
use App\Administration\Domain\Model\Import\TeacherColumnMapping;
use App\Administration\Domain\Model\Import\TeacherImportBatch;
use App\Administration\Domain\Model\Import\TeacherImportField;
use App\Administration\Domain\Repository\TeacherImportBatchRepository;
use App\Administration\Infrastructure\Security\SecurityUser;
use App\Administration\Infrastructure\Service\CurrentAcademicYearResolver;
use App\Shared\Domain\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantContext;

use function array_slice;

use DateTimeInterface;

use function in_array;

use InvalidArgumentException;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;

/**
 * Endpoints pour le wizard d'import d'enseignants via CSV/XLSX.
 *
 * @see FR77: Import enseignants via CSV
 */
#[Route('/api/import/teachers')]
final readonly class TeacherImportController
{
    private const int MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 Mo

    public function __construct(
        private Security $security,
        private TeacherImportBatchRepository $teacherImportBatchRepository,
        private TeacherImportOrchestrator $orchestrator,
        private MessageBusInterface $commandBus,
        private TenantContext $tenantContext,
        private CurrentAcademicYearResolver $academicYearResolver,
    ) {
    }

    /**
     * T5.1 : Upload d'un fichier CSV ou XLSX.
     *
     * Retourne les colonnes détectées, un aperçu et un mapping suggéré.
     */
    #[Route('/upload', methods: ['POST'], name: 'api_import_teachers_upload')]
    public function upload(Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();
        $tenantId = TenantId::fromString($user->tenantId());

        $file = $request->files->get('file');
        if (!$file instanceof UploadedFile) {
            throw new BadRequestHttpException('Un fichier CSV ou XLSX est requis.');
        }

        if ($file->getSize() > self::MAX_FILE_SIZE) {
            throw new BadRequestHttpException('Le fichier dépasse la taille maximale de 10 Mo.');
        }

        $extension = strtolower($file->getClientOriginalExtension());
        if (!in_array($extension, ['csv', 'txt', 'xlsx', 'xls'], true)) {
            throw new BadRequestHttpException('Extension non supportée. Utilisez CSV ou XLSX.');
        }

        $allowedMimeTypes = [
            'text/csv', 'text/plain', 'application/csv',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'application/vnd.ms-excel',
        ];

        $mimeType = $file->getMimeType();
        if ($mimeType === null || !in_array($mimeType, $allowedMimeTypes, true)) {
            throw new BadRequestHttpException('Type de fichier non supporté. Utilisez CSV ou XLSX.');
        }

        try {
            $result = $this->orchestrator->analyzeFile(
                $file->getPathname(),
                $extension,
                $file->getClientOriginalName(),
                $tenantId,
            );
        } catch (FichierImportInvalideException|InvalidArgumentException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }

        $batch = $result['batch'];
        $suggestedMapping = $result['suggestedMapping'];

        return new JsonResponse([
            'id' => (string) $batch->id,
            'filename' => $batch->originalFilename,
            'totalRows' => $batch->totalRows,
            'columns' => $batch->detectedColumns,
            'detectedFormat' => ($batch->detectedFormat ?? KnownImportFormat::CUSTOM)->value,
            'suggestedMapping' => $this->serializeMapping($suggestedMapping),
            'preview' => $this->serializeRows(array_slice($batch->lignes(), 0, 5)),
        ], Response::HTTP_CREATED);
    }

    /**
     * T5.2 : Valider et appliquer le mapping des colonnes.
     */
    #[Route('/{id}/mapping', methods: ['POST'], name: 'api_import_teachers_mapping')]
    public function mapping(string $id, Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();
        $batch = $this->getBatch($id, TenantId::fromString($user->tenantId()));

        $data = $request->toArray();

        /** @var array<string, string> $mappingData */
        $mappingData = $data['mapping'] ?? [];

        /** @var string $formatValue */
        $formatValue = $data['format'] ?? '';
        $format = KnownImportFormat::tryFrom($formatValue) ?? KnownImportFormat::CUSTOM;

        /** @var array<string, TeacherImportField> $mappingFields */
        $mappingFields = [];
        foreach ($mappingData as $column => $fieldValue) {
            $field = TeacherImportField::tryFrom($fieldValue);
            if ($field !== null) {
                $mappingFields[$column] = $field;
            }
        }

        try {
            $columnMapping = TeacherColumnMapping::creer($mappingFields, $format);
        } catch (MappingIncompletException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }

        $this->orchestrator->applyMapping($batch, $columnMapping);

        return new JsonResponse([
            'id' => (string) $batch->id,
            'mapping' => $this->serializeMapping($columnMapping->mapping),
            'totalRows' => $batch->totalRows,
        ]);
    }

    /**
     * T5.3 : Preview avec validation et erreurs.
     */
    #[Route('/{id}/preview', methods: ['GET'], name: 'api_import_teachers_preview')]
    public function preview(string $id): JsonResponse
    {
        $user = $this->getSecurityUser();
        $tenantId = TenantId::fromString($user->tenantId());
        $batch = $this->getBatch($id, $tenantId);

        $result = $this->orchestrator->generatePreview($batch, $tenantId);

        return new JsonResponse([
            'id' => (string) $batch->id,
            'totalRows' => $result['report']->totalRows,
            'validCount' => $result['report']->importedCount,
            'errorCount' => $result['report']->errorCount,
            'rows' => $this->serializeRows($result['validatedRows']),
            'unknownSubjects' => $result['unknownSubjects'],
            'unknownClasses' => $result['unknownClasses'],
        ]);
    }

    /**
     * T5.4 : Confirmer et lancer l'import.
     */
    #[Route('/{id}/confirm', methods: ['POST'], name: 'api_import_teachers_confirm')]
    public function confirm(string $id, Request $request): JsonResponse
    {
        $user = $this->getSecurityUser();
        $tenantId = TenantId::fromString($user->tenantId());
        $batch = $this->getBatch($id, $tenantId);

        $data = $request->toArray();

        /** @var bool $createMissingSubjects */
        $createMissingSubjects = $data['createMissingSubjects'] ?? false;

        /** @var bool $importValidOnly */
        $importValidOnly = $data['importValidOnly'] ?? true;

        /** @var bool $updateExisting */
        $updateExisting = $data['updateExisting'] ?? false;

        $academicYearId = $this->academicYearResolver->resolve('current')
            ?? throw new BadRequestHttpException('Impossible de résoudre l\'année scolaire courante.');
        $schoolName = $this->tenantContext->getCurrentTenantConfig()->subdomain;

        $this->orchestrator->prepareForConfirmation($batch, $createMissingSubjects, $importValidOnly, $tenantId);

        $this->commandBus->dispatch(new ImportTeachersCommand(
            batchId: (string) $batch->id,
            tenantId: $user->tenantId(),
            schoolName: $schoolName,
            academicYearId: $academicYearId,
            createMissingSubjects: $createMissingSubjects,
            updateExisting: $updateExisting,
        ));

        return new JsonResponse([
            'id' => (string) $batch->id,
            'status' => 'processing',
            'message' => 'Import lancé. Suivez la progression via GET /status.',
        ], Response::HTTP_ACCEPTED);
    }

    /**
     * T5.5 : Statut et progression.
     */
    #[Route('/{id}/status', methods: ['GET'], name: 'api_import_teachers_status')]
    public function status(string $id): JsonResponse
    {
        $user = $this->getSecurityUser();
        $batch = $this->getBatch($id, TenantId::fromString($user->tenantId()));

        return new JsonResponse([
            'id' => (string) $batch->id,
            'status' => $batch->status->value,
            'totalRows' => $batch->totalRows,
            'importedCount' => $batch->importedCount,
            'errorCount' => $batch->errorCount,
            'progression' => $batch->progression(),
            'completedAt' => $batch->completedAt?->format(DateTimeInterface::ATOM),
        ]);
    }

    /**
     * T5.6 : Télécharger le rapport.
     */
    #[Route('/{id}/report', methods: ['GET'], name: 'api_import_teachers_report')]
    public function report(string $id): JsonResponse
    {
        $user = $this->getSecurityUser();
        $batch = $this->getBatch($id, TenantId::fromString($user->tenantId()));

        $report = ImportReport::fromValidatedRows($batch->lignes());

        return new JsonResponse([
            'id' => (string) $batch->id,
            'status' => $batch->status->value,
            'totalRows' => $report->totalRows,
            'importedCount' => $batch->importedCount,
            'errorCount' => $batch->errorCount,
            'report' => $report->lignesRapport(),
            'errors' => array_map(
                static fn (ImportRow $row) => [
                    'line' => $row->lineNumber,
                    'errors' => array_map(
                        static fn (ImportRowError $error) => [
                            'column' => $error->column,
                            'message' => $error->message,
                        ],
                        $row->errors,
                    ),
                ],
                $report->errorRows,
            ),
        ]);
    }

    private function getSecurityUser(): SecurityUser
    {
        $user = $this->security->getUser();

        if (!$user instanceof SecurityUser) {
            throw new AccessDeniedHttpException();
        }

        return $user;
    }

    private function getBatch(string $id, TenantId $tenantId): TeacherImportBatch
    {
        try {
            $batch = $this->teacherImportBatchRepository->get(ImportBatchId::fromString($id));
        } catch (ImportBatchNotFoundException|InvalidArgumentException) {
            throw new NotFoundHttpException('Import non trouvé.');
        }

        if ((string) $batch->tenantId !== (string) $tenantId) {
            throw new NotFoundHttpException('Import non trouvé.');
        }

        return $batch;
    }

    /**
     * @param array<string, TeacherImportField> $mapping
     *
     * @return array<string, string>
     */
    private function serializeMapping(array $mapping): array
    {
        $result = [];
        foreach ($mapping as $column => $field) {
            $result[$column] = $field->value;
        }

        return $result;
    }

    /**
     * @param list<ImportRow> $rows
     *
     * @return list<array<string, mixed>>
     */
    private function serializeRows(array $rows): array
    {
        return array_map(
            static fn (ImportRow $row) => [
                'line' => $row->lineNumber,
                'data' => $row->mappedData,
                'valid' => $row->estValide(),
                'errors' => array_map(
                    static fn (ImportRowError $error) => [
                        'column' => $error->column,
                        'message' => $error->message,
                    ],
                    $row->errors,
                ),
            ],
            $rows,
        );
    }
}
