<?php

declare(strict_types=1);

namespace App\Service;

class PdfCompressorService
{
    private ?bool $qpdfAvailable = null;

    /**
     * Compress an actual PDF file on disk. Returns metadata or null if no gain.
     *
     * @return null|array{size: int, originalSize: int, saved: int}
     */
    public function compressFile(string $absolutePath): ?array
    {
        if (!$this->isQpdfAvailable()) {
            return null;
        }

        if (!file_exists($absolutePath)) {
            return null;
        }

        $originalSize = filesize($absolutePath);
        if (false === $originalSize) {
            return null;
        }

        $tempOutput = tempnam(sys_get_temp_dir(), 'pdf_out_');

        $command = sprintf(
            'qpdf --linearize --object-streams=generate %s %s 2>&1',
            escapeshellarg($absolutePath),
            escapeshellarg($tempOutput)
        );

        exec($command, $cmdOutput, $returnCode);

        if (0 !== $returnCode || !file_exists($tempOutput)) {
            @unlink($tempOutput);

            return null;
        }

        $compressedSize = filesize($tempOutput);
        if (false === $compressedSize || $compressedSize >= $originalSize) {
            @unlink($tempOutput);

            return null;
        }

        if (!rename($tempOutput, $absolutePath)) {
            @unlink($tempOutput);

            return null;
        }

        return [
            'size'         => $compressedSize,
            'originalSize' => $originalSize,
            'saved'        => $originalSize - $compressedSize,
        ];
    }

    public function compressBase64Pdf(string $base64Data): ?array
    {
        if (!$this->isQpdfAvailable()) {
            return null;
        }

        // Remove data URI prefix if present
        $originalBase64 = $base64Data;
        if (str_contains($base64Data, ',')) {
            $base64Data = explode(',', $base64Data, 2)[1];
        }

        $pdfContent = base64_decode($base64Data, true);
        if (false === $pdfContent) {
            return null;
        }

        $originalSize = strlen($pdfContent);

        // Create temp files
        $tempInput  = tempnam(sys_get_temp_dir(), 'pdf_in_');
        $tempOutput = tempnam(sys_get_temp_dir(), 'pdf_out_');

        file_put_contents($tempInput, $pdfContent);

        // Compress with qpdf (lossless)
        $command = sprintf(
            'qpdf --linearize --object-streams=generate %s %s 2>&1',
            escapeshellarg($tempInput),
            escapeshellarg($tempOutput)
        );

        exec($command, $cmdOutput, $returnCode);

        if (0 !== $returnCode || !file_exists($tempOutput)) {
            @unlink($tempInput);
            @unlink($tempOutput);

            return null;
        }

        $compressedContent = file_get_contents($tempOutput);
        $compressedSize    = strlen($compressedContent);

        @unlink($tempInput);
        @unlink($tempOutput);

        // Only return compressed version if it's smaller
        if ($compressedSize >= $originalSize) {
            return null;
        }

        // Rebuild with data URI prefix
        $newBase64 = 'data:application/pdf;base64,'.base64_encode($compressedContent);

        return [
            'path'         => $newBase64,
            'size'         => $compressedSize,
            'originalSize' => $originalSize,
            'saved'        => $originalSize - $compressedSize,
        ];
    }

    private function isQpdfAvailable(): bool
    {
        if (null === $this->qpdfAvailable) {
            exec('which qpdf', $qpdfPath, $returnCode);
            $this->qpdfAvailable = 0 === $returnCode;
        }

        return $this->qpdfAvailable;
    }
}
