<?php

namespace App\Services;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Str;

/**
 * PDF合并工具类
 * 支持pdfunite（生产环境）和qpdf（本地开发）
 */
class PdfMerger
{
    private string $mergeTool;
    private bool $isProduction;

    public function __construct()
    {
        $this->isProduction = app()->environment('production');
        $this->mergeTool = $this->detectMergeTool();
    }

    /**
     * 检测系统中可用的PDF合并工具
     * 【修复】优先使用pdfunite，更简单可靠
     */
    private function detectMergeTool(): string
    {
        // 优先检测pdfunite（更简单可靠）
        if ($this->commandExists('pdfunite')) {
            Log::info('检测到pdfunite，将使用pdfunite进行PDF合并');
            return 'pdfunite';
        }

        // 备选qpdf
        if ($this->commandExists('qpdf')) {
            Log::info('未检测到pdfunite，使用qpdf作为备选');
            return 'qpdf';
        }

        throw new \Exception('未找到PDF合并工具（pdfunite或qpdf）');
    }

    /**
     * 检查命令是否存在
     */
    private function commandExists(string $command): bool
    {
        // 【修复】异步环境中PATH可能不完整，尝试绝对路径
        $fullPaths = [
            // 本地开发路径
            "/opt/homebrew/bin/{$command}",
            "/usr/local/bin/{$command}",
            // 服务器常见路径（Ubuntu/Debian/CentOS/RHEL）
            "/usr/bin/{$command}",
            "/usr/sbin/{$command}",
            "/usr/local/sbin/{$command}",
            // Docker/Laravel 容器常见路径
            "/bin/{$command}",
            "/sbin/{$command}",
            // Alpine Linux 容器
            "/usr/gnu/bin/{$command}",
            // 自定义安装路径
            "/opt/{$command}/bin/{$command}",
            "/usr/share/{$command}/{$command}",
        ];

        Log::debug("检查命令是否存在: {$command}", [
            'checking_absolute_paths' => $fullPaths
        ]);

        // 首先尝试绝对路径
        foreach ($fullPaths as $path) {
            if (file_exists($path) && is_executable($path)) {
                Log::debug("找到命令（绝对路径）: {$command} -> {$path}");
                return true;
            }
        }

        // 如果绝对路径都不行，再尝试which命令
        Log::debug("绝对路径未找到，尝试which命令: {$command}");
        $output = Process::run("which {$command}");
        $result = $output->successful();

        Log::debug("which命令结果", [
            'command' => $command,
            'successful' => $result,
            'output' => $output->output(),
            'error' => $output->errorOutput()
        ]);

        return $result;
    }

