<?php

namespace App\Domain\Scraping\Infrastructure\Service\Scraper;

use App\Domain\Scraping\Domain\Contract\Service\ScraperInterface;
use App\Domain\Scraping\Domain\Exception\ChapterNotFoundException;
use App\Domain\Scraping\Domain\Model\ValueObject\ScrapingRequest;
use App\Domain\Scraping\Domain\Model\ValueObject\ScrapingResult;
use Symfony\Component\Process\Process;

class JavaScriptScraper implements ScraperInterface
{
    private const PUPPETEER_TIMEOUT = 60; // secondes
    private const PUPPETEER_SCRIPT_PATH = '/public/puppeteer-scraper.js';
    private const NODE_EXECUTABLE = 'node';

    public function __construct(
        private readonly string $projectDir
    ) {
    }

    public function scrape(ScrapingRequest $request): ScrapingResult
    {
        $scrappingParameters = $request->getScrapingParameters();

        try {
            $scriptPath = $this->projectDir . self::PUPPETEER_SCRIPT_PATH;

            if (!file_exists($scriptPath)) {
                throw new \RuntimeException('Puppeteer script not found at: ' . $scriptPath);
            }

            $imageUrls = !empty($scrappingParameters['nextPageSelector'])
                ? $this->scrapeHorizontalReader($request, $scriptPath)
                : $this->scrapeVerticalReader($request, $scriptPath);

            return new ScrapingResult($imageUrls, count($imageUrls));
        } catch (\Exception $e) {
            throw new \RuntimeException('JavaScript scraping failed: ' . $e->getMessage(), 0, $e);
        }
    }

    public function supports(string $sourceType): bool
    {
        return 'javascript' === $sourceType;
    }

    private function scrapeVerticalReader(ScrapingRequest $request, string $scriptPath): array
    {
        $params = $request->getScrapingParameters();
        $processArgs = [
            self::NODE_EXECUTABLE,
            $scriptPath,
            '--mode=vertical',
            '--url=' . $request->getChapterUrl(),
            '--image-selector=' . $params['imageSelector'],
            '--wait-for-images=true',
            '--scroll=true'
        ];

        // Ajouter les paramètres de chapitre si disponibles
        if (!empty($params['chapterSelector'])) {
            $processArgs[] = '--chapter-selector=' . $params['chapterSelector'];
        }

        if (isset($params['chapterNumber'])) {
            $processArgs[] = '--chapter-number=' . $params['chapterNumber'];
        }

        $process = new Process($processArgs);
        return $this->executeProcess($process);
    }

    private function scrapeHorizontalReader(ScrapingRequest $request, string $scriptPath): array
    {
        $params = $request->getScrapingParameters();

        $processArgs = [
            self::NODE_EXECUTABLE,
            $scriptPath,
            '--mode=horizontal',
            '--url=' . $request->getChapterUrl(),
            '--image-selector=' . $params['imageSelector'],
            '--next-selector=' . $params['nextPageSelector'],
            '--wait-for-images=true'
        ];

        // Ajouter les paramètres de chapitre si disponibles
        if (!empty($params['chapterSelector'])) {
            $processArgs[] = '--chapter-selector=' . $params['chapterSelector'];
        }

        if (isset($params['chapterNumber'])) {
            $processArgs[] = '--chapter-number=' . $params['chapterNumber'];
        }

        $process = new Process($processArgs);
        return $this->executeProcess($process);
    }

    private function executeProcess(Process $process): array
    {
        $process->setTimeout(self::PUPPETEER_TIMEOUT);
        $process->run();

        if (!$process->isSuccessful()) {
            $error = $process->getErrorOutput() ?: $process->getOutput();
            throw new \RuntimeException('Puppeteer process failed: ' . $error);
        }

        $output = $process->getOutput();
        $lines = explode("\n", trim($output));
        $resultLine = end($lines);

        // Gérer le cas où le chapitre n'est pas trouvé
        if (strpos($resultLine, 'CHAPTER_NOT_FOUND:') === 0) {
            $jsonData = substr($resultLine, 18); // Remove 'CHAPTER_NOT_FOUND:' prefix
            $errorData = json_decode($jsonData, true);

            if (is_array($errorData) && isset($errorData['message'])) {
                throw new ChapterNotFoundException($errorData['message']);
            }

            throw new ChapterNotFoundException('Le chapitre demandé n\'est pas disponible.');
        }

        // Gérer le cas normal avec des images
        if (strpos($resultLine, 'RESULT:') === 0) {
            $jsonData = substr($resultLine, 7); // Remove 'RESULT:' prefix
            $imageUrls = json_decode($jsonData, true);

            if (!is_array($imageUrls)) {
                throw new \RuntimeException('Failed to parse Puppeteer output');
            }

            return $this->cleanImageUrls($imageUrls);
        }

        // Format de sortie non reconnu
        throw new \RuntimeException('Invalid Puppeteer output format: ' . $resultLine);
    }

    private function cleanImageUrls(array $urls): array
    {
        return array_filter(
            array_map(
                fn ($url) => $this->cleanImageUrl($url),
                $urls
            ),
            fn ($url) => !empty($url) && filter_var($url, FILTER_VALIDATE_URL)
        );
    }

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