<?php
/**
 * Plugin Name:     Pay-To-View Lite
 * Plugin URI:      hhttps://theappmill.com/paytoview/index.html
 * Description:     Pay-To-View Lite lets you sell time-limited access to your videos or other digital content through PayPal and Credit Cards.
 * Version:         1.2.9
 * Author:          Lise Rasmussen
 * Author URI:      https://theappmill.com/
 * License:         GPLv2 or later
 * License URI:     https://www.gnu.org/licenses/gpl-2.0.html
 * Requires at least: 5.0
 * Requires PHP:    7.0
 * Tested up to:    7.0
 * Stable tag:      1.2.9
 * Text Domain:     pay-to-view-lite
 * Domain Path:     /languages
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; 
}

// Plugin constants
if ( ! defined( 'PAYTOVIEWLITE_PLUGIN_URL' ) ) {
    define( 'PAYTOVIEWLITE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
}

// Tooltips
function paytoviewlite_tooltip_icon($label, $text) {
    if (empty($text)) {
        return '';
    }
    return '
    <span class="tooltip-container">
        <span class="info-icon" tabindex="0" aria-label="' . esc_attr($label) . '">i</span>
        <span class="tooltip-text">' . esc_html($text) . '</span>
    </span>';
}

class PayToViewLite {
 
    private $option_name = 'paytoview_lite_options';
    private $shortcode_items = [];
    private function get_label_with_required($label, $field_id, $required_fields) {
        if (in_array($field_id, $required_fields, true)) {
            return $label . ' <span style="color:red; font-weight:bold;">*</span>';
        }
        return $label;
    }

    private $field_tooltips = [
        'paypal_client_id' => 'To use PayPal as payment you must first create a client ID on PayPal and then add that Client ID code here.',
        'paypal_email'     => 'Enter the PayPal business account email address where payments should be received.',
        'custom_currency'  => 'Add a 3-letter currency code (USD, EUR, GBP etc).',
        'fixed_price'      => 'Set a fixed price that will be used for all rentals.',
        'fixed_duration'   => 'Set a fixed access duration, in hours. This will be applied to all rentals, after payment. For example, 48 equals 2 days of access.',
        'email_from'       => 'The sender email address shown in the confirmation emails.',
        'email_subject'    => 'The subject line for confirmation emails.',
        'email_message'    => 'The message body for confirmation emails. Use {expiration_date} and {site_name} placeholders.',
    ];

    private $field_descriptions = [
        'paypal_client_id' => 'Used for integration with PayPal. Required for payments to work. Start here: https://developer.paypal.com/api/rest/',
        'paypal_email'     => 'Add a PayPal business email to receive payments',
        'custom_currency'  => 'Add a currency code.',
        'fixed_price'      => 'Add a fixed price',
        'fixed_duration'   => 'Add a fixed rental duration',
        'email_from'       => 'Add a sender email address for confirmation emails.',
        'email_subject'    => 'Add the subject line for confirmation emails.',
        'email_message'    => 'Add the confirmation email message.',
    ];

// Enqueue frontend styles
public function paytoviewlite_enqueue_frontend_styles() {
    $plugin_version = defined('PAYTOVIEWLITE_VERSION') ? PAYTOVIEWLITE_VERSION : '1.0.0';
    $css_path = plugin_dir_path(__FILE__) . 'assets/css/frontend-styles.css';
    $css_url  = plugin_dir_url(__FILE__) . 'assets/css/frontend-styles.css';

    wp_enqueue_style(
        'paytoviewlite-frontend-style',
        $css_url,
        [],
        file_exists($css_path) ? filemtime($css_path) : $plugin_version,
        'all'
    );
}

// Enqueue frontend scripts (with conditional PayPal SDK)
public function paytoviewlite_enqueue_frontend_scripts() {
    $plugin_version = defined('PAYTOVIEWLITE_VERSION') ? PAYTOVIEWLITE_VERSION : '1.0.0';
    $options       = get_option($this->option_name);
    $paypal_client = $options['paypal_client_id'] ?? '';
    $currency      = $options['custom_currency'] ?? 'USD';


        // Proper PayPal SDK enqueue (no version!)
    if ($paypal_client && !wp_script_is('paypal-sdk', 'enqueued')) {
        $paypal_sdk_url = 'https://www.paypal.com/sdk/js?client-id=' . rawurlencode($paypal_client) . '&currency=' . rawurlencode($currency);
    
        wp_register_script(
            'paypal-sdk',
            $paypal_sdk_url,
            [],
            null, // null prevents ?ver=
            true
        );
        wp_enqueue_script('paypal-sdk');
    }

    // Setup dependencies for frontend script
    $deps = [];
    if ($paypal_client) {
        $deps[] = 'paypal-sdk';
    }

    $frontend_js_path = plugin_dir_path(__FILE__) . 'assets/js/frontend.js';
    $frontend_js_url  = plugin_dir_url(__FILE__) . 'assets/js/frontend.js';

    wp_enqueue_script(
        'paytoviewlite-frontend',
        $frontend_js_url,
        $deps,
        file_exists($frontend_js_path) ? filemtime($frontend_js_path) : $plugin_version,
        true
    );
}

// Enqueue admin styles
public function paytoviewlite_enqueue_admin_styles() {
    $plugin_version = defined('PAYTOVIEWLITE_VERSION') ? PAYTOVIEWLITE_VERSION : '1.0.0';
    $css_path = plugin_dir_path(__FILE__) . 'assets/css/admin-styles.css';
    $css_url  = plugin_dir_url(__FILE__) . 'assets/css/admin-styles.css';

    wp_enqueue_style(
        'paytoviewlite-admin-style',
        $css_url,
        [],
        file_exists($css_path) ? filemtime($css_path) : $plugin_version
    );
}

// Enqueue admin scripts
public function paytoviewlite_enqueue_admin_scripts() {
    $plugin_version = defined('PAYTOVIEWLITE_VERSION') ? PAYTOVIEWLITE_VERSION : '1.0.0';

    $admin_js_path = plugin_dir_path(__FILE__) . 'assets/js/admin.js';
    $admin_js_url  = plugin_dir_url(__FILE__) . 'assets/js/admin.js';

    wp_enqueue_script(
        'paytoviewlite-admin',
        $admin_js_url,
        [],
        file_exists($admin_js_path) ? filemtime($admin_js_path) : $plugin_version,
        true
    );

    wp_localize_script(
        'paytoviewlite-admin',
        'PayToViewLiteAdmin',
        [
            'ajax_url' => admin_url('admin-ajax.php'),
            'save_shortcode_nonce' => wp_create_nonce('paytoviewlite_save_shortcode_ajax'),
            'delete_shortcode_nonce' => wp_create_nonce('paytoviewlite_delete_shortcode_ajax'),
            'load_shortcode_list_nonce' => wp_create_nonce('paytoviewlite_load_shortcode_list_ajax'),
        ]
    );
}

public function output_paytoviewlite_js_data() {
    if (!empty($this->shortcode_items)) {
        echo '<script type="text/javascript">';
        echo 'window.paytoviewLiteItems = ' . wp_json_encode($this->shortcode_items) . ';';
        echo '</script>';
    }
}

// Constructor — hook everything up
public function __construct() {
    // Admin
    add_action('admin_menu', [$this, 'paytoviewlite_add_admin_menu']);
    add_action('admin_init', [$this, 'paytoviewlite_register_settings']);
    add_action('admin_enqueue_scripts', [$this, 'paytoviewlite_enqueue_admin_styles']);
    add_action('admin_enqueue_scripts', [$this, 'paytoviewlite_enqueue_admin_scripts']);
    add_action('admin_footer', [$this, 'paytoviewlite_open_pro_menu_link_in_new_tab']);
    add_action('admin_post_paytoviewlite_export_rental_log', [$this, 'paytoviewlite_export_rental_log']);

    // Frontend
    add_action('wp_enqueue_scripts', [$this, 'paytoviewlite_enqueue_frontend_styles']);
    add_action('wp_enqueue_scripts', [$this, 'paytoviewlite_enqueue_frontend_scripts']);
    add_action('wp_footer', [$this, 'output_paytoviewlite_js_data']);

    // Shortcode
    add_shortcode('paytoviewlite', [$this, 'paytoviewlite_shortcode_handler']);

    // AJAX
    add_action('wp_ajax_paytoviewlite_log_rental', [$this, 'paytoviewlite_ajax_log_rental']);
    add_action('wp_ajax_nopriv_paytoviewlite_log_rental', [$this, 'paytoviewlite_ajax_log_rental']);
    add_action('wp_ajax_paytoviewlite_save_shortcode', [$this, 'paytoviewlite_ajax_save_shortcode']);
    add_action('wp_ajax_paytoviewlite_delete_shortcode', [$this, 'paytoviewlite_ajax_delete_shortcode']);
    add_action('wp_ajax_paytoviewlite_load_shortcode_list', [$this, 'paytoviewlite_ajax_load_shortcode_list']);
}

public function paytoviewlite_add_admin_menu() {
    global $submenu;

    add_menu_page(
        'Pay-To-View Lite',
        'Pay-To-View',
        'manage_options',
        'paytoview_lite_shortcode', 
        [ $this, 'paytoviewlite_shortcode_generator_page' ],
        'dashicons-video-alt3',
        26
    );

    add_submenu_page(
        'paytoview_lite_shortcode',
        'Shortcode Generator',
        'Shortcodes',
        'manage_options',
        'paytoview_lite_shortcode',
        [ $this, 'paytoviewlite_shortcode_generator_page' ]
    );

    add_submenu_page(
        'paytoview_lite_shortcode',
        'Rentals',
        'Rentals',
        'manage_options',
        'paytoview_lite_log',
        [ $this, 'paytoviewlite_rental_log_page' ]
    );

    add_submenu_page(
        'paytoview_lite_shortcode',
        'Settings',
        'Settings',
        'manage_options',
        'paytoview_lite_main',
        [ $this, 'paytoviewlite_settings_page' ]
    );


    $submenu['paytoview_lite_shortcode'][] = [
        __('Upgrade to PRO ⭐', 'pay-to-view-lite'),
        'manage_options',
        $this->paytoviewlite_get_pro_pricing_url(),
    ];
}


private function paytoviewlite_get_pro_pricing_url() {
    return 'https://theappmill.com/paytoview/pricing_plans.html';
}

public function paytoviewlite_open_pro_menu_link_in_new_tab() {
    ?>
    <script>
        document.querySelectorAll('#adminmenu a[href="<?php echo esc_js($this->paytoviewlite_get_pro_pricing_url()); ?>"]').forEach(function(link) {
            link.setAttribute('target', '_blank');
            link.setAttribute('rel', 'noopener noreferrer');
        });
    </script>
    <?php
}


public function paytoviewlite_register_settings() {
    register_setting(
        'paytoview_lite_group',
        $this->option_name,
        array($this, 'paytoviewlite_sanitize_options')
    );

    add_settings_section(
        'paytoview_lite_section',
        null,
        null,
        'paytoview_lite_main'
    );

    $fields = [
        'paypal_client_id' => 'PayPal Client ID',
        'paypal_email'     => 'PayPal Email',
        'fixed_price'      => 'Rental Price',
        'custom_currency'  => 'Currency',
        'fixed_duration'   => 'Rental Duration (hours)',
        'email_from'       => 'Email "From" Address',
        'email_subject'    => 'Email Subject',
        'email_message'    => 'Email Message',
    ];

    $required_fields = ['paypal_client_id', 'paypal_email', 'fixed_price', 'custom_currency', 'fixed_duration'];

    foreach ($fields as $id => $label) {
        $label = $this->get_label_with_required($label, $id, $required_fields);

        if ($id === 'media_player') {
            add_settings_field(
                $id,
                $label . ' ' . paytoviewlite_tooltip_icon($id, $this->field_tooltips[$id]),
                [$this, 'paytoviewlite_render_media_player_dropdown'],
                'paytoview_lite_main',
                'paytoview_lite_section',
                ['label_for' => $id]
            );
        } else {
            add_settings_field(
                $id,
                $label . ' ' . (isset($this->field_tooltips[$id]) ? paytoviewlite_tooltip_icon($id, $this->field_tooltips[$id]) : ''),
                [$this, 'paytoviewlite_render_text_field'],
                'paytoview_lite_main',
                'paytoview_lite_section',
                ['label_for' => $id]
            );
        }
    }
}

public function paytoviewlite_sanitize_options($options) {
    $sanitized = [];

    if (isset($options['paypal_client_id'])) {
        $sanitized['paypal_client_id'] = sanitize_text_field($options['paypal_client_id']);
    }
    if (isset($options['paypal_email'])) {
        $sanitized['paypal_email'] = sanitize_email($options['paypal_email']);
    }
    if (isset($options['fixed_price'])) {
        $sanitized['fixed_price'] = sanitize_text_field($options['fixed_price']);
    }
    if (isset($options['custom_currency'])) {
        $sanitized['custom_currency'] = sanitize_text_field($options['custom_currency']);
    }
    if (isset($options['fixed_duration'])) {
        $sanitized['fixed_duration'] = absint($options['fixed_duration']);
    }
    if (array_key_exists('email_from', $options)) {
        $sanitized['email_from'] = sanitize_email($options['email_from']);
    } else {
        $sanitized['email_from'] = '';
    }
    if (array_key_exists('email_subject', $options)) {
        $sanitized['email_subject'] = sanitize_text_field($options['email_subject']);
    } else {
        $sanitized['email_subject'] = '';
    }
    if (array_key_exists('email_message', $options)) {
        $sanitized['email_message'] = wp_kses_post($options['email_message']);
    } else {
        $sanitized['email_message'] = '';
    }
    return $sanitized;
}

public function paytoviewlite_render_text_field($args) {
    $options = get_option($this->option_name);
    if (!is_array($options)) {
        $options = [];
    }
    $id      = $args['label_for'];

    $required_fields = [
        'paypal_client_id',
        'paypal_email',
        'custom_currency',
        'fixed_price',
        'fixed_duration'
    ];

    // Default values
    $defaults = [
        'custom_currency' => 'USD',
        'email_from'      => get_option('admin_email'),
        'email_subject'   => 'Rental on {site_name}',
        'email_message'   => "Thank you for renting with us!<br>Your access expires on: {expiration_date}<br><br>/The team on {site_name}",
    ];

    // Value with default fallback
    $value = array_key_exists($id, $options)
        ? $options[$id]
        : ($defaults[$id] ?? '');

    $description = $this->field_descriptions[$id] ?? '';

    // Wrap input and error message container
    echo '<div class="paytoview-input-wrapper" id="wrapper_' . esc_attr($id) . '">';

    // Render input fields
    if ($id === 'custom_currency') {
        echo '<input type="text" id="' . esc_attr($id) . '" 
            name="' . esc_attr($this->option_name . "[$id]") . '" 
            value="' . esc_attr($value) . '" 
            maxlength="3" size="3" 
            class="paytoview-currency-input" />';
    } elseif ($id === 'email_message') {
        echo '<textarea id="' . esc_attr($id) . '" 
            name="' . esc_attr($this->option_name . "[$id]") . '" 
            rows="6" class="large-text">' 
            . esc_textarea($value) . '</textarea>';
    } else {
        echo '<input type="text" id="' . esc_attr($id) . '" 
            name="' . esc_attr($this->option_name . "[$id]") . '" 
            value="' . esc_attr($value) . '" 
            class="regular-text" />';
    }

    // Inline error message placeholder (hidden by default)
    echo '<div class="paytoview-error-message" style="display:none;"></div>';

    // Description
    if ($description) {
    echo '<p class="paytoview-description"><em>' . esc_html($description) . '</em></p>';
    }

    // Placeholder info for email fields
    if (in_array($id, ['email_subject', 'email_message'], true)) {
        echo '<em>
            Available placeholders: 
            <code>{movie_title}</code>, 
            <code>{site_name}</code>, 
            <code>{expiration_date}</code>, 
            <code>{media_file}</code>, 
            <code>{movie_url}</code>, 
            <code>{rental_id}</code>
        </em>';
    }

    if ($id === 'email_message') {
        echo '<p><button type="button" id="paytoview-reset-email-template" class="button paytoview-reset-button" data-default-subject="' . esc_attr($defaults['email_subject']) . '" data-default-message="' . esc_attr($defaults['email_message']) . '">Reset email subject and message</button></p>';
    }

    // Info message about mail configuration
    if (in_array($id, ['email_from', 'admin_email'], true)) {
        echo '<em>
            Note: To send confirmation emails, you must have a working mail setup on your WordPress site. If mail isn’t configured, payments will still work but emails won’t be sent.
        </em>';
    }
    echo '</div>'; // Close wrapper div
}

private function paytoviewlite_render_settings_field_row($field_id) {
    global $wp_settings_fields;

    if (empty($wp_settings_fields['paytoview_lite_main']['paytoview_lite_section'][$field_id])) {
        return;
    }

    $field = $wp_settings_fields['paytoview_lite_main']['paytoview_lite_section'][$field_id];

    echo '<tr>';
    echo '<th scope="row">' . wp_kses_post($field['title']) . '</th>';
    echo '<td>';
    call_user_func($field['callback'], $field['args']);
    echo '</td>';
    echo '</tr>';
}

public function paytoviewlite_settings_page() {
?>
<div class="wrap">
    <?php
    $settings_intro = sprintf(
    /* translators: %s is a link to the upgrade page labeled "PRO" */
    esc_html__('Configure PayPal settings, rental price, rental duration and email settings.', 'pay-to-view-lite') .
    '<br>' .
    esc_html__('To unlock more features like setting individual price and duration, customize the buttons and more, consider upgrading to %s.', 'pay-to-view-lite'),
    '<a href="' . esc_url($this->paytoviewlite_get_pro_pricing_url()) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('PRO', 'pay-to-view-lite') . '</a>'
    );
    $this->paytoviewlite_render_admin_banner(__('Settings', 'pay-to-view-lite'), '⚙', $settings_intro);
    ?>
    <div class="postbox">
        <?php
        /**
         * The 'settings-updated' query var is set by WordPress core after saving options.
         * It is not user input and does NOT require nonce verification.
         * We unslash and sanitize it here to satisfy security standards.
         */
    
        // phpcs:disable WordPress.Security.NonceVerification.Recommended
        // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
        // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    
        $raw_settings_updated = isset($_GET['settings-updated']) ? wp_unslash($_GET['settings-updated']) : '';
        $settings_updated = sanitize_text_field($raw_settings_updated);
    
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
        // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
        // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    
        if ($settings_updated) {
            add_settings_error(
                'paytoview_messages',
                'paytoview_message',
                __('Settings saved successfully.', 'pay-to-view-lite'),
                'updated'
            );
        }
        settings_errors('paytoview_messages');
        ?>

        <form action="options.php" method="post">
            <?php
            settings_fields('paytoview_lite_group');
            ?>
            <table class="form-table">
                <tr class="paytoview-table-title-row">
                    <th colspan="2" scope="colgroup"><div class="paytoview-table-title-inner"><?php esc_html_e('PayPal settings', 'pay-to-view-lite'); ?></div></th>
                </tr>
                <?php
                $this->paytoviewlite_render_settings_field_row('paypal_client_id');
                $this->paytoviewlite_render_settings_field_row('paypal_email');
                ?>
                <tr class="paytoview-table-title-row">
                    <th colspan="2" scope="colgroup"><div class="paytoview-table-title-inner"><?php esc_html_e('Price and Duration', 'pay-to-view-lite'); ?></div></th>
                </tr>
                <?php
                $this->paytoviewlite_render_settings_field_row('fixed_price');
                $this->paytoviewlite_render_settings_field_row('custom_currency');
                $this->paytoviewlite_render_settings_field_row('fixed_duration');
                ?>
                <tr class="paytoview-table-title-row">
                    <th colspan="2" scope="colgroup"><div class="paytoview-table-title-inner"><?php esc_html_e('Notifications', 'pay-to-view-lite'); ?></div></th>
                </tr>
                <?php
                $this->paytoviewlite_render_settings_field_row('email_from');
                $this->paytoviewlite_render_settings_field_row('email_subject');
                $this->paytoviewlite_render_settings_field_row('email_message');
                ?>
            </table>
            <p class="submit">
                <button type="submit" class="paytoview-btn"><?php esc_html_e('Save Changes', 'pay-to-view-lite'); ?></button>
            </p>
        </form>
    </div>
