<?php

namespace MintyPHP\Service\User;

use Dompdf\Dompdf;
use Dompdf\Options;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use MintyPHP\Service\Branding\BrandingLogoService;
use MintyPHP\Service\Tenant\TenantLogoService;
use Throwable;
use ZipArchive;

class UserAccessPdfService
{
    public function __construct(
        private readonly UserAccessTemplateService $userAccessTemplateService,
        private readonly BrandingLogoService $brandingLogoService,
        private readonly ?TenantLogoService $tenantLogoService = null
    ) {
    }

    // Returns PDF bytes for one user; empty string means rendering failed.
    public function renderUserAccessPdf(array $user): string
    {
        if (!class_exists(Dompdf::class) || !class_exists(Options::class)) {
            return '';
        }

        $context = $this->userAccessTemplateService->buildContext($user);
        $locale = (string) ($context['locale'] ?? '');
        $vars = is_array($context['vars'] ?? null) ? $context['vars'] : [];
        $vars['pdf_title'] = self::buildPdfTitle($locale);
        $tenantUuid = (string) ($user['tenant_uuid'] ?? $user['primary_tenant_uuid'] ?? '');
        $vars['app_logo_url'] = $this->resolveLogoDataUri($tenantUuid);
        $vars['login_qr_data_uri'] = self::buildLoginQrDataUri((string) ($vars['login_url'] ?? ''));
        $vars['generated_at'] = gmdate('Y-m-d H:i:s') . ' UTC';

        $html = self::renderTemplate('access_info', $vars, $locale);
        if ($html === '') {
            return '';
        }

        try {
            $options = new Options();
            self::configurePdfOptions($options);
            $dompdf = new Dompdf($options);
            $dompdf->loadHtml($html, 'UTF-8');
            $dompdf->setPaper('A4');
            $dompdf->render();
            $dompdf->addInfo('Title', (string) $vars['pdf_title']);
            $dompdf->addInfo('Subject', (string) $vars['pdf_title']);
            return $dompdf->output();
        } catch (Throwable $e) {
            return '';
        }
    }

    // Creates one PDF per user and packs them into a temp ZIP file.
    // Caller is responsible for deleting the returned ZIP path after download.
    public function renderUsersAccessZip(array $users): array
    {
        if (!class_exists(ZipArchive::class) || !$users) {
            return ['path' => '', 'filename' => '', 'count' => 0];
        }

        $zipPath = tempnam(sys_get_temp_dir(), 'access_pdfs_');
        if ($zipPath === false) {
            return ['path' => '', 'filename' => '', 'count' => 0];
        }

        $zip = new ZipArchive();
        if ($zip->open($zipPath, ZipArchive::OVERWRITE | ZipArchive::CREATE) !== true) {
            @unlink($zipPath);
            return ['path' => '', 'filename' => '', 'count' => 0];
        }

        $usedNames = [];
        $count = 0;
        foreach ($users as $user) {
            if (!is_array($user)) {
                continue;
            }
            $pdf = $this->renderUserAccessPdf($user);
            if ($pdf === '') {
                continue;
            }
            $filename = self::buildPdfEntryName($user, $usedNames, $count + 1);
            if (!$zip->addFromString($filename, $pdf)) {
                continue;
            }
            $count++;
        }

        $closed = $zip->close();
        if (!$closed || $count === 0) {
            @unlink($zipPath);
            return ['path' => '', 'filename' => '', 'count' => 0];
        }

        return [
            'path' => $zipPath,
            'filename' => 'user-invitations-' . gmdate('Ymd-His') . '.zip',
            'count' => $count,
        ];
    }

    private static function configurePdfOptions(Options $options): void
    {
        // Keep PDF rendering deterministic and avoid remote/network execution.
        $options->setIsRemoteEnabled(false);
        $options->setIsPhpEnabled(false);
        $options->setDefaultFont('DejaVu Sans');
    }

    private static function renderTemplate(string $template, array $vars, string $locale): string
    {
        $base = dirname(__DIR__, 3) . '/templates';
        $pdfBase = $base . '/pdfs';
        $emailBase = $base . '/emails';

        $htmlPath = $pdfBase . '/' . $locale . '/' . $template . '.html';
        if (!is_file($htmlPath)) {
            $htmlPath = $pdfBase . '/' . $template . '.html';
        }
        if (!is_file($htmlPath)) {
            return '';
        }

        // Priority: locale-specific PDF partials -> locale email partials -> generic email partials.
        $htmlHeaderPath = $pdfBase . '/' . $locale . '/partials/header.html';
        $htmlFooterPath = $pdfBase . '/' . $locale . '/partials/footer.html';
        if (!is_file($htmlHeaderPath)) {
            $htmlHeaderPath = $emailBase . '/' . $locale . '/partials/header.html';
        }
        if (!is_file($htmlFooterPath)) {
            $htmlFooterPath = $emailBase . '/' . $locale . '/partials/footer.html';
        }
        $html = (string) file_get_contents($htmlPath);
        if (!isset($vars['email_header'])) {
            $vars['email_header'] = is_file($htmlHeaderPath) ? (string) file_get_contents($htmlHeaderPath) : '';
        }
        if (!isset($vars['email_footer'])) {
            $vars['email_footer'] = is_file($htmlFooterPath) ? (string) file_get_contents($htmlFooterPath) : '';
        }

        foreach ($vars as $key => $value) {
            $html = str_replace('{{' . $key . '}}', (string) $value, $html);
        }
        // Second pass resolves placeholders that may appear inside injected header/footer snippets.
        foreach ($vars as $key => $value) {
            $html = str_replace('{{' . $key . '}}', (string) $value, $html);
        }

        return $html;
    }

