<?php

namespace App\Services;

use setasign\Fpdi\Fpdi;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;

/**
 * Extended FPDI with alpha (transparency), rotation support, and watermark logic.
 * Applies a diagonal semi-transparent watermark + footer to each page of a PDF.
 */
class PdfWatermarkService extends Fpdi
{
    protected $extgstates = [];
    protected $angle = 0;

    /**
     * Apply watermark to a PDF file.
     *
     * @param string $storagePath  Path relative to Storage (e.g. 'private/media/file.pdf')
     * @param string $userName     Full name of the downloading user
     * @param string $companyName  Company / member organisation name
     * @param string $dateTime     Formatted date-time string
     * @return string|null         Absolute path to the temporary watermarked PDF, or null on failure
     */
    public function apply(string $storagePath, string $userName, string $companyName, string $dateTime): ?string
    {
        $absolutePath = Storage::path($storagePath);
        $convertedPath = null;

        try {
            $sourcePath = $absolutePath;

            // Try to open the PDF — if it fails (e.g. PDF 1.5+), downgrade with Ghostscript
            try {
                $pageCount = $this->setSourceFile($sourcePath);
            } catch (\Exception $e) {
                Log::info('PdfWatermarkService: PDF not compatible with FPDI, attempting Ghostscript conversion.', [
                    'file' => $storagePath,
                    'error' => $e->getMessage(),
                ]);

                $convertedPath = $this->convertToCompatiblePdf($absolutePath);

                if (!$convertedPath) {
                    throw new \RuntimeException('Ghostscript conversion failed.');
                }

                $sourcePath = $convertedPath;
                $pageCount = $this->setSourceFile($sourcePath);
            }

            $footerText = "Downloaded by {$companyName} ({$userName}) on {$dateTime}";

            for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
                $templateId = $this->importPage($pageNo);
                $size = $this->getTemplateSize($templateId);

                $this->AddPage($size['orientation'], [$size['width'], $size['height']]);
                $this->useTemplate($templateId, 0, 0, $size['width'], $size['height']);

                $pageWidth = $size['width'];
                $pageHeight = $size['height'];

                // --- Header watermark (small text, centered) ---
                $this->addHeaderWatermark($pageWidth, $footerText);

                // --- Footer watermark (small text, centered) ---
                $this->addFooterWatermark($pageWidth, $pageHeight, $footerText);

                // Reset alpha for next page
                $this->SetAlpha(1);
            }

            // Write to a temporary file
            $tempDir = Storage::path('temp');
            if (!is_dir($tempDir)) {
                mkdir($tempDir, 0755, true);
            }

            $tempPath = $tempDir . '/' . uniqid('watermarked_', true) . '.pdf';
            $this->Output('F', $tempPath);

            return $tempPath;
        } catch (\Exception $e) {
            Log::warning('PdfWatermarkService: Failed to apply watermark, falling back to original PDF.', [
                'file' => $storagePath,
                'error' => $e->getMessage(),
            ]);

            return null;
        } finally {
            // Clean up the Ghostscript-converted temp file if it was created
            if ($convertedPath && file_exists($convertedPath)) {
                @unlink($convertedPath);
            }
        }
    }

    /**
     * Use Ghostscript to convert a PDF to version 1.4 (compatible with FPDI free).
     *
     * @param string $inputPath  Absolute path to the original PDF
     * @return string|null       Absolute path to the converted PDF, or null on failure
     */
    private function convertToCompatiblePdf(string $inputPath): ?string
    {
        $tempDir = Storage::path('temp');
        if (!is_dir($tempDir)) {
            mkdir($tempDir, 0755, true);
        }

        $outputPath = $tempDir . '/' . uniqid('gs_converted_', true) . '.pdf';

        $command = sprintf(
            'gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dNOPAUSE -dQUIET -dBATCH -sOutputFile=%s %s 2>&1',
            escapeshellarg($outputPath),
            escapeshellarg($inputPath)
        );

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

        if ($returnCode !== 0 || !file_exists($outputPath)) {
            Log::warning('PdfWatermarkService: Ghostscript conversion failed.', [
                'command' => $command,
                'output' => implode("\n", $output),
                'returnCode' => $returnCode,
            ]);
            return null;
        }

        return $outputPath;
    }

    // ---------------------------------------------------------------
    //  Watermark drawing helpers
    // ---------------------------------------------------------------

    private function addHeaderWatermark(float $pageWidth, string $text): void
    {
        $this->SetAlpha(0.5);
        $this->SetFont('Helvetica', '', 8);
        $this->SetTextColor(100, 100, 100);

        $textWidth = $this->GetStringWidth($text);
        $this->Text(($pageWidth - $textWidth) / 2, 8, $text);
    }

    private function addFooterWatermark(float $pageWidth, float $pageHeight, string $text): void
    {
        $this->SetAlpha(0.5);
        $this->SetFont('Helvetica', '', 8);
        $this->SetTextColor(100, 100, 100);

        // Use Text() for absolute positioning — avoids triggering page breaks
        $textWidth = $this->GetStringWidth($text);
        $this->Text(($pageWidth - $textWidth) / 2, $pageHeight - 5, $text);
    }

    // ---------------------------------------------------------------
    //  Alpha (transparency) support via PDF ExtGState
    // ---------------------------------------------------------------

    public function SetAlpha(float $alpha, string $bm = 'Normal'): void
    {
        $gs = $this->addExtGState(['ca' => $alpha, 'CA' => $alpha, 'BM' => '/' . $bm]);
        $this->setExtGState($gs);
    }

    private function addExtGState(array $parms): int
    {
        $n = count($this->extgstates) + 1;
        $this->extgstates[$n] = ['parms' => $parms];
        return $n;
    }

    private function setExtGState(int $gs): void
    {
        $this->_out(sprintf('/GS%d gs', $gs));
    }

    protected function _putextgstates(): void
    {
        foreach ($this->extgstates as $i => $ext) {
            $this->_newobj();
            $this->extgstates[$i]['n'] = $this->n;
            $this->_put('<</Type /ExtGState');
            $parms = $ext['parms'];
            $this->_put(sprintf('/ca %.3F', $parms['ca']));
            $this->_put(sprintf('/CA %.3F', $parms['CA']));
            $this->_put('/BM ' . $parms['BM']);
            $this->_put('>>');
            $this->_put('endobj');
        }
    }

    protected function _putresourcedict(): void
    {
        parent::_putresourcedict();
        $this->_put('/ExtGState <<');
        foreach ($this->extgstates as $k => $extgstate) {
            $this->_put('/GS' . $k . ' ' . $extgstate['n'] . ' 0 R');
        }
        $this->_put('>>');
    }

    protected function _putresources(): void
    {
        $this->_putextgstates();
        parent::_putresources();
    }

    // ---------------------------------------------------------------
    //  Rotation support
    // ---------------------------------------------------------------

    public function Rotate(float $angle, float $x = -1, float $y = -1): void
    {
        if ($x == -1) $x = $this->x;
        if ($y == -1) $y = $this->y;

        if ($this->angle != 0) {
            $this->_out('Q');
        }

        $this->angle = $angle;

        if ($angle != 0) {
            $angleRad = deg2rad($angle);
            $c = cos($angleRad);
            $s = sin($angleRad);
            $cx = $x * $this->k;
            $cy = ($this->h - $y) * $this->k;
            $this->_out(sprintf(
                'q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm',
                $c, $s, -$s, $c,
                $cx, $cy,
                -$cx, -$cy
            ));
        }
    }

    public function _endpage(): void
    {
        if ($this->angle != 0) {
            $this->angle = 0;
            $this->_out('Q');
        }
        parent::_endpage();
    }
}