</div>
<?php
}

public function paytoviewlite_rental_log_page() {
    ?>
    <div class="wrap">
        <?php
        $rental_intro = esc_html__('All completed payments for the current month are listed here. Use the month selector to view earlier logs. Export all logs as a CSV file.', 'pay-to-view-lite') .
        '<br>' .
        esc_html__('You\'re using Pay-To-View Lite. Like this plugin? Your support helps keep it free, consider a', 'pay-to-view-lite') . ' ' .
        '<a href="https://www.paypal.com/donate/?hosted_button_id=4SMV5H3BFFJKQ" target="_blank" rel="noopener noreferrer">' . esc_html__('donation', 'pay-to-view-lite') . '</a> ❤️🙏';
        $this->paytoviewlite_render_admin_banner(__('Rental Log', 'pay-to-view-lite'), '$', $rental_intro);
        ?>
        <div class="postbox">
            <?php
            $log_path = $this->paytoviewlite_get_log_path();
            if ( ! file_exists( $log_path ) ) {
                echo '<p><strong>Rental log file not found yet.</strong><br>';
                echo 'No rentals have been logged yet or the file does not exist:<br>';
                $relative_path = preg_replace('#.*(wp-content/uploads/.*)#', '$1', $log_path);
                echo '<code>' . esc_html( $relative_path ) . '</code></p>';
                return;
            }

            $rows = $this->paytoviewlite_read_csv_log($log_path);
            if ( empty($rows) ) {
                echo '<p>No rental records found.</p>';
                return;
            }

            // Detect "Date" column for sorting (fallback to first col if not found)
            $headers = array_keys($rows[0]);
            $dateKey = null;
            foreach ($headers as $h) {
                if (strcasecmp(trim($h), 'Date') === 0) {
                    $dateKey = $h;
                    break;
                }
            }
            if (!$dateKey) {
                $dateKey = $headers[0];
            }

            // Sort by date DESC
            usort($rows, function($a, $b) use ($dateKey) {
                return strtotime($b[$dateKey]) <=> strtotime($a[$dateKey]);
            });

            $raw_month = isset($_GET['m']) ? sanitize_text_field(wp_unslash($_GET['m'])) : 'current';
            if ($raw_month !== 'current' && $raw_month !== '' && !preg_match('/^\d{4}-\d{2}$/', $raw_month)) {
                $raw_month = '';
            }
            $current_month = gmdate('Y-m');
            $selected_month = ('current' === $raw_month) ? $current_month : $raw_month;
            $available_months = [];

            foreach ($rows as $row) {
                $row_timestamp = !empty($row[$dateKey]) ? strtotime($row[$dateKey]) : false;
                if ($row_timestamp) {
                    $available_months[] = gmdate('Y-m', $row_timestamp);
                }
            }

            $available_months = array_values(array_unique(array_merge([$current_month], $available_months)));
            rsort($available_months);

            if ($selected_month !== '') {
                $rows = array_values(array_filter($rows, function($row) use ($dateKey, $selected_month) {
                    $row_timestamp = !empty($row[$dateKey]) ? strtotime($row[$dateKey]) : false;
                    return $row_timestamp && gmdate('Y-m', $row_timestamp) === $selected_month;
                }));
            }

            // Pagination
            $per_page   = 20;
            $paged      = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
            $total      = count($rows);
            $total_pages = max(1, (int) ceil($total / $per_page));
            $paged      = min($paged, $total_pages);
            $offset     = ($paged - 1) * $per_page;
            $paged_rows = array_slice($rows, $offset, $per_page);
            $base_url   = admin_url('admin.php?page=paytoview_lite_log');
            $export_url = wp_nonce_url(
                add_query_arg(
                    [
                        'action' => 'paytoviewlite_export_rental_log',
                        'm'      => $raw_month,
                    ],
                    admin_url('admin-post.php')
                ),
                'paytoviewlite_export_rental_log'
            );

            $expires_key = null;
            foreach ($headers as $header) {
                if (strcasecmp(trim($header), 'Expires') === 0) {
                    $expires_key = $header;
                    break;
                }
            }
            $display_headers = $this->paytoviewlite_get_rental_log_display_headers($headers);

            ?>
            <form method="get" class="paytoview-rentals-toolbar">
                <input type="hidden" name="page" value="paytoview_lite_log">
                <div class="paytoview-rentals-filter-row">
                    <span class="paytoview-rentals-filter-text">
                        <?php
                        printf(
                            /* translators: %s is the selected rental log month. */
                            esc_html__('Showing rentals for: %s.', 'pay-to-view-lite'),
                            '<strong>' . esc_html($selected_month !== '' ? date_i18n('F Y', strtotime($selected_month . '-01')) : __('All months', 'pay-to-view-lite')) . '</strong>'
                        );
                        ?>
                    </span>
                    <label class="paytoview-rentals-filter-label" for="paytoview-rentals-month-select"><?php esc_html_e('View logs for month:', 'pay-to-view-lite'); ?></label>
                    <select id="paytoview-rentals-month-select" class="paytoview-rentals-month-select" name="m" aria-label="<?php echo esc_attr__('Select month', 'pay-to-view-lite'); ?>" onchange="this.form.submit()">
                        <option value="current" <?php selected('current', $raw_month); ?>><?php esc_html_e('- Select month -', 'pay-to-view-lite'); ?></option>
                        <option value="" <?php selected('', $raw_month); ?>><?php esc_html_e('All months', 'pay-to-view-lite'); ?></option>
                        <?php foreach ($available_months as $available_month) : ?>
                            <option value="<?php echo esc_attr($available_month); ?>" <?php selected($raw_month, $available_month); ?>>
                                <?php echo esc_html(date_i18n('F Y', strtotime($available_month . '-01'))); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </form>
            <?php

            echo '<div class="paytoview-rentals-table-scroll">';
            echo '<table class="wp-list-table widefat fixed striped paytoview-log-table">';
            echo '<colgroup>';
            foreach ($display_headers as $header) {
                echo '<col class="' . esc_attr($this->paytoviewlite_get_log_column_class($header)) . '">';
                if ($expires_key !== null && $header === $expires_key) {
                    echo '<col class="paytoview-rental-log-col-status">';
                }
            }
            echo '</colgroup>';
            echo '<thead><tr>';
            foreach ($display_headers as $header) {
                $column_class = $this->paytoviewlite_get_log_column_class($header);
                echo '<th class="paytoview-rental-log-th ' . esc_attr($column_class) . '">' . esc_html($this->paytoviewlite_get_log_display_header($header)) . '</th>';
                if ($expires_key !== null && $header === $expires_key) {
                    echo '<th class="paytoview-rental-log-th paytoview-rental-log-col-status">' . esc_html__('Status', 'pay-to-view-lite') . '</th>';
                }
            }
            echo '</tr></thead><tbody>';

            if (empty($paged_rows)) {
                echo '<tr><td colspan="' . esc_attr(count($headers) + ($expires_key !== null ? 1 : 0)) . '">' . esc_html__('No rentals found.', 'pay-to-view-lite') . '</td></tr>';
            } else {
                foreach ($paged_rows as $row) {
                    $expires_timestamp = ($expires_key !== null && !empty($row[$expires_key])) ? strtotime($row[$expires_key]) : false;
                    $is_active = $expires_timestamp && $expires_timestamp > current_time('timestamp');
                    echo '<tr>';
                    foreach ($display_headers as $header) {
                        $val = isset($row[$header]) ? $row[$header] : '';
                        $column_class = $this->paytoviewlite_get_log_column_class($header);
                        echo '<td class="' . esc_attr($column_class) . '">' . esc_html($val) . '</td>';
                        if ($expires_key !== null && $header === $expires_key) {
                            echo '<td class="paytoview-rental-log-col-status"><span class="paytoview-rental-status-pill ' . esc_attr($is_active ? 'is-active' : 'is-expired') . '">' . esc_html($is_active ? 'Active' : 'Expired') . '</span></td>';
                        }
                    }
                    echo '</tr>';
                }
            }

            echo '</tbody><tfoot><tr><td colspan="' . esc_attr(count($headers) + ($expires_key !== null ? 1 : 0)) . '">';
            echo '<div class="paytoview-rentals-table-footer">';
            echo '<a class="paytoview-btn paytoview-export-csv" href="' . esc_url($export_url) . '" download>' . esc_html__('Export to CSV', 'pay-to-view-lite') . '</a>';

            if ($total_pages > 1) {
                echo '<div class="paytoview-pagination">';
                if ($paged > 1) {
                    echo '<a href="' . esc_url(add_query_arg(['paged' => $paged - 1, 'm' => $raw_month], $base_url)) . '" class="button paytoview-page-button">&laquo; ' . esc_html__('Prev', 'pay-to-view-lite') . '</a>';
                } else {
                    echo '<span class="button paytoview-page-button disabled">&laquo; ' . esc_html__('Prev', 'pay-to-view-lite') . '</span>';
                }

                for ($p = 1; $p <= min($total_pages, 20); $p++) {
                    $current_class = ($p === $paged) ? ' current-page' : '';
                    echo '<a href="' . esc_url(add_query_arg(['paged' => $p, 'm' => $raw_month], $base_url)) . '" class="button paytoview-page-button' . esc_attr($current_class) . '">' . esc_html($p) . '</a>';
                }

                if ($paged < $total_pages) {
                    echo '<a href="' . esc_url(add_query_arg(['paged' => $paged + 1, 'm' => $raw_month], $base_url)) . '" class="button paytoview-page-button">' . esc_html__('Next', 'pay-to-view-lite') . ' &raquo;</a>';
                } else {
                    echo '<span class="button paytoview-page-button disabled">' . esc_html__('Next', 'pay-to-view-lite') . ' &raquo;</span>';
                }
                echo '</div>';
            }

            echo '</div>';
            echo '</td></tr></tfoot></table>';
            echo '</div>';
            ?>
        </div>
    </div>
    <?php
}