    private function resolveLogoDataUri(string $tenantUuid = ''): string
    {
        $path = '';
        $mime = '';

        // Prefer tenant light-logo if available (PDF has no theme context; always-light).
        if ($this->tenantLogoService !== null && $tenantUuid !== '' && $this->tenantLogoService->hasLogo($tenantUuid, TenantLogoService::THEME_LIGHT)) {
            $logoPath = $this->tenantLogoService->findLogoPath($tenantUuid, TenantLogoService::THEME_LIGHT, 128);
            if ($logoPath && is_file($logoPath)) {
                $path = $logoPath;
                $mime = $this->tenantLogoService->detectMime($logoPath);
            }
        }

        // Fallback to global app-branding logo.
        if ($path === '' && $this->brandingLogoService->hasLogo()) {
            $logoPath = $this->brandingLogoService->findLogoPath(128);
            if ($logoPath && is_file($logoPath)) {
                $path = $logoPath;
                $mime = $this->brandingLogoService->detectMime($logoPath);
            }
        }

        // Fallback to static brand logo from web assets.
        if ($path === '') {
            $fallbackPath = dirname(__DIR__, 3) . '/web/brand/logo.svg';
            if (is_file($fallbackPath)) {
                $path = $fallbackPath;
                $mime = 'image/svg+xml';
            }
        }

        if ($path === '' || !is_file($path)) {
            return self::transparentPixelDataUri();
        }

        $content = @file_get_contents($path);
        if ($content === false || $content === '') {
            return self::transparentPixelDataUri();
        }

        if ($mime === '') {
            $mime = 'image/png';
        }
        return 'data:' . $mime . ';base64,' . base64_encode($content);
    }

    private static function buildLoginQrDataUri(string $loginUrl): string
    {
        $loginUrl = trim($loginUrl);
        if ($loginUrl === '') {
            return self::transparentPixelDataUri();
        }
        if (!class_exists(QrCode::class) || !class_exists(PngWriter::class)) {
            return self::transparentPixelDataUri();
        }

        try {
            $qrCode = new QrCode(data: $loginUrl, size: 180, margin: 8);

            $writer = new PngWriter();
            $result = $writer->write($qrCode);
            $png = (string) $result->getString();
            if ($png === '') {
                return self::transparentPixelDataUri();
            }
            return 'data:image/png;base64,' . base64_encode($png);
        } catch (Throwable $e) {
            return self::transparentPixelDataUri();
        }
    }

    private static function transparentPixelDataUri(): string
    {
        // Smallest safe image placeholder to keep <img> tags valid in PDF templates.
        return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4////fwAJ+wP9KobjigAAAABJRU5ErkJggg==';
    }

    private static function buildPdfEntryName(array $user, array &$usedNames, int $fallbackIndex): string
    {
        $parts = [
            (string) ($user['first_name'] ?? ''),
            (string) ($user['last_name'] ?? ''),
        ];
        if (trim(implode('', $parts)) === '' && !empty($user['email'])) {
            $parts[] = (string) preg_replace('/@.*/', '', (string) $user['email']);
        }
        $base = self::slugify(implode('-', array_filter(array_map('trim', $parts))));
        if ($base === '') {
            $base = 'user-' . $fallbackIndex;
        }

        $uuid = trim((string) ($user['uuid'] ?? ''));
        $suffix = '';
        if ($uuid !== '') {
            $suffix = substr(preg_replace('/[^a-z0-9]/i', '', $uuid), 0, 8);
        }
        $name = $base;
        if ($suffix !== '') {
            $name .= '-' . strtolower($suffix);
        }
        $name .= '.pdf';

        $candidate = $name;
        $counter = 2;
        // Ensure stable unique filenames inside the ZIP archive.
        while (isset($usedNames[$candidate])) {
            $candidate = substr($name, 0, -4) . '-' . $counter . '.pdf';
            $counter++;
        }
        $usedNames[$candidate] = true;
        return $candidate;
    }

    private static function slugify(string $value): string
    {
        $value = strtolower(trim($value));
        if ($value === '') {
            return '';
        }
        $value = preg_replace('/[^a-z0-9]+/', '-', $value);
        $value = trim((string) $value, '-');
        return $value;
    }

    private static function buildPdfTitle(string $locale): string
    {
        if (str_starts_with(strtolower($locale), 'de')) {
            return 'Zugangsdaten PDF';
        }
        return 'Access Details PDF';
    }
}
