<?php

namespace App\Service\Scraper;

use App\Entity\Chapter;
use App\Entity\ContentSource;
use App\Entity\Manga;
use App\Event\PageScrappingProgressEvent;
use App\Manager\FileSystemManager;
use Doctrine\ORM\EntityManagerInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

abstract class AbstractScraper implements ScraperInterface
{
    protected Client $httpClient;

    public function __construct(
        protected FileSystemManager $fileSystemManager,
        protected EventDispatcherInterface $eventDispatcher,
        protected EntityManagerInterface $entityManager
    ) {
        $this->httpClient = new Client();
    }

    protected function getValidChapterUrl(ContentSource $contentSource, Manga $manga, float $chapterNumber): ?string
    {
        $slugs = array_merge([$manga->getSlug()], $manga->getAlternativeSlugs() ?? []);

        foreach ($slugs as $slug) {
            $url = $contentSource->getChapterUrl($slug, $chapterNumber);
            if ($this->isChapterUrlValid($url)) {
                return $url;
            }
        }

        return null;
    }

    protected function isChapterUrlValid(string $url): bool
    {
        try {
            $response = $this->httpClient->head($url);

            return 200 === $response->getStatusCode();
        } catch (RequestException $e) {
            return false;
        }
    }

    protected function generateCbzPath(Manga $manga, Chapter $chapter): string
    {
        $mangaDir = $this->fileSystemManager->createMangaDirectory($manga->getSlug(), $manga->getPublicationYear());
        $volumeDir = $this->fileSystemManager->createVolumeDirectory($mangaDir, $chapter->getVolume());
        $fileName = sprintf(
            '%s_vol%d_ch%s.cbz',
            $manga->getSlug(),
            $chapter->getVolume(),
            $chapter->getNumber()
        );

        return $volumeDir.'/'.$fileName;
    }

    protected function createCbzFile(array $pageData, string $cbzFilePath): void
    {
        $zip = new \ZipArchive();

        if (true === $zip->open($cbzFilePath, \ZipArchive::CREATE)) {
            foreach ($pageData as $page) {
                $zip->addFile($page['local_image_url'], basename($page['local_image_url']));
            }
            $zip->close();
        }
    }

    protected function cleanupTempFiles(string $directory): void
    {
        $this->fileSystemManager->deleteDirectory($directory);
    }

    protected function cleanImageUrl(string $url): string
    {
        return preg_replace('/[\x00-\x1F\x7F]/', '', trim($url));
    }

    protected function dispatchProgressEvent(Chapter $chapter, int $currentPage, int $totalPages): void
    {
        $event = new PageScrappingProgressEvent($chapter->getId(), $currentPage, $totalPages);
        $this->eventDispatcher->dispatch($event, PageScrappingProgressEvent::NAME);
    }

    /**
     * @throws GuzzleException
     * @throws \Exception
     */
    protected function downloadAndSaveImage(string $imageUrl, string $destinationPath): string
    {
        try {
            $response = $this->httpClient->get($imageUrl);
            $contentType = $response->getHeaderLine('Content-Type');

            if (!str_starts_with($contentType, 'image/')) {
                throw new \Exception('Le contenu récupéré n\'est pas une image. Type de contenu : '.$contentType);
            }

            $imageData = $response->getBody()->getContents();
            $tempFilePath = $this->saveTempFile($imageData);

            $image = $this->createImageResource($tempFilePath, $contentType);
            if (false === $image) {
                throw new \Exception('Échec de la création de la ressource image.');
            }

            $destinationPath = $this->ensureJpgExtension($destinationPath);
            if (!imagejpeg($image, $destinationPath)) {
                imagedestroy($image);
                unlink($tempFilePath);
                throw new \Exception('Échec de la sauvegarde de l\'image en JPG.');
            }

            imagedestroy($image);
            unlink($tempFilePath);

            return $destinationPath;
        } catch (\Exception $e) {
            throw new \Exception('Erreur lors de la récupération de l\'image : '.$e->getMessage());
        }
    }

    private function saveTempFile(string $data): string
    {
        $tempFilePath = tempnam(sys_get_temp_dir(), 'manga_img_');
        file_put_contents($tempFilePath, $data);

        return $tempFilePath;
    }

    /**
     * @throws \Exception
     */
    private function createImageResource(string $filePath, string $contentType)
    {
        return match ($contentType) {
            'image/webp' => imagecreatefromwebp($filePath),
            'image/png' => imagecreatefrompng($filePath),
            'image/jpeg', 'image/jpg' => imagecreatefromjpeg($filePath),
            default => throw new \Exception('Format d\'image non pris en charge : '.$contentType),
        };
    }

    private function ensureJpgExtension(string $path): string
    {
        $info = pathinfo($path);

        return $info['dirname'].'/'.$info['filename'].'.jpg';
    }
}
