<?php

namespace App\Services;

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

class PdfService {
    /**
     * 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);
        }

        $pdf = new Pdf($files);

        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, fn($p) => file_exists($p)));

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

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

        $pdf = new Pdf($valid);

        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 PDF.
     * The image is embedded as a base64 data URI to avoid any file:// restrictions.
     * 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}";

        $html = <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { background: #fff; display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; }
  img  { max-width: 100%; max-height: 100vh; 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)
            ->margins(10, 10, 10, 10)
            ->save(Storage::path($tempPath));

        return $tempPath;
    }
}