    /**
     * 合并多个PDF文件
     *
     * @param array $pdfPaths PDF文件路径数组
     * @param string $outputPath 输出文件路径
     * @return bool 合并是否成功
     * @throws \Exception
     */
    public function merge(array $pdfPaths, string $outputPath): bool
    {
        // 验证输入文件
        foreach ($pdfPaths as $path) {
            if (!file_exists($path)) {
                throw new \Exception("PDF文件不存在: {$path}");
            }
        }

        // 确保输出目录存在
        $outputDir = dirname($outputPath);
        if (!is_dir($outputDir)) {
            mkdir($outputDir, 0755, true);
        }

        Log::info('开始合并PDF', [
            'tool' => $this->mergeTool,
            'input_count' => count($pdfPaths),
            'output_path' => $outputPath
        ]);

        try {
            // 根据工具类型执行合并
            if ($this->mergeTool === 'pdfunite') {
                return $this->mergeWithPdfunite($pdfPaths, $outputPath, null);
            } else {
                return $this->mergeWithQpdf($pdfPaths, $outputPath, null);
            }
        } catch (\Exception $e) {
            Log::error('PDF合并失败', [
                'tool' => $this->mergeTool,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            throw $e;
        }
    }


    /**
     * 获取当前使用的合并工具
     */
    public function getMergeTool(): string
    {
        return $this->mergeTool;
    }

    /**
     * 检查环境是否支持PDF合并
     */
    public function isSupported(): bool
    {
        return in_array($this->mergeTool, ['pdfunite', 'qpdf']);
    }

    /**
     * 【新增】快速合并模式（带进度回调）
     *
     * @param array $pdfPaths PDF文件路径数组
     * @param string $outputPath 输出文件路径
     * @param callable|null $progressCallback 进度回调函数 (percentage, message) => void
     * @return bool 合并是否成功
     * @throws \Exception
     */
    public function mergeWithProgress(array $pdfPaths, string $outputPath, ?callable $progressCallback = null): bool
    {
        // 进度回调：开始
        if ($progressCallback) {
            $progressCallback(0, '开始合并PDF...');
        }

        // 验证输入文件
        foreach ($pdfPaths as $index => $path) {
            if (!file_exists($path)) {
                throw new \Exception("PDF文件不存在: {$path}");
            }
            // 进度回调：验证文件
            if ($progressCallback) {
                $progress = round(($index + 1) / (count($pdfPaths) + 1) * 20, 0); // 前20%用于验证
                $progressCallback($progress, "验证PDF文件: " . basename($path));
            }
        }

        // 确保输出目录存在
        $outputDir = dirname($outputPath);
        if (!is_dir($outputDir)) {
            mkdir($outputDir, 0755, true);
        }

        Log::info('开始快速合并PDF', [
            'tool' => $this->mergeTool,
            'input_count' => count($pdfPaths),
            'output_path' => $outputPath,
            'tool_selection' => $this->mergeTool === 'pdfunite' ? '优先使用pdfunite（更可靠）' : '使用qpdf作为备选'
        ]);

        try {
            // 进度回调：开始合并
            if ($progressCallback) {
                $toolName = $this->mergeTool === 'pdfunite' ? 'pdfunite' : 'qpdf';
                $progressCallback(20, "使用{$toolName}开始合并PDF...");
            }

            // 根据工具类型执行合并
            if ($this->mergeTool === 'pdfunite') {
                $result = $this->mergeWithPdfunite($pdfPaths, $outputPath, $progressCallback);
            } else {
                $result = $this->mergeWithQpdf($pdfPaths, $outputPath, $progressCallback);
            }

            // 进度回调：完成
            if ($progressCallback) {
                $progressCallback(100, 'PDF合并完成！');
            }

            return $result;
        } catch (\Exception $e) {
            Log::error('PDF合并失败', [
                'tool' => $this->mergeTool,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            if ($progressCallback) {
                $progressCallback(-1, 'PDF合并失败: ' . $e->getMessage());
            }

            throw $e;
        }
    }

    /**
     * 使用pdfunite合并PDF（带进度回调）
     * 【新增】pdfunite更简单可靠
     */
    private function mergeWithPdfunite(array $pdfPaths, string $outputPath, ?callable $progressCallback = null): bool
    {
        // 验证输入文件
        foreach ($pdfPaths as $index => $path) {
            if (!file_exists($path)) {
                Log::error('pdfunite合并失败：PDF文件不存在', [
                    'file_path' => $path,
                    'file_index' => $index,
                    'all_files' => $pdfPaths
                ]);

                if ($progressCallback) {
                    $progressCallback(-1, "PDF文件不存在: " . basename($path));
                }

                return false;
            }
            Log::debug('pdfunite验证文件存在', ['path' => $path, 'size' => filesize($path)]);
        }

        // 构建pdfunite命令：pdfunite file1.pdf file2.pdf output.pdf
        $filesArg = '';
        foreach ($pdfPaths as $path) {
            $filesArg .= escapeshellarg($path) . ' ';
        }
        $command = 'pdfunite ' . $filesArg . escapeshellarg($outputPath);

        Log::info('使用pdfunite合并PDF', [
            'command' => $command,
            'input_count' => count($pdfPaths),
            'output_path' => $outputPath
        ]);

        if ($progressCallback) {
            $progressCallback(30, '执行pdfunite命令...');
        }

        $startTime = microtime(true);
        $output = Process::timeout(60)->run($command);
        $duration = round((microtime(true) - $startTime) * 1000, 2);

        if ($progressCallback) {
            $progressCallback(80, '处理PDF合并结果...');
        }

        if ($output->successful()) {
            Log::info('pdfunite合并成功', [
                'output_path' => $outputPath,
                'duration_ms' => $duration,
                'file_count' => count($pdfPaths),
                'command' => $command
            ]);

            if ($progressCallback) {
                $progressCallback(95, "合并完成！耗时 {$duration}ms");
            }

            return true;
        }

        Log::error('pdfunite合并失败', [
            'exit_code' => $output->exitCode(),
            'output' => $output->output(),
            'error' => $output->errorOutput(),
            'duration_ms' => $duration,
            'command' => $command
        ]);

        if ($progressCallback) {
            $progressCallback(-1, 'pdfunite合并失败: ' . $output->errorOutput());
        }

        return false;
    }

    /**
     * 使用qpdf合并PDF（带进度回调）
     * 【修复】优化qpdf命令格式
     */
    private function mergeWithQpdf(array $pdfPaths, string $outputPath, ?callable $progressCallback = null): bool
    {
        // 【重要】验证输入文件是否存在
        foreach ($pdfPaths as $index => $path) {
            if (!file_exists($path)) {
                Log::error('qpdf合并失败：PDF文件不存在', [
                    'file_path' => $path,
                    'file_index' => $index,
                    'all_files' => $pdfPaths,
                    'file_exists_check' => file_exists($path),
                    'directory' => dirname($path),
                    'directory_exists' => is_dir(dirname($path)),
                    'directory_contents' => is_dir(dirname($path)) ? scandir(dirname($path)) : 'N/A'
                ]);

                if ($progressCallback) {
                    $progressCallback(-1, "PDF文件不存在: " . basename($path));
                }

                return false;
            }
            Log::debug('qpdf验证文件存在', ['path' => $path, 'size' => filesize($path)]);
        }

        // 【最终修复】qpdf命令格式问题 - 查阅qpdf手册
        // 正确格式应该是：qpdf --empty --pages file1.pdf,file2.pdf z -- output.pdf
        // 注意：qpdf需要在最后添加页面范围（如z表示最后一页）

        // 【最终修复】qpdf命令格式问题
        // 正确格式：qpdf --empty --pages file1.pdf,file2.pdf -- output.pdf
        // 注意：qpdf需要为每个PDF指定页面范围，如1-z表示所有页面

        // 使用逗号分隔文件，并为每个文件指定页面范围1-z（所有页面）
        $pagesArg = '';
        foreach ($pdfPaths as $path) {
            $pagesArg .= escapeshellarg($path) . '1-z,';
        }
        // 移除末尾的逗号
        $pagesArg = rtrim($pagesArg, ',');
        $command = "qpdf --empty --pages {$pagesArg} -- -- " . escapeshellarg($outputPath);

        Log::info('【最终修复】qpdf命令格式 - 为每个PDF指定页面范围', [
            'command' => $command,
            'pages_arg' => $pagesArg,
            'note' => 'qpdf需要为每个PDF指定页面范围（如1-z），使用逗号分隔文件'
        ]);

        // 【优化】设置超时时间为60秒，避免无限等待
        $timeout = 60;

        if ($progressCallback) {
            $progressCallback(30, '执行qpdf命令（修复版）...');
        }

        $startTime = microtime(true);
        $output = Process::timeout($timeout)->run($command);
        $duration = round((microtime(true) - $startTime) * 1000, 2);

        if ($progressCallback) {
            $progressCallback(80, '处理PDF合并结果...');
        }

        if ($output->successful()) {
            Log::info('qpdf合并成功', [
                'output_path' => $outputPath,
                'duration_ms' => $duration,
                'file_count' => count($pdfPaths),
                'final_command' => $command
            ]);

            if ($progressCallback) {
                $progressCallback(95, "合并完成！耗时 {$duration}ms");
            }

            return true;
        }

        Log::error('qpdf合并失败', [
            'exit_code' => $output->exitCode(),
            'output' => $output->output(),
            'error' => $output->errorOutput(),
            'duration_ms' => $duration,
            'timeout_seconds' => $timeout,
            'final_command' => $command,
            'input_files' => $pdfPaths,
            'output_file' => $outputPath,
            'note' => 'qpdf需要为每个PDF指定页面范围（如1-z），用逗号分隔文件'
        ]);

        if ($progressCallback) {
            $progressCallback(-1, 'qpdf合并失败: ' . $output->errorOutput());
        }

        return false;
    }
}