private function paytoviewlite_get_log_path() {
    $upload_dir = wp_upload_dir();
    $log_dir = trailingslashit($upload_dir['basedir']) . 'pay-to-view-lite/';
    if (!file_exists($log_dir)) {
        wp_mkdir_p($log_dir);
    }
    return $log_dir . 'rental_log.csv';
}

public function paytoviewlite_export_rental_log() {
    if (!current_user_can('manage_options')) {
        wp_die(esc_html__('You do not have permission to export rental logs.', 'pay-to-view-lite'));
    }

    check_admin_referer('paytoviewlite_export_rental_log');

    $log_path = $this->paytoviewlite_get_log_path();
    if (!file_exists($log_path) || !is_readable($log_path)) {
        wp_die(esc_html__('Rental log file not found.', 'pay-to-view-lite'));
    }

    $raw_month = isset($_GET['m']) ? sanitize_text_field(wp_unslash($_GET['m'])) : 'current';
    if ($raw_month !== 'current' && $raw_month !== '' && !preg_match('/^\d{4}-\d{2}$/', $raw_month)) {
        $raw_month = 'current';
    }

    $selected_month = ('current' === $raw_month) ? gmdate('Y-m') : $raw_month;
    $rows = $this->paytoviewlite_read_csv_log($log_path);
    if (empty($rows)) {
        wp_die(esc_html__('No rental records found.', 'pay-to-view-lite'));
    }

    $headers = array_keys($rows[0]);
    $date_key = null;
    foreach ($headers as $header) {
        if (strcasecmp(trim($header), 'Date') === 0) {
            $date_key = $header;
            break;
        }
    }
    if (!$date_key) {
        $date_key = $headers[0];
    }

    if ($selected_month !== '') {
        $rows = array_values(array_filter($rows, function($row) use ($date_key, $selected_month) {
            $row_timestamp = !empty($row[$date_key]) ? strtotime($row[$date_key]) : false;
            return $row_timestamp && gmdate('Y-m', $row_timestamp) === $selected_month;
        }));
    }

    $filename = ($selected_month === '')
        ? 'rental_log_all_months.csv'
        : 'rental_log_' . $selected_month . '.csv';

    nocache_headers();
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('X-Content-Type-Options: nosniff');

    $output = fopen('php://output', 'w');
    fputcsv($output, array_map([$this, 'paytoviewlite_get_log_display_header'], $headers));
    foreach ($rows as $row) {
        fputcsv($output, array_map(function($header) use ($row) {
            return isset($row[$header]) ? $row[$header] : '';
        }, $headers));
    }
    fclose($output);
    exit;
}

