<?php

declare(strict_types=1);

namespace App\Domain\Manga\Application\QueryHandler;

use App\Domain\Manga\Application\Query\FindMangaMatchByFilename;
use App\Domain\Manga\Application\Response\MangaMatchItem;
use App\Domain\Manga\Application\Response\MangaMatchResponse;
use App\Domain\Manga\Domain\Contract\Repository\MangaRepositoryInterface;
use App\Domain\Manga\Domain\Contract\Service\FilenameAnalyzerInterface;
use App\Domain\Manga\Domain\Model\Manga;

readonly class FindMangaMatchByFilenameHandler
{
    public function __construct(
        private FilenameAnalyzerInterface $filenameAnalyzer,
        private MangaRepositoryInterface $mangaRepository
    ) {
    }

    public function handle(FindMangaMatchByFilename $query): MangaMatchResponse
    {
        // Analyser le nom de fichier pour extraire les informations
        $analyzedFilename = $this->filenameAnalyzer->analyze($query->filename);

        $searchedTitle = $analyzedFilename->getTitle()->getValue();
        $chapterNumber = $analyzedFilename->hasChapterNumber()
            ? $analyzedFilename->getChapterNumber()->getValue()
            : null;
        $volumeNumber = $analyzedFilename->hasVolumeNumber()
            ? $analyzedFilename->getVolumeNumber()->getValue()
            : null;

        // Rechercher les mangas correspondants
        $foundMangas = $this->mangaRepository->search($searchedTitle, 1, 10);
        $matches = [];

        foreach ($foundMangas as $manga) {
            $mangaId = $manga->getId()->getValue();

            // Calculer un score de correspondance
            $matchScore = $this->calculateMatchScore(
                $manga,
                $searchedTitle
            );

            $matches[] = new MangaMatchItem(
                id: $mangaId,
                title: $manga->getTitle()->getValue(),
                slug: $manga->getSlug()->getValue(),
                alternativeSlugs: $manga->getAlternativeSlugs(),
                thumbnailUrl: $manga->getImageUrls()->getThumbnail(),
                matchScore: $matchScore,
                chapterNumber: $chapterNumber,
                volumeNumber: $volumeNumber
            );
        }

        // Trier les résultats par score de correspondance (du plus élevé au plus faible)
        usort($matches, fn ($a, $b) => $b->matchScore <=> $a->matchScore);

        return new MangaMatchResponse(
            matches: $matches,
            chapterNumber: $chapterNumber,
            volumeNumber: $volumeNumber,
            possibleTitles: [$searchedTitle]
        );
    }

    /**
     * Calcule un score de correspondance entre le manga et le titre recherché
     * Score plus élevé = meilleure correspondance
     */
    private function calculateMatchScore(Manga $manga, string $searchedTitle): int
    {
        $score = 0;
        $mangaTitle = $manga->getTitle()->getValue();
        $mangaSlug = $manga->getSlug()->getValue();

        // Correspondance exacte avec le titre
        if (strtolower($mangaTitle) === strtolower($searchedTitle)) {
            $score += 100;
        }

        // Correspondance exacte avec le slug
        if (strtolower($mangaSlug) === strtolower($searchedTitle)) {
            $score += 90;
        }

        // Correspondance avec les slugs alternatifs
        foreach ($manga->getAlternativeSlugs() as $altSlug) {
            if (strtolower($altSlug) === strtolower($searchedTitle)) {
                $score += 80;
                break;
            }
        }

        // Le titre du manga contient le terme recherché
        if (stripos($mangaTitle, $searchedTitle) !== false) {
            $score += 50;
        }

        // Le terme recherché contient le titre du manga
        if (stripos($searchedTitle, $mangaTitle) !== false) {
            $score += 40;
        }

        // Similarité de Levenshtein (pour les fautes de frappe)
        $levenshteinDistance = levenshtein(
            strtolower($mangaTitle),
            strtolower($searchedTitle)
        );

        if ($levenshteinDistance <= 3) {
            $score += (3 - $levenshteinDistance) * 10;
        }

        return $score;
    }
}
