<?php
defined('ABSPATH') || exit;

use Mpdf\Mpdf;

class PDFInvX_Generator {
    private $mpdf_config;
    private $mpdf;

    public function __construct() {
        try {
            require_once PDFINVX51_PLUGIN_DIR . 'vendor/autoload.php';
            
            // Store mPDF configuration for reuse
            $this->mpdf_config = [
                'tempDir' => sys_get_temp_dir(),
                'format' => 'A4',
                'margin_left' => 5,
                'margin_right' => 5,
                'margin_top' => 5,
                'margin_bottom' => 5,
                'mode' => 'utf-8',
                'autoPageBreak' => false,
                'fontDir' => [PDFINVX51_PLUGIN_DIR . 'vendor/mpdf/mpdf/ttfonts'],
                'fontdata' => [
                    'dejavusans' => [
                        'R' => 'DejaVuSans.ttf',
                        'B' => 'DejaVuSans-Bold.ttf',
                    ],
                ],
                'default_font' => 'dejavusans',
            ];
            
            $this->initialize_mpdf();
        } catch (Exception $e) {
            $this->log_error('PDFInvoiceX mPDF Initialization Error: ' . $e->getMessage());
            throw new Exception(esc_html__('Failed to initialize PDF generator.', 'pdf-invoicex'));
        }
    }

    private function initialize_mpdf() {
        // Create new mPDF instance with stored configuration
        $this->mpdf = new Mpdf($this->mpdf_config);
    }

    public function generate($order_id, $for_download = false) {
        $order = wc_get_order($order_id);
        if (!$order || !$order->has_status(wc_get_is_paid_statuses())) {
            throw new Exception(esc_html__('Invalid or unpaid order.', 'pdf-invoicex'));
        }

        $settings = get_option('pdfinvx_settings', []);
        $template = !empty($settings['template']) ? $settings['template'] : 'default';
        $invoice_number = $this->get_invoice_number($order_id);

        // Trigger action to enqueue styles for the specific template
        do_action('pdfinvx_before_pdf_render', $template);

        $template_path = apply_filters('pdfinvx_template_path', PDFINVX51_TEMPLATE_DIR . $template . '.php', $template, $order_id);
        if (!file_exists($template_path)) {
            $this->log_error('PDFInvoiceX Template Not Found: ' . $template_path);
            $template_path = PDFINVX51_TEMPLATE_DIR . 'default.php';
            $template = 'default';
        }

        // Use WP_Filesystem to check readability
        global $wp_filesystem;
        if (empty($wp_filesystem)) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
            WP_Filesystem();
        }
        if (!$wp_filesystem->is_readable($template_path)) {
            $this->log_error('PDFInvoiceX Template Unreadable: ' . $template_path);
            throw new Exception(esc_html__('Invoice template is not accessible.', 'pdf-invoicex'));
        }

        ob_start();
        try {
            include $template_path;
        } catch (Exception $e) {
            ob_end_clean();
            $this->log_error('PDFInvoiceX Template Rendering Error for Order ' . $order_id . ': ' . $e->getMessage());
            throw new Exception(esc_html__('Failed to render invoice template.', 'pdf-invoicex'));
        }
        $html = ob_get_clean();

        try {
            // Reset mPDF state by reinitializing
            $this->initialize_mpdf();
            $this->mpdf->WriteHTML($html);
            $page_count = $this->mpdf->page;
            $this->log_error('PDFInvoiceX PDF Generated for Order ' . $order_id . ': Page Count = ' . $page_count);
            if ($page_count > 1) {
                $this->log_error('PDFInvoiceX Warning: Multiple pages detected for Order ' . $order_id . ' | HTML: ' . substr($html, 0, 1000));
            }
        } catch (Exception $e) {
            $this->log_error('PDFInvoiceX PDF Write Error for Order ' . $order_id . ': ' . $e->getMessage() . ' | HTML: ' . substr($html, 0, 1000));
            throw new Exception(esc_html__('Failed to generate PDF content.', 'pdf-invoicex'));
        }