private function paytoviewlite_read_csv_log($file_path) {
    $rows = [];

    if (!file_exists($file_path)) {
        return $rows;
    }

    $file_content = file_get_contents($file_path);
    if ($file_content === false) {
        return $rows;
    }

    $lines = preg_split('/\r\n|\r|\n/', trim($file_content));
    if (empty($lines)) {
        return $rows;
    }

    $headers = str_getcsv(array_shift($lines));
    if (empty($headers)) {
        return $rows;
    }

    $media_file_index = null;
    foreach ($headers as $index => $header) {
        if (strcasecmp(trim($header), 'Media File') === 0) {
            $media_file_index = $index;
            break;
        }
    }

    if ($media_file_index === null) {
        $rental_id_index = 2;
        foreach ($headers as $index => $header) {
            $normalized_header = trim($header);
            if (strcasecmp($normalized_header, 'Rental ID') === 0 || strcasecmp($normalized_header, 'Slug') === 0) {
                $rental_id_index = $index;
                break;
            }
        }

        $media_file_index = $rental_id_index + 1;
        array_splice($headers, $media_file_index, 0, 'Media File');
    }

    foreach ($lines as $line) {
        if ($line === '') {
            continue;
        }
        $data = str_getcsv($line);
        if (count($data) === count($headers) - 1) {
            array_splice($data, $media_file_index, 0, '');
        }
        if (count($data) === count($headers)) {
            $rows[] = array_combine($headers, $data);
        }
    }

    return $rows;
}

private function paytoviewlite_get_log_display_header($header) {
    $normalized_header = trim((string) $header);

    if (strcasecmp($normalized_header, 'Slug') === 0) {
        return __('Rental ID', 'pay-to-view-lite');
    }

    if (strcasecmp($normalized_header, 'TxnID') === 0) {
        return __('Txn ID', 'pay-to-view-lite');
    }

    if (strcasecmp($normalized_header, 'PaymentStatus') === 0) {
        return __('Payment Status', 'pay-to-view-lite');
    }

    return $header;
}

private function paytoviewlite_get_rental_log_display_headers($headers) {
    $ordered_headers = array_values($headers);
    $amount_index = null;
    $media_file_index = null;

    foreach ($ordered_headers as $index => $header) {
        $normalized_header = trim((string) $header);

        if (strcasecmp($normalized_header, 'Amount') === 0) {
            $amount_index = $index;
        }

        if (strcasecmp($normalized_header, 'Media File') === 0) {
            $media_file_index = $index;
        }
    }

    if ($amount_index === null || $media_file_index === null || $amount_index === $media_file_index + 1) {
        return $ordered_headers;
    }

    $amount_header = $ordered_headers[$amount_index];
    array_splice($ordered_headers, $amount_index, 1);

    if ($amount_index < $media_file_index) {
        $media_file_index--;
    }

    array_splice($ordered_headers, $media_file_index + 1, 0, [$amount_header]);

    return $ordered_headers;
}

private function paytoviewlite_get_log_column_class($header) {
    $header = trim((string) $header);
    $class_suffix = strcasecmp($header, 'Slug') === 0 ? 'rental-id' : sanitize_title($header);

    if ($class_suffix === '') {
        $class_suffix = 'unknown';
    }

    return 'paytoview-rental-log-col-' . sanitize_html_class($class_suffix);
}

private function paytoviewlite_get_media_display_title($media_value) {
    $media_value = trim((string) $media_value);
    if ($media_value === '') {
        return '';
    }

    if (ctype_digit($media_value)) {
        $title = get_the_title((int) $media_value);
        if (!empty($title)) {
            return $title;
        }
    }

    $path = parse_url($media_value, PHP_URL_PATH);
    $basename = basename($path ?: $media_value);
    $title = preg_replace('/\.[a-z0-9]{2,5}$/i', '', $basename);
    $title = preg_replace('/[-_]+/', ' ', $title);
    $title = trim($title);

    return $title !== '' ? ucwords($title) : $media_value;
}

private function paytoviewlite_render_admin_banner($title, $icon, $intro_html) {
    ?>
    <div class="paytoview-logo paytoview-logo--with-intro">
        <img src="<?php echo esc_url(PAYTOVIEWLITE_PLUGIN_URL . 'assets/img/PTVlogo.svg'); ?>" alt="Pay-To-View Logo" />
        <div class="paytoview-banner-intro">
            <h1 class="paytoview-banner-title">
               
                <?php echo esc_html($title); ?>
            </h1>
            <p><?php echo wp_kses_post($intro_html); ?></p>
        </div>
    </div>
    <?php
}

private function paytoviewlite_render_shortcode_header($active_tab = 'generate') {
    $generate_url = admin_url('admin.php?page=paytoview_lite_shortcode&paytoview_tab=generate');
    $list_url = admin_url('admin.php?page=paytoview_lite_shortcode&paytoview_tab=list');
    $intro_html = esc_html__('Create shortcodes for videos. Insert a shortcode into a page, and it will display a rental button. Save shortcodes in the list. First, configure the plugin on the', 'pay-to-view-lite') . ' ' .
        '<a href="' . esc_url(admin_url('admin.php?page=paytoview_lite_main')) . '">' . esc_html__('Settings page', 'pay-to-view-lite') . '</a>.';

    $this->paytoviewlite_render_admin_banner(
        __('Shortcode Generator', 'pay-to-view-lite'),
        null,
        $intro_html
    );
    ?>
    <nav class="paytoview-shortcode-pills" aria-label="<?php echo esc_attr__('Shortcode pages', 'pay-to-view-lite'); ?>">
        <a class="paytoview-shortcode-pill<?php echo $active_tab === 'generate' ? ' is-active' : ''; ?>" href="<?php echo esc_url($generate_url); ?>">
            <?php esc_html_e('Generate shortcode', 'pay-to-view-lite'); ?>
        </a>
        <a class="paytoview-shortcode-pill<?php echo $active_tab === 'list' ? ' is-active' : ''; ?>" href="<?php echo esc_url($list_url); ?>">
            <?php esc_html_e('Shortcode list', 'pay-to-view-lite'); ?>
        </a>
    </nav>
    <?php
}

