<?php

declare(strict_types=1);

namespace App\Modules\Reporting\Application\Services;

use App\Shared\Support\RemotePdfService;

final class ReportFileExportService
{
    /** @param array<string,string|int|float> $summary @param array<int,string> $headers @param array<int,array<int,string|int|float|null>> $rows */
    public function sendExcel(string $filename, string $title, array $summary, array $headers, array $rows): void
    {
        $filename = $this->safeFilename($filename, 'report') . '.xls';
        header('Content-Type: application/vnd.ms-excel; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        echo "\xEF\xBB\xBF";
        echo '<!doctype html><html lang="ar" dir="rtl"><head><meta charset="utf-8"><style>';
        echo 'body{font-family:Arial,Tahoma,sans-serif;direction:rtl;}table{border-collapse:collapse;width:100%;direction:rtl;}th,td{border:1px solid #cbd5e1;padding:7px;text-align:center;mso-number-format:"\\@";}th{background:#16324a;color:#fff;font-weight:bold}.title{font-size:18px;font-weight:bold;background:#f8fafc}.summary td{font-weight:bold;background:#f8fafc}.num{direction:ltr;text-align:center}';
        echo '</style></head><body>';
        echo '<table><tr><td class="title" colspan="' . max(1, count($headers)) . '">' . $this->esc($title) . '</td></tr>';
        if ($summary !== []) {
            foreach ($summary as $k => $v) {
                echo '<tr class="summary"><td>' . $this->esc((string)$k) . '</td><td colspan="' . max(1, count($headers) - 1) . '">' . $this->esc((string)$v) . '</td></tr>';
            }
        }
        echo '</table><br><table><thead><tr>';
        foreach ($headers as $h) { echo '<th>' . $this->esc((string)$h) . '</th>'; }
        echo '</tr></thead><tbody>';
        foreach ($rows as $row) {
            echo '<tr>';
            foreach ($row as $cell) {
                echo '<td>' . $this->esc((string)($cell ?? '')) . '</td>';
            }
            echo '</tr>';
        }
        echo '</tbody></table></body></html>';
        exit;
    }

    public function sendPdfFromHtml(string $html, string $filename): void
    {
        $bytes = $this->pdfBytesFromHtml($html);
        if ($bytes === null) {
            http_response_code(500);
            header('Content-Type: text/plain; charset=UTF-8');
            echo 'تعذر إنشاء ملف PDF على هذا السيرفر. تأكد من ضبط PDF_SERVICE_URL / PDF_SERVICE_TOKEN أو توفر Chrome/Edge Headless.';
            exit;
        }
        $filename = $this->safeFilename($filename, 'report') . '.pdf';
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . strlen($bytes));
        echo $bytes;
        exit;
    }

    public function pdfBytesFromHtml(string $html): ?string
    {
        $remoteBytes = (new RemotePdfService())->render($html, 'report.pdf');
        if ($remoteBytes !== null) {
            return $remoteBytes;
        }

        $tempDir = sys_get_temp_dir();
        $id = 'report_' . bin2hex(random_bytes(6));
        $htmlFile = $tempDir . DIRECTORY_SEPARATOR . $id . '.html';
        $pdfFile = $tempDir . DIRECTORY_SEPARATOR . $id . '.pdf';
        file_put_contents($htmlFile, $this->localizeAssetUrls($html));
        try {
            $browser = $this->findExecutable($this->headlessBrowserCandidates());
            if ($browser !== null) {
                $cmd = escapeshellarg($browser)
                    . ' --headless=new --disable-gpu --disable-dev-shm-usage --allow-file-access-from-files --no-pdf-header-footer --print-to-pdf-no-header'
                    . ' --print-to-pdf=' . escapeshellarg($pdfFile)
                    . ' ' . escapeshellarg('file:///' . str_replace('\\', '/', $htmlFile));
                $this->runCommand($cmd);
                if (is_file($pdfFile)) {
                    $bytes = (string)file_get_contents($pdfFile);
                    return $bytes !== '' ? $bytes : null;
                }
            }
            return null;
        } finally {
            @unlink($htmlFile);
            @unlink($pdfFile);
        }
    }

    private function localizeAssetUrls(string $html): string
    {
        $public = realpath(APP_BASE_PATH . '/public');
        if ($public === false) { return $html; }
        $replace = static function (array $m) use ($public): string {
            $attr = $m[1];
            $url = html_entity_decode($m[2], ENT_QUOTES | ENT_HTML5, 'UTF-8');
            if (preg_match('~^(data:|file:|https?://)~i', $url)) { return $m[0]; }
            $path = parse_url($url, PHP_URL_PATH) ?: $url;
            $path = preg_replace('~^.*?/public/~', '/', $path);
            $path = ltrim((string)$path, '/');
            $file = $public . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
            if (!is_file($file)) { return $m[0]; }
            return $attr . '="file:///' . str_replace('\\', '/', $file) . '"';
        };
        return (string)preg_replace_callback('~(src|href)=["\']([^"\']+)["\']~i', $replace, $html);
    }

    /** @param array<int,string> $candidates */
    private function findExecutable(array $candidates): ?string
    {
        foreach ($candidates as $candidate) {
            if ($candidate === '') { continue; }
            if (str_contains($candidate, DIRECTORY_SEPARATOR) || str_contains($candidate, ':')) {
                if (is_file($candidate) && is_executable($candidate)) { return $candidate; }
                if (is_file($candidate)) { return $candidate; }
                continue;
            }
            $cmd = stripos(PHP_OS_FAMILY, 'Windows') === 0 ? 'where ' . escapeshellarg($candidate) : 'command -v ' . escapeshellarg($candidate);
            $out = [];
            @exec($cmd, $out);
            if (!empty($out[0])) { return trim((string)$out[0]); }
        }
        return null;
    }

    /** @return array<int,string> */
    private function headlessBrowserCandidates(): array
    {
        return [
            'google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'microsoft-edge', 'msedge',
            'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
            'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
            'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
            'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
        ];
    }

    private function runCommand(string $cmd): bool
    {
        $output = [];
        $code = 1;
        @exec($cmd . ' 2>&1', $output, $code);
        return $code === 0;
    }

    private function safeFilename(string $filename, string $fallback): string
    {
        $filename = trim($filename) !== '' ? trim($filename) : $fallback;
        $filename = preg_replace('/[^A-Za-z0-9._-]+/', '-', $filename) ?: $fallback;
        return trim($filename, '-_.') ?: $fallback;
    }

    private function esc(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
}
