<?php

namespace App\Service;

use App\Entity\Manga;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\String\Slugger\SluggerInterface;

class CbzService
{
    public function __construct(private SluggerInterface $slugger)
    {
    }

    /**
     * @throws \Exception
     */
    public function extractMetadata(string $filePath, string $originalFileName): array
    {
        $zip = new \ZipArchive();

        $fileInfo = $this->extractInfoFromFileName($originalFileName);

        $metadata['title'] = $fileInfo['title'];
        $metadata['volume'] = null !== $fileInfo['volume'] ? (int) $fileInfo['volume'] : null;
        $metadata['chapter'] = null !== $fileInfo['chapter'] ? (int) $fileInfo['chapter'] : null;

        if (is_null($metadata['chapter'])) {
            try {
                $zip->open($filePath);
                $chapterNumbers = [];

                for ($i = 0; $i < $zip->numFiles; ++$i) {
                    $stat = $zip->statIndex($i);
                    $fileName = $stat['name'];

                    $chapterNumbers[] = $this->extractChapter($fileName);
                }

                $chapterNumbers = array_unique($chapterNumbers);

                if (1 === count($chapterNumbers)) {
                    $metadata['chapter'] = '' === array_values($chapterNumbers)[0] ? null : (int) array_values($chapterNumbers)[0];
                } elseif (count($chapterNumbers) > 1) {
                    $metadata['chapter'] = min($chapterNumbers);
                }

                $zip->close();
            } catch (\Exception $e) {
                throw new \Exception("Impossible d'ouvrir le fichier CBZ. ".$e->getMessage());
            }
        }

        return $metadata;
    }

    public function getPageContent(string $cbzPath, int $pageNumber): ?string
    {
        $zip = new \ZipArchive();
        if (true === $zip->open($cbzPath)) {
            $images = $this->getImageList($zip);
            if (isset($images[$pageNumber - 1])) {
                $content = $zip->getFromName($images[$pageNumber - 1]);
                $zip->close();

                return $content;
            }
            $zip->close();
        }

        return null;
    }

    public function getPageCount(string $cbzPath): int
    {
        $zip = new \ZipArchive();
        if (true === $zip->open($cbzPath)) {
            $count = count($this->getImageList($zip));
            $zip->close();

            return $count;
        }

        return 0;
    }

    private function extractInfoFromFileName(string $fileName): array
    {
        $title = $this->extractTitle($fileName);
        $volume = $this->extractVolume($fileName);
        $chapter = $this->extractChapter($fileName);

        return [
            'title' => '' === $title ? null : $title,
            'volume' => '' === $volume ? null : $volume,
            'chapter' => '' === $chapter ? null : $chapter,
        ];
    }

    private function extractTitle(string $fileName): string
    {
        $titlePattern = '/^(?P<title>.+?)(?:\s*-\s*|\s+)?(?:(?:[Tt]ome|[Vv]ol\.?|[Tt]|[Cc]hap(?:itre|ter)?)\s*\d+)/';
        if (preg_match($titlePattern, $fileName, $matches)) {
            return $this->slugger->slug(trim($matches['title']), '-')->lower()->toString();
        }

        $newFormatPattern = '/^(?P<title>.*?)_\d+/';
        if (preg_match($newFormatPattern, $fileName, $matches)) {
            return $this->slugger->slug(trim($matches['title']), '-')->lower()->toString();
        }

        return $this->slugger->slug(pathinfo($fileName, PATHINFO_FILENAME), '-')->lower()->toString();
    }

    private function extractVolume(string $fileName): string
    {
        $volumePattern = '/(?:[Tt]ome|[Vv]ol\.?|[Tt])\s*(?P<volume>\d+)/';
        if (preg_match($volumePattern, $fileName, $matches)) {
            return str_pad($matches['volume'], 2, '0', STR_PAD_LEFT);
        }

        return '';
    }

    private function extractChapter(string $fileName): string
    {
        $chapterPattern = '/[Cc]hap(?:itre|ter)?\s*(?P<chapter>\d+)/';
        if (preg_match($chapterPattern, $fileName, $matches)) {
            return $matches['chapter'];
        }

        $newFormatPattern = '/_(?P<chapter>\d+)(?:\.\w+)?$/';
        if (preg_match($newFormatPattern, $fileName, $matches)) {
            return $matches['chapter'];
        }

        return '';
    }

    private function getImageList(\ZipArchive $zip): array
    {
        $images = [];
        for ($i = 0; $i < $zip->numFiles; ++$i) {
            $filename = $zip->getNameIndex($i);
            if (preg_match('/\.(jpg|jpeg|png|gif)$/i', $filename)) {
                $images[] = $filename;
            }
        }
        sort($images);

        return $images;
    }

    public function createVolumeArchive(array $chapters): string
    {
        $tempFile = tempnam(sys_get_temp_dir(), 'volume_cbz_');
        $zip = new \ZipArchive();
        if (true !== $zip->open($tempFile, \ZipArchive::CREATE)) {
            throw new \RuntimeException('Impossible de créer le fichier ZIP temporaire.');
        }

        foreach ($chapters as $chapter) {
            $chapterZip = new \ZipArchive();
            if (true === $chapterZip->open($chapter->getCbzPath())) {
                for ($i = 0; $i < $chapterZip->numFiles; ++$i) {
                    $filename = $chapterZip->getNameIndex($i);
                    $fileContent = $chapterZip->getFromIndex($i);
                    $zip->addFromString('Chapter '.$chapter->getNumber().'/'.$filename, $fileContent);
                }
                $chapterZip->close();
            }
        }

        $zip->close();

        return $tempFile;
    }

    public function generateFileName(Manga $manga, int $volume = null, float $chapterNumber = null): string
    {
        $sluggedTitle = $this->slugger->slug($manga->getTitle())->lower();
        if (null !== $volume) {
            return sprintf('%s_volume_%02d.cbz', $sluggedTitle, $volume);
        } elseif (null !== $chapterNumber) {
            return sprintf('%s_chapter_%s.cbz', $sluggedTitle, number_format($chapterNumber, 2));
        } else {
            throw new \InvalidArgumentException('Either volume or chapter number must be provided');
        }
    }

    public function createBinaryFileResponse(string $filePath, string $fileName): BinaryFileResponse
    {
        $response = new BinaryFileResponse($filePath);
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $fileName
        );

        return $response;
    }

    public function areAllChaptersCbzIdentical(array $chapters): bool
    {
        if (empty($chapters)) {
            return false;
        }
        $firstCbzPath = $chapters[0]->getCbzPath();

        return array_reduce($chapters, function ($carry, $chapter) use ($firstCbzPath) {
            return $carry && $chapter->getCbzPath() === $firstCbzPath;
        }, true);
    }

    public function doAllChaptersHaveCbz(array $chapters): bool
    {
        return array_reduce($chapters, function ($carry, $chapter) {
            return $carry && null !== $chapter->getCbzPath();
        }, true);
    }
}
