<?php

namespace App\Services;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use mikehaertl\pdftk\Pdf;
use Spatie\Browsershot\Browsershot;

class PdfService {

    /**
     * Apply environment-specific Chrome/Browsershot configuration.
     *
     * All settings are driven by config/pdf.php (see that file for the
     * full list of env vars). When no env vars are set (local development),
     * this method is a no-op and Browsershot auto-detects Chrome.
     */
    public function configureBrowsershot(Browsershot $browsershot): void {
        if ($path = config('pdf.chrome_path')) {
            $browsershot->setChromePath($path);
        }

        if (config('pdf.chrome_no_sandbox')) {
            $browsershot->noSandbox();
        }

        $chromeBase = storage_path('app/chrome');

        $browsershot
            ->setUserDataDir($chromeBase . '/data')
            ->setEnvironmentOptions([
                'HOME' => $chromeBase,
                'XDG_CONFIG_HOME' => $chromeBase . '/config',
                'XDG_CACHE_HOME' => $chromeBase . '/cache',
                'CHROME_CONFIG_HOME' => $chromeBase . '/config',
            ]);

        $browsershot
            ->addChromiumArguments([
                '--disable-dev-shm-usage',
                '--disable-gpu',
                '--headless=new',
            ]);
    }

    /**
     * Merge multiple PDFs into one.
     */
    public function merge(array $paths, string $outputPath): string {
        $files = collect($paths)
            ->filter(fn($path) => Storage::exists($path))
            ->map(fn($path) => Storage::path($path))
            ->values()
            ->toArray();

        if (empty($files)) {
            throw new \Exception('No valid PDF files provided for merge.');
        }

        $dir = dirname($outputPath);
        if (!Storage::exists($dir)) {
            Storage::makeDirectory($dir);
        }

        $options = [];
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            $options = [
                'command' => 'C:\Program Files (x86)\PDFtk\bin\pdftk.exe',
                'useExec' => true
            ];
        }

        $pdf = new Pdf($files, $options);

        if (!$pdf->cat()->saveAs(Storage::path($outputPath))) {
            throw new \Exception('PDF merge failed: ' . $pdf->getError());
        }