private function paytoviewlite_get_saved_shortcodes() {
    $upload_dir = wp_upload_dir();
    $shortcode_file = trailingslashit($upload_dir['basedir']) . 'pay-to-view-lite/shortcodes.json';
    $shortcodes = [];

    if (file_exists($shortcode_file)) {
        $shortcode_data = file_get_contents($shortcode_file);
        $decoded_shortcodes = json_decode($shortcode_data, true);
        if (is_array($decoded_shortcodes)) {
            $shortcodes = $decoded_shortcodes;
        }
    }

    usort($shortcodes, function($a, $b) {
        return strtotime($b['created'] ?? '') <=> strtotime($a['created'] ?? '');
    });

    return $shortcodes;
}

private function paytoviewlite_get_shortcode_list_page_data($requested_page = 1) {
    $shortcodes = $this->paytoviewlite_get_saved_shortcodes();
    $per_page = 10;
    $page = max(1, (int) $requested_page);
    $total_pages = max(1, (int) ceil(count($shortcodes) / $per_page));
    $page = min($page, $total_pages);
    $offset = ($page - 1) * $per_page;

    return [
        'shortcodes' => array_slice($shortcodes, $offset, $per_page, true),
        'page' => $page,
        'total_pages' => $total_pages,
    ];
}

private function paytoviewlite_render_shortcode_list_rows($paged_shortcodes, $currency) {
    ob_start();
    ?>
    <?php if (empty($paged_shortcodes)) : ?>
        <tr>
            <td colspan="7"><?php esc_html_e('No saved shortcodes yet.', 'pay-to-view-lite'); ?></td>
        </tr>
    <?php else : ?>
        <?php foreach ($paged_shortcodes as $row) :
            $row_currency = $row['currency'] ?? $currency;
            $shortcode_raw = '[paytoviewlite rentalid="' . esc_attr($row['slug'] ?? '') . '" videoid="' . esc_attr($row['videoid'] ?? '') . '" media_player="' . esc_attr($row['media_player'] ?? '') . '"]';
            $wrapped_shortcode = '<!-- wp:shortcode -->' . $shortcode_raw . '<!-- /wp:shortcode -->';
            $delete_key = md5(($row['created'] ?? '') . '|' . ($row['slug'] ?? '') . '|' . ($row['videoid'] ?? '') . '|' . ($row['media_player'] ?? ''));
            ?>
            <tr data-delete-key="<?php echo esc_attr($delete_key); ?>">
                <td class="paytoview-shortcode-col-slug"><?php echo esc_html($row['slug'] ?? ''); ?></td>
                
                <td class="paytoview-shortcode-col-price"><?php echo esc_html(number_format((float) ($row['fixed_price'] ?? 0), 2) . ' ' . $row_currency); ?></td>
                <td class="paytoview-shortcode-col-duration"><?php echo esc_html(($row['fixed_duration'] ?? '') . ' h'); ?></td>
                <td class="paytoview-shortcode-col-media"><?php echo esc_html($row['media_player'] ?? ''); ?></td>
                <td class="paytoview-shortcode-col-file"><?php echo esc_html($row['videoid'] ?? ''); ?></td>
                <td class="paytoview-shortcode-col-code"><code><?php echo esc_html($shortcode_raw); ?></code></td>
                <td class="paytoview-shortcode-col-actions">
                    <div class="paytoview-actions-cell">
                        <button type="button" class="button paytoview-icon-button copy-shortcode-button" data-shortcode="<?php echo esc_attr($wrapped_shortcode); ?>" aria-label="Copy shortcode" title="Copy shortcode">
                            <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <rect x="9" y="9" width="10" height="10" rx="2"></rect>
                                <path d="M5 15V7a2 2 0 0 1 2-2h8"></path>
                            </svg>
                        </button>
                        <button type="button" class="button paytoview-icon-button paytoview-trash-button delete-shortcode-button" data-delete-key="<?php echo esc_attr($delete_key); ?>" aria-label="Delete shortcode" title="Delete shortcode">
                            <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path d="M3 6h18"></path>
                                <path d="M8 6V4h8v2"></path>
                                <path d="M6 6l1 15h10l1-15"></path>
                                <path d="M10 11v6"></path>
                                <path d="M14 11v6"></path>
                            </svg>
                        </button>
                    </div>
                </td>
            </tr>
        <?php endforeach; ?>
    <?php endif; ?>
    <?php
    return ob_get_clean();
}

private function paytoviewlite_render_shortcode_list_pagination($page, $total_pages) {
    ob_start();
    ?>
    <div class="paytoview-pagination" data-current-page="<?php echo esc_attr($page); ?>">
        <?php $prev_page = $page - 1; $next_page = $page + 1; ?>
        <?php if ($page > 1) : ?>
            <a class="button paytoview-page-button" href="<?php echo esc_url(add_query_arg('shortcodes_paged', $prev_page)); ?>" data-page="<?php echo esc_attr($prev_page); ?>">&laquo; Prev</a>
        <?php else : ?>
            <span class="button paytoview-page-button disabled">&laquo; Prev</span>
        <?php endif; ?>

        <?php for ($i = 1; $i <= $total_pages; $i++) : ?>
            <?php if ($i === $page) : ?>
                <span class="button paytoview-page-button current-page" data-page="<?php echo esc_attr($i); ?>"><?php echo esc_html($i); ?></span>
            <?php else : ?>
                <a class="button paytoview-page-button" href="<?php echo esc_url(add_query_arg('shortcodes_paged', $i)); ?>" data-page="<?php echo esc_attr($i); ?>"><?php echo esc_html($i); ?></a>
            <?php endif; ?>
        <?php endfor; ?>

        <?php if ($page < $total_pages) : ?>
            <a class="button paytoview-page-button" href="<?php echo esc_url(add_query_arg('shortcodes_paged', $next_page)); ?>" data-page="<?php echo esc_attr($next_page); ?>">Next &raquo;</a>
        <?php else : ?>
            <span class="button paytoview-page-button disabled">Next &raquo;</span>
        <?php endif; ?>
    </div>
    <?php
    return ob_get_clean();
}

private function paytoviewlite_render_shortcode_list_table($currency, $requested_page = 1) {
    $list_page = $this->paytoviewlite_get_shortcode_list_page_data($requested_page);
    ?>
    <div class="postbox paytoview-shortcode-list-postbox">
        <table class="wp-list-table widefat fixed striped paytoview-log-table paytoview-shortcode-list-table">
            <colgroup>
                <col class="paytoview-shortcode-col-slug">
                <col class="paytoview-shortcode-col-price">
                <col class="paytoview-shortcode-col-duration">
                <col class="paytoview-shortcode-col-media">
                <col class="paytoview-shortcode-col-file">
                <col class="paytoview-shortcode-col-code">
                <col class="paytoview-shortcode-col-actions">
            </colgroup>
            <thead>
                <tr class="paytoview-table-title-row">
                    <th colspan="7" scope="colgroup">
                        <div class="paytoview-table-title-inner"><?php esc_html_e('Shortcode list', 'pay-to-view-lite'); ?></div>
                    </th>
                </tr>
                <tr>
                    <th class="paytoview-shortcode-col-slug"><?php esc_html_e('Rental ID', 'pay-to-view-lite'); ?></th>
                    <th class="paytoview-shortcode-col-price"><?php esc_html_e('Price', 'pay-to-view-lite'); ?></th>
                    <th class="paytoview-shortcode-col-duration"><?php esc_html_e('Duration', 'pay-to-view-lite'); ?></th>
                    <th class="paytoview-shortcode-col-media"><?php esc_html_e('Media Player', 'pay-to-view-lite'); ?></th>
                     <th class="paytoview-shortcode-col-file"><?php esc_html_e('Media ID or URL', 'pay-to-view-lite'); ?></th>
                    <th class="paytoview-shortcode-col-code"><?php esc_html_e('Shortcode', 'pay-to-view-lite'); ?></th>
                    <th class="paytoview-shortcode-col-actions"><?php esc_html_e('Actions', 'pay-to-view-lite'); ?></th>
                </tr>
            </thead>
            <tbody id="paytoviewlite-shortcode-list-body">
                <?php echo $this->paytoviewlite_render_shortcode_list_rows($list_page['shortcodes'], $currency); ?>
            </tbody>
        </table>

        <div class="paytoview-shortcode-table-footer">
            <?php echo $this->paytoviewlite_render_shortcode_list_pagination($list_page['page'], $list_page['total_pages']); ?>
        </div>
    </div>
    <?php
}