        $filename = sprintf('invoice-%s.pdf', sanitize_file_name($invoice_number));

        if ($for_download) {
            $upload_dir = wp_upload_dir();
            $pdf_path = $upload_dir['basedir'] . '/pdfinvx-invoices/' . $filename;
            $pdf_url = $upload_dir['baseurl'] . '/pdfinvx-invoices/' . $filename;

            if (!$wp_filesystem->is_dir($upload_dir['basedir'] . '/pdfinvx-invoices')) {
                wp_mkdir_p($upload_dir['basedir'] . '/pdfinvx-invoices');
            }

            if (!$wp_filesystem->is_writable($upload_dir['basedir'] . '/pdfinvx-invoices')) {
                $this->log_error('PDFInvoiceX Directory Not Writable: ' . $upload_dir['basedir'] . '/pdfinvx-invoices');
                throw new Exception(esc_html__('PDF storage directory is not writable.', 'pdf-invoicex'));
            }

            $this->mpdf->Output($pdf_path, 'F');
            return $pdf_url;
        }

        return $this->mpdf->Output('', 'S');
    }

    public function attach_to_email($attachments, $email_id, $order, $email) {
        $settings = get_option('pdfinvx_settings', []);
        if (empty($settings['attach_to_emails']) || !in_array($email_id, $settings['attach_to_emails'], true)) {
            return $attachments;
        }

        if (!$order || !$order->has_status(wc_get_is_paid_statuses())) {
            return $attachments;
        }

        try {
            $order_id = $order->get_id();
            $pdf_content = $this->generate($order_id, false);
            $invoice_number = $this->get_invoice_number($order_id);
            $filename = sprintf('invoice-%s.pdf', sanitize_file_name($invoice_number));
            $path = sys_get_temp_dir() . '/' . $filename;

            global $wp_filesystem;
            if (empty($wp_filesystem)) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
                WP_Filesystem();
            }
            if (!$wp_filesystem->is_writable(sys_get_temp_dir()) || !$wp_filesystem->put_contents($path, $pdf_content)) {
                throw new Exception(esc_html__('Failed to save PDF for email attachment.', 'pdf-invoicex'));
            }

            $attachments[] = $path;
        } catch (Exception $e) {
            $this->log_error('PDFInvoiceX Email Attachment Error for Order ' . $order->get_id() . ': ' . $e->getMessage());
        }

        return $attachments;
    }

    public function get_invoice_number($order_id) {
        $settings = get_option('pdfinvx_settings', []);
        $prefix = !empty($settings['invoice_prefix']) ? $settings['invoice_prefix'] : 'INV-';
        $suffix = !empty($settings['invoice_suffix']) ? $settings['invoice_suffix'] : '';
        $number = get_post_meta($order_id, '_pdfinvx_invoice_number', true);

        if (empty($number)) {
            $number = $this->generate_invoice_number();
            update_post_meta($order_id, '_pdfinvx_invoice_number', $number);
        }

        return apply_filters('pdfinvx_invoice_number', $prefix . $number . $suffix, $order_id);
    }

    private function generate_invoice_number() {
        global $wpdb;
        $lock_name = 'pdfinvx_invoice_number_lock';
        $cache_key = 'pdfinvx_last_invoice_number';
        $last_number = wp_cache_get($cache_key);

        if (false === $last_number) {
            $last_number = get_option('pdfinvx_last_invoice_number', 0);
            wp_cache_set($cache_key, $last_number, '', 300); // Cache for 5 minutes
        }

        // Direct query required for atomic increment with locking
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $wpdb->query('START TRANSACTION');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $wpdb->query($wpdb->prepare("SELECT GET_LOCK(%s, 10)", $lock_name));

        $new_number = $last_number + 1;
        update_option('pdfinvx_last_invoice_number', $new_number);
        wp_cache_set($cache_key, $new_number, '', 300);

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $wpdb->query($wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name));
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $wpdb->query('COMMIT');

        return str_pad($new_number, 6, '0', STR_PAD_LEFT);
    }

    private function log_error($message) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            error_log($message);
        }
    }
}
?>