        return $outputPath;
    }

    /**
     * Check whether a PDF exists in storage.
     */
    public function exists(string $path): bool {
        return Storage::exists($path);
    }

    /**
     * Add a text watermark (background stamp).
     */
    public function watermark(string $inputPath, string $outputPath, string $text): string {
        if (!Storage::exists($inputPath)) {
            throw new \Exception('Input PDF does not exist.');
        }

        $stampPath = storage_path('app/temp_stamp.pdf');

        // Create simple stamp PDF using pdftk background method
        $stamp = new Pdf();
        $stamp->addPage();
        $stamp->setData(['watermark' => $text]);
        $stamp->saveAs($stampPath);

        $pdf = new Pdf(Storage::path($inputPath));

        if (!$pdf->background($stampPath)->saveAs(Storage::path($outputPath))) {
            throw new \Exception('Watermark failed: ' . $pdf->getError());
        }

        @unlink($stampPath);

        return $outputPath;
    }

    /**
     * Split a PDF into individual pages.
     */
    public function split(string $inputPath, string $outputDirectory): array {
        if (!Storage::exists($inputPath)) {
            throw new \Exception('Input PDF does not exist.');
        }

        $pdf = new Pdf(Storage::path($inputPath));
        $pageCount = $pdf->getPageCount();

        $outputPaths = [];

        for ($i = 1; $i <= $pageCount; $i++) {
            $outputPath = rtrim($outputDirectory, '/') . "/page_{$i}.pdf";

            $pagePdf = new Pdf(Storage::path($inputPath));

            if (!$pagePdf->cat($i)->saveAs(Storage::path($outputPath))) {
                throw new \Exception('Failed splitting page ' . $i . ': ' . $pagePdf->getError());
            }

            $outputPaths[] = $outputPath;
        }

        return $outputPaths;
    }

    /**
     * Flatten a PDF (removes form fields).
     */
    public function flatten(string $inputPath, string $outputPath): string {
        if (!Storage::exists($inputPath)) {
            throw new \Exception('Input PDF does not exist.');
        }

        $pdf = new Pdf(Storage::path($inputPath));

        if (!$pdf->flatten()->saveAs(Storage::path($outputPath))) {
            throw new \Exception('Flatten failed: ' . $pdf->getError());
        }

        return $outputPath;
    }

    /**
     * Stream PDF to browser (inline).
     */
    public function stream(string $path, ?string $filename = null) {
        if (!Storage::exists($path)) {
            abort(404);
        }

        $filename ??= basename($path);

        return response()->streamDownload(function () use ($path) {
            echo Storage::get($path);
        }, $filename, [
            'Content-Type' => 'application/pdf',
        ]);
    }

    /**
     * Force-download a PDF as an attachment.
     */
    public function download(string $path, ?string $filename = null) {
        if (!Storage::exists($path)) {
            abort(404);
        }

        $filename ??= basename($path);

        return response()->streamDownload(function () use ($path) {
            echo Storage::get($path);
        }, $filename, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
        ]);
    }

    /**
     * Get the number of pages in a PDF.
     */
    public function pageCount(string $path): int {
        if (!Storage::exists($path)) {
            throw new \Exception('PDF does not exist: ' . $path);
        }

        $pdf = new Pdf(Storage::path($path));
        $count = $pdf->getPageCount();

        if ($count === false) {
            throw new \Exception('Could not read page count: ' . $pdf->getError());
        }

        return $count;
    }

    /**
     * Delete a PDF from storage.
     */
    public function delete(string $path): void {
        Storage::delete($path);
    }

    /**
     * Generate a unique temporary storage path.
     * The file is not created — this just reserves a path.
     */
    public function temp(?string $prefix = null): string {
        $name = ($prefix ? $prefix . '-' : '') . Str::uuid() . '.pdf';

        return 'temp/' . $name;
    }

    /**
     * Merge files given as absolute filesystem paths into a storage-relative output path.
     * Useful when files span multiple disks (e.g. combining public-disk documents with
     * default-disk generated PDFs).
     */
    public function mergeAbsolute(array $absolutePaths, string $outputStoragePath): string {
        $valid = array_values(array_filter($absolutePaths, function ($p) {
            if (!file_exists($p)) {
                return false;
            }
            $header = @file_get_contents($p, false, null, 0, 4);
            return $header === '%PDF';
        }));

        if (empty($valid)) {
            throw new \Exception('No valid PDF files to merge.');
        }

        $dir = dirname($outputStoragePath);
        if (!Storage::exists($dir)) {
            Storage::makeDirectory($dir);
        }

        $options = [];
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            $options = [
                'command' => 'C:\Program Files (x86)\PDFtk\bin\pdftk.exe',
                'useExec' => true
            ];
        }

        $pdf = new Pdf($valid, $options);

        if (!$pdf->cat()->saveAs(Storage::path($outputStoragePath))) {
            throw new \Exception('PDF merge failed: ' . $pdf->getError());
        }

        return $outputStoragePath;
    }

    /**
     * Convert an image file (given as an absolute filesystem path) into a single-page A4 PDF.
     * The image is referenced via a file:// URL so Chrome reads it directly — avoids the
     * memory and temp-file overhead of embedding as a base64 data URI.
     * Returns the storage-relative path of the temp PDF — caller must delete it when done.
     */
    public function imageToPdf(string $absoluteImagePath, string $mimeType): string {
        if (!file_exists($absoluteImagePath)) {
            throw new \Exception("Image file not found: {$absoluteImagePath}");
        }

        $base64 = base64_encode(file_get_contents($absoluteImagePath));
        $dataUri = "data:{$mimeType};base64,{$base64}";

        // A4 at 96 dpi: 794 × 1123 px.  The viewport drives the PDF page dimensions
        // in Browsershot — @page CSS alone is not enough.  Padding (≈57px ≈ 15mm)
        // keeps the image away from the page edge.
        $html = <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
  @page  { size: A4 portrait; margin: 0; }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { width: 794px; height: 1123px; background: #fff; overflow: hidden; }
  body { display: flex; justify-content: center; align-items: center; padding: 57px; }
  img  { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
</style>
</head>
<body><img src="{$dataUri}"></body>
</html>
HTML;

        $tempPath = $this->temp('img');

        if (!Storage::exists('temp')) {
            Storage::makeDirectory('temp');
        }

        \Spatie\LaravelPdf\Facades\Pdf::html($html)
            ->format('A4')
            ->margins(0, 0, 0, 0)
            ->withBrowsershot(function (Browsershot $b) {
                $b->windowSize(794, 1123);
                $this->configureBrowsershot($b);
            })
            ->save(Storage::path($tempPath));

        return $tempPath;
    }
}