public function paytoviewlite_shortcode_generator_page() {
    $options = get_option($this->option_name);
    if (!is_array($options)) {
        $options = [];
    }
    $active_tab = isset($_GET['paytoview_tab']) ? sanitize_key(wp_unslash($_GET['paytoview_tab'])) : 'generate';
    if (!in_array($active_tab, ['generate', 'list'], true)) {
        $active_tab = 'generate';
    }
    $fixed_price = isset($options['fixed_price']) ? esc_attr($options['fixed_price']) : '';
    $fixed_duration = isset($options['fixed_duration']) ? esc_attr($options['fixed_duration']) : '';
    $currency = $options['custom_currency'] ?? ($options['paytoview_currency'] ?? 'USD');
    $required_fields = ['slug', 'videoid'];
    $upload_dir = wp_upload_dir();
    $shortcode_dir = trailingslashit($upload_dir['basedir']) . 'pay-to-view-lite/';
    $shortcode_file = $shortcode_dir . 'shortcodes.json';
    $generated_shortcode_value = '';

    if (!file_exists($shortcode_dir)) {
        wp_mkdir_p($shortcode_dir);
    }

    $shortcodes = [];
    if (file_exists($shortcode_file)) {
        $shortcode_data = file_get_contents($shortcode_file);
        $decoded_shortcodes = json_decode($shortcode_data, true);
        if (is_array($decoded_shortcodes)) {
            $shortcodes = $decoded_shortcodes;
        }
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['paytoviewlite_save_shortcode'])) {
        check_admin_referer('paytoviewlite_save_shortcode_action', 'paytoviewlite_save_shortcode_nonce');

        $slug = sanitize_text_field(wp_unslash($_POST['slug'] ?? ''));
        $videoid = sanitize_text_field(wp_unslash($_POST['videoid'] ?? ''));
        $media_player = sanitize_text_field(wp_unslash($_POST['media_player'] ?? ''));

        if ($media_player === 'jetpack' && preg_match('/^\[videopress\s+(.+?)\]$/i', $videoid, $matches)) {
            $videoid = trim($matches[1]);
        } elseif ($media_player === 'aiovg' && preg_match('/\[aiovg_video\s+mp4="([^"]+)"\]/i', $videoid, $matches)) {
            $videoid = $matches[1];
        } elseif ($media_player === 'html5' && preg_match('/\[html5_video\s+id="?(\d+)"?\]/i', $videoid, $matches)) {
            $videoid = $matches[1];
        } elseif ($media_player === 'fv_player' && preg_match('/\[fvplayer\s+id="?(\d+)"?\]/i', $videoid, $matches)) {
            $videoid = $matches[1];
        }

        if ($slug && $videoid && $media_player) {
            $shortcodes[] = [
                'slug' => $slug,
                'fixed_price' => $fixed_price,
                'fixed_duration' => $fixed_duration,
                'currency' => $currency,
                'media_player' => $media_player,
                'videoid' => $videoid,
                'created' => current_time('mysql'),
            ];
            file_put_contents($shortcode_file, wp_json_encode(array_values($shortcodes), JSON_PRETTY_PRINT));
            $generated_shortcode_value = '[paytoviewlite rentalid="' . $slug . '" videoid="' . $videoid . '" media_player="' . $media_player . '"]';
        }
    }

    ?>
    <div class="wrap">
        <?php $this->paytoviewlite_render_shortcode_header($active_tab); ?>
        <?php if ($active_tab === 'list') : ?>
            <?php $this->paytoviewlite_render_shortcode_list_table($currency, $_GET['shortcodes_paged'] ?? 1); ?>
        <?php else : ?>
        <div class="postbox">
            <form id="paytoview-shortcode-form" method="post">
                <?php wp_nonce_field('paytoviewlite_save_shortcode_action', 'paytoviewlite_save_shortcode_nonce'); ?>
                <table class="form-table">
                    <tr class="paytoview-table-title-row">
                        <th colspan="2" scope="colgroup"><div class="paytoview-table-title-inner">Generate shortcode</div></th>
                    </tr>
                    <tr>
                        <th>
                            <label for="slug">
                                <?php 
                                echo wp_kses_post($this->get_label_with_required(
                                    'Rental ID',
                                    'slug',
                                    $required_fields
                                )); 
                                echo wp_kses_post(paytoviewlite_tooltip_icon(
                                    'Rental ID',
                                    'This identifies the rental in the log and is used for cookies, logs, and confirmation emails. It does not need to match the page URL slug or be unique.'
                                )); 
                                ?>
                            </label>
                        </th>
                        <td>
                            <input type="text" id="slug" name="slug" class="regular-text" required value="<?php echo esc_attr($_POST['slug'] ?? ''); ?>">
                            <p class="description"><em>Add a rental ID for this video, such as the page slug, video title etc. Do not use spaces or special characters.</em></p>
                        </td>
                    </tr>
                    <tr>
                        <th><label for="fixed_price">Fixed Price</label></th>
                        <td>
                            <input name="fixed_price" value="<?php echo esc_attr($fixed_price); ?>" type="number" step="0.01" min="0" class="small-text" disabled>
                            <span><?php echo esc_html($currency); ?></span>
                            <p class="description"><em>The price is set on the Settings page</em></p>
                        </td>
                    </tr>
                    <tr>
                        <th><label for="fixed_duration">Fixed Duration</label></th>
                        <td>
                            <input type="text" id="fixed_duration" name="fixed_duration" value="<?php echo esc_attr($fixed_duration); ?>" min="1" step="1" class="paytoview-fixed-duration" disabled> hours
                            <p class="description"><em>The duration is set on the Settings page</em></p>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <label for="media_player">
                                <?php
                                echo wp_kses_post($this->get_label_with_required(
                                    'Media Player',
                                    'media_player',
                                    []
                                ));
                                echo wp_kses_post(paytoviewlite_tooltip_icon(
                                    'media_player',
                                    'Choose which video player to use for playback. This affects how the shortcode will output the video.'
                                ));
                                ?>
                            </label>
                        </th>
                        <td>
                            <select id="media_player" name="media_player" class="regular-text" required>
                                <option value=""><?php echo esc_html__('Select player', 'pay-to-view-lite'); ?></option>
                                <option value="aiovg" <?php selected($_POST['media_player'] ?? '', 'aiovg'); ?>>All in one video gallery</option>
                                <option value="fv_player" <?php selected($_POST['media_player'] ?? '', 'fv_player'); ?>>FV Player</option>
                                <option value="html5" <?php selected($_POST['media_player'] ?? '', 'html5'); ?>>HTML5 Player</option>
                                <option value="jetpack" <?php selected($_POST['media_player'] ?? '', 'jetpack'); ?>>JetPack VideoPress</option>
                                <option value="presto_player" <?php selected($_POST['media_player'] ?? '', 'presto_player'); ?>>Presto Player</option>
                                <option value="youtube" <?php selected($_POST['media_player'] ?? '', 'youtube'); ?>>YouTube</option>
                                <option value="vimeo" <?php selected($_POST['media_player'] ?? '', 'vimeo'); ?>>Vimeo</option>
                            </select>
                            <p class="description"><em>Select the video player to use when rendering this shortcode.</em></p>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <label for="videoid">
                                <?php 
                                echo wp_kses_post($this->get_label_with_required(
                                    'Media ID or URL',
                                    'videoid',
                                    $required_fields
                                )); 
                                echo wp_kses_post(paytoviewlite_tooltip_icon(
                                    'VideoID', 
                                    'Add the ID or URL to your media file, depending on the media player you use. If you use shortcodes for the media file, only paste the ID. Example: a video shortcode like this [html5_video id=53] shall be added as simply 53 here.'
                                )); 
                                ?>
                            </label>
                        </th>
                        <td>
                            <input type="text" id="videoid" name="videoid" class="regular-text" required value="<?php echo esc_attr($_POST['videoid'] ?? ''); ?>">
                            <p class="description"><em>Add the shortcode ID (do not add the entire shortcode!) or URL to your media file.</em></p>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <label for="generated_shortcode">Rental shortcode</label>
                        </th>
                        <td>
                            <div class="paytoview-generated-shortcode-row">
                                <input type="text" id="generated_shortcode" readonly class="paytoview-generated-shortcode" value="<?php echo esc_attr($generated_shortcode_value); ?>">
                                <button type="button" id="copy_generated_shortcode" class="button paytoview-icon-button" onclick="copyShortcode()" aria-label="Copy shortcode" title="Copy shortcode">
                                    <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                        <rect x="9" y="9" width="10" height="10" rx="2"></rect>
                                        <path d="M5 15V7a2 2 0 0 1 2-2h8"></path>
                                    </svg>
                                </button>
                                <span id="copy_status" class="paytoview-copy-status"></span>
                            </div>
                            <p class="description"><em>Copy and paste this on a page or post where you have a media file for rental.</em></p>
                        </td>
                    </tr>
                </table>
                <p class="paytoview-save-shortcode-row">
                    <button type="button" id="paytoviewlite_generate_shortcode" name="paytoviewlite_save_shortcode" value="1" class="paytoview-btn">Save shortcode</button>
                    <p class="description"><em>All shortcodes are saved in the shortcode list.</em></p>
                    <span id="paytoviewlite_save_shortcode_status" class="paytoview-save-shortcode-status" role="status" aria-live="polite"></span>
                </p>
                
            </form>   
        </div>
        <?php endif; ?>
    </div>
    <?php
}

public function paytoviewlite_shortcode_list_page() {
    $options = get_option($this->option_name);
    if (!is_array($options)) {
        $options = [];
    }
    $currency = $options['custom_currency'] ?? ($options['paytoview_currency'] ?? 'USD');
    ?>
    <div class="wrap">
        <?php $this->paytoviewlite_render_shortcode_header('list'); ?>
        <?php $this->paytoviewlite_render_shortcode_list_table($currency, $_GET['shortcodes_paged'] ?? 1); ?>
    </div>
    <?php
}

