<?php

namespace Storeplugin\PDFInvoice\App\Preview;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\App\Integrations\PdfDocumentGenerator;
use Storeplugin\PDFInvoice\App\Utils;
use Storeplugin\PDFInvoice\Config;

class DocumentPreviewPage
{
    public const PAGE_SLUG = 'nxtpdf-document-preview';

    private const TRANSIENT_PREFIX = 'nxtpdf_preview_';
    private const TRANSIENT_TTL = 15 * MINUTE_IN_SECONDS;

    /**
     * WordPress hook suffix for the preview screen.
     *
     * @var string
     */
    private string $hookSuffix = '';

    /**
     * Cached preview state for the current request.
     *
     * @var array<string, mixed>|null
     */
    private ?array $previewState = null;

    public function register(): void
    {
        add_action('admin_menu', [$this, 'registerPage']);
        add_filter('admin_body_class', [$this, 'addAdminBodyClass']);
    }

    /**
     * Register the hidden preview page used by the live iframe.
     */
    public function registerPage(): void
    {
        $hookSuffix = add_submenu_page(
            '',
            __('PDF Template Preview', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
            __('PDF Template Preview', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
            'manage_options',
            self::PAGE_SLUG,
            [$this, 'renderPage']
        );

        if (!is_string($hookSuffix) || $hookSuffix === '') {
            return;
        }

        $this->hookSuffix = $hookSuffix;
        add_action('load-' . $this->hookSuffix, [$this, 'preparePage']);
    }

    /**
     * Create a preview URL backed by a transient payload.
     */
    public static function createPreviewUrl(array $settings, string $optionGroup, int $userId = 0): string
    {
        $token = wp_generate_uuid4();
        $payload = [
            'user_id' => $userId > 0 ? $userId : get_current_user_id(),
            'option_group' => self::normalizeEditorGroup($optionGroup),
            'settings' => $settings,
            'created_at' => time(),
        ];

        set_transient(self::getTransientKey($token), $payload, self::TRANSIENT_TTL);

        return add_query_arg([
            'page' => self::PAGE_SLUG,
            'preview_token' => $token,
        ], admin_url('admin.php'));
    }

    /**
     * Set screen title and enqueue preview assets.
     */
    public function preparePage(): void
    {
        global $title;

        if (empty($title)) {
            $title = __('PDF Template Preview', 'storeinvoice-print-pdf-invoices-and-packing-slips');
        }

        $this->enqueueAssets();
    }

    /**
     * Enqueue the preview page styles for the selected template.
     */
    public function enqueueAssets(): void
    {
        $previewHandle = Config::asset('pdf-preview-page');
        $previewStylesheet = trailingslashit(NXTPDF_DIR) . 'assets/css/pdf-preview.css';

        $this->enqueueStylesheetFile($previewHandle, $previewStylesheet);

        $previewState = $this->getPreviewState();
        if ($previewState === null) {
            return;
        }

        $generator = new PdfDocumentGenerator(
            (array) ($previewState['settings'] ?? []),
            (string) ($previewState['option_group'] ?? Utils::GROUP_INVOICE_EDITOR)
        );

        $pageCss = $generator->get_preview_page_stylesheet();
        if ($pageCss !== '') {
            wp_add_inline_style($previewHandle, $pageCss);
        }

        $dependencies = [$previewHandle];
        foreach (array_values($generator->get_document_stylesheet_files()) as $index => $stylesheetFile) {
            $handle = Config::asset('pdf-preview-style-' . (string) ($index + 1));
            if ($this->enqueueStylesheetFile($handle, $stylesheetFile, $dependencies)) {
                $dependencies = [$handle];
            }
        }

        $dynamicCss = $generator->get_dynamic_stylesheet();
        if ($dynamicCss === '') {
            return;
        }

        $dynamicHandle = Config::asset('pdf-preview-dynamic');
        wp_register_style($dynamicHandle, false, $dependencies, '1.0.0');
        wp_enqueue_style($dynamicHandle);
        wp_add_inline_style($dynamicHandle, $dynamicCss);
    }

    /**
     * Add a scoped admin body class for the iframe preview screen.
     */
    public function addAdminBodyClass(string $classes): string
    {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        $screenId = is_object($screen) && isset($screen->id) ? (string) $screen->id : '';

        if ($this->hookSuffix !== '' && $screenId === $this->hookSuffix) {
            return trim($classes . ' nxtpdf-preview-admin');
        }

        return $classes;
    }

    /**
     * Render the preview iframe document.
     */
    public function renderPage(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized', 'storeinvoice-print-pdf-invoices-and-packing-slips'));
        }

        $previewState = $this->getPreviewState();
        if ($previewState === null) {
            $this->renderError(
                __('Preview session expired. Update the preview again.', 'storeinvoice-print-pdf-invoices-and-packing-slips')
            );
            return;
        }

        try {
            $generator = new PdfDocumentGenerator(
                (array) ($previewState['settings'] ?? []),
                (string) ($previewState['option_group'] ?? Utils::GROUP_INVOICE_EDITOR)
            );
            $html = $generator->get_preview_markup();
        } catch (\Throwable $e) {
            $this->renderError($e->getMessage());
            return;
        }
        ?>
        <div class="wrap nxtpdf-preview-screen">
            <div class="nxtpdf-preview-document" data-nxtpdf-preview-document>
                <?php echo wp_kses($html, $this->getAllowedPreviewHtml()); ?>
            </div>
        </div>
        <?php
    }

    /**
     * Allowed HTML for template preview markup rendered in the admin iframe.
     *
     * @return array<string, array<string, bool>>
     */
    private function getAllowedPreviewHtml(): array
    {
        $allowedHtml = wp_kses_allowed_html('post');
        $templateHtml = [
            'table' => [
                'class' => true,
            ],
            'thead' => [],
            'tbody' => [],
            'tr' => [
                'class' => true,
            ],
            'td' => [
                'class' => true,
                'width' => true,
                'valign' => true,
                'colspan' => true,
                'rowspan' => true,
            ],
            'th' => [
                'class' => true,
                'width' => true,
                'valign' => true,
                'colspan' => true,
                'rowspan' => true,
            ],
            'div' => [
                'class' => true,
            ],
            'span' => [
                'class' => true,
            ],
            'h1' => [
                'class' => true,
            ],
            'strong' => [],
            'small' => [],
            'br' => [],
            'img' => [
                'class' => true,
                'src' => true,
                'alt' => true,
                'width' => true,
                'height' => true,
            ],
        ];

        foreach ($templateHtml as $tag => $attributes) {
            $allowedHtml[$tag] = isset($allowedHtml[$tag])
                ? array_merge($allowedHtml[$tag], $attributes)
                : $attributes;
        }

        /**
         * Filter the allowed HTML tags for document preview markup.
         *
         * @param array<string, array<string, bool>> $allowedHtml Allowed tags and attributes.
         */
        return apply_filters('nxtpdf_preview_allowed_html', $allowedHtml);
    }

    /**
     * Render a preview-safe error page that the editor iframe can inspect.
     */
    private function renderError(string $message): void
    {
        $message = $message !== '' ? $message : __('Unable to render preview.', 'storeinvoice-print-pdf-invoices-and-packing-slips');
        ?>
        <div class="wrap nxtpdf-preview-screen">
            <div
                class="nxtpdf-preview-error"
                data-nxtpdf-preview-error="<?php echo esc_attr($message); ?>"
            >
                <?php echo esc_html($message); ?>
            </div>
        </div>
        <?php
    }

    /**
     * Resolve and cache the preview payload for the current request.
     *
     * @return array<string, mixed>|null
     */
    private function getPreviewState(): ?array
    {
        if ($this->previewState !== null) {
            return $this->previewState;
        }

        $token = $this->getPreviewToken();
        if ($token === '') {
            return null;
        }

        $state = get_transient(self::getTransientKey($token));
        if (!is_array($state)) {
            return null;
        }

        $userId = isset($state['user_id']) ? absint($state['user_id']) : 0;
        if ($userId !== get_current_user_id()) {
            return null;
        }

        $this->previewState = [
            'option_group' => self::normalizeEditorGroup((string) ($state['option_group'] ?? '')),
            'settings' => isset($state['settings']) && is_array($state['settings']) ? $state['settings'] : [],
        ];

        return $this->previewState;
    }

    /**
     * Read the preview token from the request.
     */
    private function getPreviewToken(): string
    {
        $value = filter_input(INPUT_GET, 'preview_token', FILTER_UNSAFE_RAW, FILTER_REQUIRE_SCALAR);
        if ($value === null || $value === false) {
            return '';
        }

        $value = sanitize_text_field((string) $value);
        return preg_replace('/[^a-zA-Z0-9-]/', '', $value) ?: '';
    }

    /**
     * Normalize a supported editor option group.
     */
    private static function normalizeEditorGroup(string $group): string
    {
        $group = sanitize_key($group);

        if ($group === Utils::GROUP_PACKING_SLIP_EDITOR) {
            return Utils::GROUP_PACKING_SLIP_EDITOR;
        }

        return Utils::GROUP_INVOICE_EDITOR;
    }

    /**
     * Build the transient key for a preview token.
     */
    private static function getTransientKey(string $token): string
    {
        return self::TRANSIENT_PREFIX . md5($token);
    }

    /**
     * Enqueue a plugin stylesheet file by absolute path.
     */
    private function enqueueStylesheetFile(string $handle, string $absolutePath, array $dependencies = []): bool
    {
        $url = $this->getPublicFileUrl($absolutePath);
        if ($url === '') {
            return false;
        }

        wp_enqueue_style(
            $handle,
            $url,
            $dependencies,
            (string) filemtime($absolutePath)
        );

        return true;
    }

    /**
     * Resolve a plugin file URL from an absolute path.
     */
    private function getPublicFileUrl(string $absolutePath): string
    {
        if ($absolutePath === '' || !file_exists($absolutePath)) {
            return '';
        }

        $normalizedRoot = trailingslashit(wp_normalize_path(NXTPDF_DIR));
        $normalizedPath = wp_normalize_path($absolutePath);

        if (strpos($normalizedPath, $normalizedRoot) !== 0) {
            return '';
        }

        $relativePath = ltrim(substr($normalizedPath, strlen($normalizedRoot)), '/');

        return trailingslashit(NXTPDF_URL) . str_replace('\\', '/', $relativePath);
    }
}