public function paytoviewlite_shortcode_handler($atts) {
    $atts = shortcode_atts([
        'rentalid'     => '',
        'slug'         => '',
        'videoid'      => '',
        'media_player' => '',
    ], $atts);

    $slug         = sanitize_text_field($atts['rentalid'] ?: $atts['slug']);
    $videoid      = trim($atts['videoid']);
    $video_player = sanitize_text_field($atts['media_player'] ?? '');

    $options         = get_option($this->option_name);
    $fixed_price     = isset($options['fixed_price']) ? floatval($options['fixed_price']) : 0;
    $expire_hours    = isset($options['fixed_duration']) ? intval($options['fixed_duration']) : 48;
    $default_player  = $options['media_player'] ?? 'wp_video';
    $player          = $video_player ?: $default_player;

    // Rental validation via cookie
    $cookie_name = 'paytoview_rental_' . $slug;
    $validRental = false;

    $raw_cookie = isset($_COOKIE[$cookie_name]) ? sanitize_text_field(wp_unslash($_COOKIE[$cookie_name])) : '';
    if (!empty($raw_cookie)) {
        $cookie_value = json_decode($raw_cookie, true);
        if (is_array($cookie_value)) {
            $expires_raw = sanitize_text_field($cookie_value['expires'] ?? '');
            if (!empty($expires_raw) && strtotime($expires_raw) > time()) {
                $validRental = true;
            }
        }
    }

    // JS data for PayPal
    $data = [
        'slug'        => $slug,
        'mediaFile'   => $videoid,
        'mediaTitle'  => $this->paytoviewlite_get_media_display_title($videoid),
        'price'       => $fixed_price > 0 ? $fixed_price : 0.01,
        'expireHours' => $expire_hours > 0 ? $expire_hours : 48,
        'ajaxurl'     => admin_url('admin-ajax.php'),
        'nonce'       => wp_create_nonce('paytoviewlite_nonce'),
    ];
    $this->shortcode_items[] = $data;

    ob_start();
    ?>
    <div id="paytoview-container-<?php echo esc_attr($slug); ?>" class="paytoview-container">

        <!-- Rent Button (hidden if already rented) -->
        <button id="paytoview-rent-button-<?php echo esc_attr($slug); ?>"
                class="paytoviewlite-rent-button"
                style="<?php echo esc_attr($validRental ? 'display:none;' : ''); ?>">
            <?php esc_html_e('Rent this movie', 'pay-to-view-lite'); ?>
        </button>

        <!-- PayPal Button Container (hidden if already rented) -->
        <div id="paypal-button-container-<?php echo esc_attr($slug); ?>"
             class="paytoview-paypal-button-container"
             style="<?php echo esc_attr($validRental ? 'display:none;' : ''); ?>">
        </div>
        
        <!-- Video Container -->
    <div id="paytoview-video-container-<?php echo esc_attr($slug); ?>"
     class="paytoview-video-container">
    <?php 
    if ($validRental) {
        $videoid_safe = sanitize_text_field($videoid);
        $video_output = '';

        switch ($player) {
            case 'youtube':
                if (preg_match('/(?:youtu\.be\/|v=|embed\/)([a-zA-Z0-9_-]{11})/', $videoid_safe, $matches)) {
                    $youtube_id = $matches[1];
                } else {
                    $youtube_id = $videoid_safe;
                }
                $video_output = '<iframe width="100%" height="480" src="https://www.youtube.com/embed/' . esc_attr($youtube_id) . '" frameborder="0" allowfullscreen></iframe>';
                break;
        
            case 'vimeo':
                if (preg_match('/(?:vimeo\.com\/(?:video\/)?|player\.vimeo\.com\/video\/)?(\d+)/', $videoid_safe, $matches)) {
                    $vimeo_id = $matches[1];
                    $video_output = '<iframe src="https://player.vimeo.com/video/' . esc_attr($vimeo_id) . '" width="640" height="360" frameborder="0" allowfullscreen></iframe>';
                } else {
                    $video_output = '<p>Invalid Vimeo video ID or URL.</p>';
                }
                break;

            case 'presto_player':
                $video_output = do_shortcode('[presto_player id="' . esc_attr($videoid_safe) . '"]');
                break;
        
            case 'html5':
                $video_output = do_shortcode('[html5_video id="' . esc_attr($videoid_safe) . '"]');
                break;
        
            case 'fv_player':
                $video_output = do_shortcode('[fvplayer id="' . esc_attr($videoid_safe) . '"]');
                break;
        
            case 'jetpack':
                $video_output = do_shortcode('[videopress ' . esc_attr($videoid_safe) . ']');
                break;
        
            case 'aiovg':
                $video_output = do_shortcode('[aiovg_video mp4="' . esc_url($videoid_safe) . '"]');
                break;
        
            default:
                $video_output = '<p>Unsupported media player type.</p>';
                break;
        }

        echo '<div class="paytoviewlite-video">' . $video_output . '</div>';
    }
    ?>
</div>
</div>
<?php
return ob_get_clean();
}

// --- Rental Log Checker ---
private function paytoviewlite_check_rental_log($email, $slug, $expires) {
    $file = $this->paytoviewlite_get_log_path();
    if (!file_exists($file)) return false;

    $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        $parts = str_getcsv($line);
        if (count($parts) >= 7 &&
            $parts[1] === $email &&
            $parts[2] === $slug &&
            abs(strtotime($parts[5]) - strtotime($expires)) < 60) {
            return true;
        }
    }
    return false;
}

public function paytoviewlite_ajax_save_shortcode() {
    check_ajax_referer('paytoviewlite_save_shortcode_ajax', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied.', 'pay-to-view-lite')]);
    }

    $options = get_option($this->option_name);
    if (!is_array($options)) {
        $options = [];
    }

    $slug = sanitize_text_field(wp_unslash($_POST['slug'] ?? ''));
    $videoid = sanitize_text_field(wp_unslash($_POST['videoid'] ?? ''));
    $media_player = sanitize_text_field(wp_unslash($_POST['media_player'] ?? ''));

    if (!$slug || !$videoid || !$media_player) {
        wp_send_json_error(['message' => __('Missing required shortcode fields.', 'pay-to-view-lite')]);
    }

    if ($media_player === 'jetpack' && preg_match('/^\[videopress\s+(.+?)\]$/i', $videoid, $matches)) {
        $videoid = trim($matches[1]);
    } elseif ($media_player === 'aiovg' && preg_match('/\[aiovg_video\s+mp4="([^"]+)"\]/i', $videoid, $matches)) {
        $videoid = $matches[1];
    } elseif ($media_player === 'html5' && preg_match('/\[html5_video\s+id="?(\d+)"?\]/i', $videoid, $matches)) {
        $videoid = $matches[1];
    } elseif ($media_player === 'fv_player' && preg_match('/\[fvplayer\s+id="?(\d+)"?\]/i', $videoid, $matches)) {
        $videoid = $matches[1];
    }

    $fixed_price = $options['fixed_price'] ?? '';
    $fixed_duration = $options['fixed_duration'] ?? '';
    $currency = $options['custom_currency'] ?? ($options['paytoview_currency'] ?? 'USD');

    $upload_dir = wp_upload_dir();
    $shortcode_dir = trailingslashit($upload_dir['basedir']) . 'pay-to-view-lite/';
    $shortcode_file = $shortcode_dir . 'shortcodes.json';

    if (!file_exists($shortcode_dir)) {
        wp_mkdir_p($shortcode_dir);
    }

    $shortcodes = [];
    if (file_exists($shortcode_file)) {
        $shortcode_data = file_get_contents($shortcode_file);
        $decoded_shortcodes = json_decode($shortcode_data, true);
        if (is_array($decoded_shortcodes)) {
            $shortcodes = $decoded_shortcodes;
        }
    }

    $new_shortcode = [
        'slug' => $slug,
        'fixed_price' => $fixed_price,
        'fixed_duration' => $fixed_duration,
        'currency' => $currency,
        'media_player' => $media_player,
        'videoid' => $videoid,
        'created' => current_time('mysql'),
    ];

    $shortcodes[] = $new_shortcode;

    if (file_put_contents($shortcode_file, wp_json_encode(array_values($shortcodes), JSON_PRETTY_PRINT)) === false) {
        wp_send_json_error(['message' => __('Could not save shortcode.', 'pay-to-view-lite')]);
    }

    $shortcode_raw = '[paytoviewlite rentalid="' . $slug . '" videoid="' . $videoid . '" media_player="' . $media_player . '"]';
    $wrapped_shortcode = '<!-- wp:shortcode -->' . $shortcode_raw . '<!-- /wp:shortcode -->';

    wp_send_json_success([
        'delete_key' => md5($new_shortcode['created'] . '|' . $slug . '|' . $videoid . '|' . $media_player),
        'slug' => $slug,
        'media_file' => $videoid,
        'price' => number_format((float) $fixed_price, 2) . ' ' . $currency,
        'duration' => $fixed_duration . ' hours',
        'media_player' => $media_player,
        'shortcode' => $shortcode_raw,
        'wrapped_shortcode' => $wrapped_shortcode,
    ]);
}

public function paytoviewlite_ajax_delete_shortcode() {
    check_ajax_referer('paytoviewlite_delete_shortcode_ajax', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied.', 'pay-to-view-lite')]);
    }

    $delete_key = sanitize_text_field(wp_unslash($_POST['delete_key'] ?? ''));
    if (!$delete_key) {
        wp_send_json_error(['message' => __('Missing shortcode key.', 'pay-to-view-lite')]);
    }

    $upload_dir = wp_upload_dir();
    $shortcode_file = trailingslashit($upload_dir['basedir']) . 'pay-to-view-lite/shortcodes.json';

    if (!file_exists($shortcode_file)) {
        wp_send_json_error(['message' => __('Shortcode list not found.', 'pay-to-view-lite')]);
    }

    $shortcode_data = file_get_contents($shortcode_file);
    $shortcodes = json_decode($shortcode_data, true);
    if (!is_array($shortcodes)) {
        wp_send_json_error(['message' => __('Could not read shortcode list.', 'pay-to-view-lite')]);
    }

    $original_count = count($shortcodes);
    $shortcodes = array_filter($shortcodes, function($row) use ($delete_key) {
        $row_key = md5(($row['created'] ?? '') . '|' . ($row['slug'] ?? '') . '|' . ($row['videoid'] ?? '') . '|' . ($row['media_player'] ?? ''));
        return !hash_equals($row_key, $delete_key);
    });

    if (count($shortcodes) === $original_count) {
        wp_send_json_error(['message' => __('Shortcode was not found.', 'pay-to-view-lite')]);
    }

    if (file_put_contents($shortcode_file, wp_json_encode(array_values($shortcodes), JSON_PRETTY_PRINT)) === false) {
        wp_send_json_error(['message' => __('Could not delete shortcode.', 'pay-to-view-lite')]);
    }

    wp_send_json_success(['deleted' => $delete_key]);
}

public function paytoviewlite_ajax_load_shortcode_list() {
    check_ajax_referer('paytoviewlite_load_shortcode_list_ajax', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => __('Permission denied.', 'pay-to-view-lite')]);
    }

    $options = get_option($this->option_name);
    if (!is_array($options)) {
        $options = [];
    }

    $currency = $options['custom_currency'] ?? ($options['paytoview_currency'] ?? 'USD');
    $requested_page = isset($_POST['page']) ? absint($_POST['page']) : 1;
    $list_page = $this->paytoviewlite_get_shortcode_list_page_data($requested_page);

    wp_send_json_success([
        'rows' => $this->paytoviewlite_render_shortcode_list_rows($list_page['shortcodes'], $currency),
        'pagination' => $this->paytoviewlite_render_shortcode_list_pagination($list_page['page'], $list_page['total_pages']),
        'page' => $list_page['page'],
    ]);
}

// --- Log Rental (UTC) ---
public function paytoviewlite_ajax_log_rental() {
    check_ajax_referer('paytoviewlite_nonce');

    $input = file_get_contents('php://input');
    $data  = json_decode($input, true);

    if (!is_array($data)) {
        wp_send_json_error(__('Invalid JSON', 'pay-to-view-lite'));
    }

    $email         = sanitize_email($data['email'] ?? '');
    $slug          = sanitize_text_field($data['slug'] ?? '');
    $txn_id        = sanitize_text_field($data['txn_id'] ?? '');
    $paymentStatus = sanitize_text_field($data['paymentStatus'] ?? '');
    $amount        = floatval($data['amount'] ?? 0);
    $expireHours   = intval($data['expireHours'] ?? 0);
    $media_file    = sanitize_text_field($data['mediaFile'] ?? '');
    $media_title   = sanitize_text_field($data['mediaTitle'] ?? '');

    if (!$email || !$slug || !$txn_id || !$paymentStatus || !$amount || !$expireHours) {
        wp_send_json_error(__('Missing or invalid required fields', 'pay-to-view-lite'));
    }

    $nowUtc     = new DateTime('now', new DateTimeZone('UTC'));
    $expiresUtc = clone $nowUtc;
    $expiresUtc->modify("+{$expireHours} hours");

    $options = get_option($this->option_name);

    $site_name   = get_bloginfo('name');
    $site_url    = home_url();
    $movie_page  = get_page_by_path($slug);
    $movie_title = $movie_page ? get_the_title($movie_page) : ucfirst($slug);
    $movie_url   = $movie_page ? get_permalink($movie_page) : $site_url;

    $expires_local = get_date_from_gmt($expiresUtc->format('Y-m-d H:i:s'), 'F j, Y, g:i a');
    
    // translators: %d is the number of hours for the movie rental duration
    $duration_text = sprintf(_n('%d hour', '%d hours', $expireHours, 'pay-to-view-lite'), $expireHours);

    $from_email = !empty($options['email_from']) ? sanitize_email($options['email_from']) : get_option('admin_email');
    $default_subject = 'Rental on {site_name}';
    $default_message = "Thank you for renting with us!<br>Media rented: {media_file}<br>Your access expires on: {expiration_date}<br><br>/The team on {site_name}";
    $old_default_subjects = ['Movie rental confirmation', 'Rental confirmation'];
    $old_default_messages = [
        "Thank you for renting our movies.<br><br>You can watch this movie for {duration_hours}.<br>Your access expires on: {expiration_date}<br><br>Enjoy!",
        "Thank you for renting with us.<br><br>Your access expires on: {expiration_date}<br><br>Enjoy!",
        "Thank you for renting with us!<br>Your access expires on: {expiration_date}<br><br>/The team on {site_name}",
    ];

    $subject = !empty($options['email_subject']) ? sanitize_text_field($options['email_subject']) : $default_subject;
    if (in_array($subject, $old_default_subjects, true)) {
        $subject = $default_subject;
    }

    $message    = !empty($options['email_message']) 
        ? wp_kses_post($options['email_message']) 
        : $default_message;
    if (in_array($message, $old_default_messages, true)) {
        $message = $default_message;
    }

    $search  = ['{movie_title}', '{site_name}', '{expiration_date}', '{media_file}', '{movie_url}', '{rental_id}', '{slug}', '{duration_hours}'];
    $media_display_title = $media_title ?: $this->paytoviewlite_get_media_display_title($media_file);

    $replace = [esc_html($movie_title), esc_html($site_name), esc_html($expires_local), esc_html($media_display_title ?: $movie_title), esc_url($movie_url), esc_html($slug), esc_html($slug), esc_html($duration_text)];

    $subject = str_replace($search, $replace, $subject);
    $message = str_replace($search, $replace, $message);

    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: ' . $site_name . ' <' . $from_email . '>',
    ];

    if ( ! wp_mail($email, $subject, wpautop($message), $headers) ) {
        error_log("Pay-To-View Lite: Failed to send rental confirmation to $email");
    }

    // Filesystem
    if ( ! function_exists( 'request_filesystem_credentials' ) ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    WP_Filesystem();
    global $wp_filesystem;

    if ( ! $wp_filesystem ) {
        wp_send_json_error(__('Filesystem not available. Cannot log rental.', 'pay-to-view-lite'));
    }

    $log_file = $this->paytoviewlite_get_log_path();
    $log_dir  = dirname($log_file);

    if ( ! $wp_filesystem->is_dir($log_dir) ) {
        $wp_filesystem->mkdir($log_dir);
    }

    $log_handle = fopen($log_file, 'c+');
    if (!$log_handle) {
        wp_send_json_error(__('Could not open rental log file.', 'pay-to-view-lite'));
    }

    if (!flock($log_handle, LOCK_EX)) {
        fclose($log_handle);
        wp_send_json_error(__('Could not lock rental log file.', 'pay-to-view-lite'));
    }

    $existing = stream_get_contents($log_handle);
    if ($existing === false) {
        flock($log_handle, LOCK_UN);
        fclose($log_handle);
        wp_send_json_error(__('Could not read rental log file.', 'pay-to-view-lite'));
    }

    if (trim($existing) !== '') {
        $lines = preg_split('/\r\n|\r|\n/', trim($existing));
        $existing_headers = !empty($lines) ? str_getcsv($lines[0]) : [];
        $has_media_file_header = false;

        foreach ($existing_headers as $existing_header) {
            if (strcasecmp(trim($existing_header), 'Media File') === 0) {
                $has_media_file_header = true;
                break;
            }
        }

        if (!$has_media_file_header && !empty($existing_headers)) {
            $rental_id_index = 2;

            foreach ($existing_headers as $index => $existing_header) {
                $normalized_header = trim($existing_header);
                if (strcasecmp($normalized_header, 'Rental ID') === 0 || strcasecmp($normalized_header, 'Slug') === 0) {
                    $rental_id_index = $index;
                    break;
                }
            }

            $media_file_index = $rental_id_index + 1;
            array_splice($existing_headers, $media_file_index, 0, 'Media File');

            rewind($log_handle);
            ftruncate($log_handle, 0);
            fputcsv($log_handle, $existing_headers);

            foreach (array_slice($lines, 1) as $line) {
                if ($line === '') {
                    continue;
                }

                $row = str_getcsv($line);
                array_splice($row, $media_file_index, 0, '');
                fputcsv($log_handle, $row);
            }

            fflush($log_handle);
            rewind($log_handle);
            $existing = stream_get_contents($log_handle);
            if ($existing === false) {
                flock($log_handle, LOCK_UN);
                fclose($log_handle);
                wp_send_json_error(__('Could not read rental log file.', 'pay-to-view-lite'));
            }
        }
    }

    // Check for duplicate txn_id without rewriting the existing log.
    if (strpos($existing, $txn_id) !== false) {
        flock($log_handle, LOCK_UN);
        fclose($log_handle);
        wp_send_json_error(__('Transaction already logged.', 'pay-to-view-lite'));
    }

    $needs_header = trim($existing) === '';
    fseek($log_handle, 0, SEEK_END);

    if ($needs_header) {
        fputcsv($log_handle, ['Date', 'Email', 'Rental ID', 'Media File', 'TxnID', 'PaymentStatus', 'Expires', 'Amount']);
    }

    fputcsv($log_handle, [
        $nowUtc->format('Y-m-d H:i:s'),
        $email,
        $slug,
        $media_file,
        $txn_id,
        $paymentStatus,
        $expiresUtc->format('Y-m-d H:i:s'),
        number_format($amount, 2, '.', '')
    ]);

    fflush($log_handle);
    flock($log_handle, LOCK_UN);
    fclose($log_handle);
    @chmod($log_file, FS_CHMOD_FILE);

    // Instead of setting cookie on server, return cookie data for JS
    $cookie_payload = json_encode([
        'email'   => $email,
        'expires' => $expiresUtc->format(DateTime::ATOM)
    ]);

    wp_send_json_success([
        'message'       => __('Rental logged successfully', 'pay-to-view-lite'),
        'cookie_name'   => 'paytoview_rental_' . $slug,
        'cookie_value'  => $cookie_payload,
        'cookie_expiry' => $expiresUtc->getTimestamp(),
    ]);
}
}
new PayToViewLite();
