<?php
/**
 * Plugin Name: DABL Restaurant Reservations
 * Description: Restaurant reservations that work immediately with just email, phone number, and opening hours.
 * Version: 1.0.0
 * Author: DABL Software Solutions
 * Author URI: https://software.dabl.ca/
 * License: GPLv2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: dabl-restaurant-reservations
 * Domain Path: /languages
 */

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

final class Dabl_RR_Plugin {

    const VERSION = '1.0.0';
    const OPT     = 'dabl_rr_options';
    const DBV     = '1.0.0';
    const TABLE   = 'dabl_rr_reservations';

    public static function init() {
        register_activation_hook( __FILE__, array( __CLASS__, 'on_activate' ) );

        add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) );
        add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
        add_action( 'admin_notices', array( __CLASS__, 'maybe_admin_notice_setup' ) );

        add_action( 'wp_enqueue_scripts', array( __CLASS__, 'front_assets' ) );
        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_assets' ) );

        add_action( 'init', array( __CLASS__, 'register_shortcodes' ) );

        add_action( 'admin_post_nopriv_dabl_rr_book', array( __CLASS__, 'handle_booking' ) );
        add_action( 'admin_post_dabl_rr_book', array( __CLASS__, 'handle_booking' ) );

        add_action( 'admin_post_dabl_rr_create_pages', array( __CLASS__, 'handle_create_pages' ) );

        add_action( 'admin_post_dabl_rr_portal_update', array( __CLASS__, 'handle_portal_update' ) );
    }

    public static function defaults() {
        return array(
            'restaurant_name' => __( 'Your Restaurant', 'dabl-restaurant-reservations' ),

            // Layout (Free)
            'layout' => 'tall', // tall|wide

            // Booking behavior (Free: manual confirmation only)
            'auto_approve'             => 0,
            'require_phone'            => 1,
            'require_email'            => 1,
            'require_name'             => 1,
            'max_party_size'           => 30,
            'large_party_threshold'    => 999, // Free: disabled
            'duration_default_mins'    => 60,
            'duration_large_party_mins' => 60, // Free: same as default

            'min_notice_hours' => 0,
            'max_days_ahead'   => 90,
            'time_step_mins'   => 15,

            // Availability (Free: business hours only)
            'weekly_hours' => self::default_weekly_hours( '16:00', '22:00' ),

            // Design (Free: fixed black/white)
            'design' => array(
                'max_width'            => '380px',
                'max_height'           => 'none',
                'base_font_px'         => 16,
                'form_bg'              => '#FFFFFF',
                'form_text'            => '#000000',
                'form_border_px'       => 1,
                'field_border_px'      => 1,
                'field_border_color'   => '#000000',
                'border'               => '#000000',
                'button_bg'            => '#000000',
                'button_text'          => '#FFFFFF',
                'button_border_px'     => 1,
                'button_border_color'  => '#000000',
                'radius'               => 0,
                'field_radius'         => 0,
                'button_radius'        => 0,
                'calendar_bg'          => '#FFFFFF',
                'calendar_text'        => '#000000',
                'calendar_border_color' => '#000000',
                'calendar_border_px'   => 1,
                'calendar_radius'      => 0,
            ),

            // Emails (Free: restaurant notification only, fixed templates)
            'emails' => array(
                'from_name'             => '{restaurant}',
                'from_email'            => '',
                'to_restaurant_subject' => '{restaurant}: ' . __( 'New reservation request', 'dabl-restaurant-reservations' ),
                'to_restaurant_body'    => __( "New reservation request:\n\nName: {name}\nEmail: {email}\nPhone: {phone}\nDate: {date}\nTime: {time}\nParty size: {party}\nNotes: {notes}", 'dabl-restaurant-reservations' ),
                'to_customer_subject'   => __( 'Your reservation request at {restaurant}', 'dabl-restaurant-reservations' ),
                'to_customer_body'      => __( "Thank you for your reservation request.\n\nDate: {date}\nTime: {time}\nParty size: {party}\n\nWe will contact you to confirm.", 'dabl-restaurant-reservations' ),
                'to_customer_confirm_subject' => __( 'Your reservation is confirmed at {restaurant}', 'dabl-restaurant-reservations' ),
                'to_customer_confirm_body'    => __( "Your reservation has been confirmed.\n\nDate: {date}\nTime: {time}\nParty size: {party}\n\nWe look forward to seeing you!", 'dabl-restaurant-reservations' ),
                'to_customer_cancel_subject'  => __( 'Reservation cancelled at {restaurant}', 'dabl-restaurant-reservations' ),
                'to_customer_cancel_body'     => __( "Your reservation has been cancelled.\n\nDate: {date}\nTime: {time}\nParty size: {party}\n\nIf this was a mistake, please contact us.", 'dabl-restaurant-reservations' ),
                'footer'                => '',
            ),

            // Cancellation
            'cancellation_mode'  => 'phone',
            'cancellation_phone' => '',

            // Pages
            'pages_created' => 0,
        );
    }

    private static function default_weekly_hours( $open = '16:00', $close = '22:00' ) {
        $days = array( 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' );
        $out  = array();
        foreach ( $days as $d ) {
            $out[ $d ] = array(
                'open'   => $open,
                'close'  => $close,
                'closed' => 0,
            );
        }
        return $out;
    }

    public static function get_opts() {
        $d = self::defaults();
        $o = get_option( self::OPT, array() );
        if ( ! is_array( $o ) ) {
            $o = array();
        }
        $merged = self::merge_deep( $d, $o );
        return self::normalize_weekly_hours( $merged, $d );
    }

    private static function normalize_weekly_hours( $opts, $defaults ) {
        $valid_days = array( 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' );

        if ( empty( $opts['weekly_hours'] ) || ! is_array( $opts['weekly_hours'] ) ) {
            $opts['weekly_hours'] = $defaults['weekly_hours'];
        }

        foreach ( $valid_days as $d ) {
            if ( ! isset( $opts['weekly_hours'][ $d ] ) || ! is_array( $opts['weekly_hours'][ $d ] ) ) {
                $opts['weekly_hours'][ $d ] = $defaults['weekly_hours'][ $d ];
            }

            $h      = $opts['weekly_hours'][ $d ];
            $open   = isset( $h['open'] ) ? trim( (string) $h['open'] ) : $defaults['weekly_hours'][ $d ]['open'];
            $close  = isset( $h['close'] ) ? trim( (string) $h['close'] ) : $defaults['weekly_hours'][ $d ]['close'];
            $closed = ! empty( $h['closed'] ) ? 1 : 0;

            if ( ! preg_match( '/^\d{2}:\d{2}$/', $open ) ) {
                $open = $defaults['weekly_hours'][ $d ]['open'];
            }
            if ( ! preg_match( '/^\d{2}:\d{2}$/', $close ) ) {
                $close = $defaults['weekly_hours'][ $d ]['close'];
            }

            $opts['weekly_hours'][ $d ] = array(
                'open'   => $open,
                'close'  => $close,
                'closed' => $closed,
            );
        }

        // Free does not support date blocks or rule engines.
        $opts['layout'] = ( ( isset( $opts['layout'] ) ? $opts['layout'] : 'tall' ) === 'wide' ) ? 'wide' : 'tall';

        return $opts;
    }

    private static function merge_deep( $defaults, $incoming ) {
        foreach ( $incoming as $k => $v ) {
            if ( is_array( $v ) && isset( $defaults[ $k ] ) && is_array( $defaults[ $k ] ) ) {
                $defaults[ $k ] = self::merge_deep( $defaults[ $k ], $v );
            } else {
                $defaults[ $k ] = $v;
            }
        }
        return $defaults;
    }

    public static function on_activate() {
        self::create_roles();
        self::create_tables();
        if ( ! get_option( self::OPT ) ) {
            add_option( self::OPT, self::defaults() );
        }
        set_transient( 'dabl_rr_show_setup_notice', 1, DAY_IN_SECONDS );
    }

    public static function create_roles() {
        $role = get_role( 'dabl_rr_staff' );
        if ( ! $role ) {
            $role = add_role( 'dabl_rr_staff', __( 'Restaurant Staff', 'dabl-restaurant-reservations' ), array( 'read' => true ) );
        }
        if ( $role instanceof WP_Role ) {
            $role->add_cap( 'dabl_rr_view_portal', true );
        }
    }

    public static function create_tables() {
        global $wpdb;
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        $table   = $wpdb->prefix . self::TABLE;
        $charset = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            status VARCHAR(30) NOT NULL DEFAULT 'pending',
            name VARCHAR(200) NOT NULL,
            email VARCHAR(200) NOT NULL,
            phone VARCHAR(50) NOT NULL,
            party_size INT NOT NULL DEFAULT 2,
            res_date DATE NOT NULL,
            res_time TIME NOT NULL,
            notes TEXT NULL,
            table_label VARCHAR(200) NULL,
            duration_mins INT NOT NULL DEFAULT 60,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NULL,
            PRIMARY KEY  (id),
            KEY status (status),
            KEY res_date (res_date),
            KEY res_time (res_time)
        ) {$charset};";

        dbDelta( $sql );
        update_option( 'dabl_rr_dbv', self::DBV );
    }

    public static function admin_menu() {
        add_menu_page(
            __( 'Reservations', 'dabl-restaurant-reservations' ),
            __( 'Reservations', 'dabl-restaurant-reservations' ),
            'manage_options',
            'dabl-rr',
            array( __CLASS__, 'admin_page_dashboard' ),
            'dashicons-calendar-alt',
            56
        );

        add_submenu_page(
            'dabl-rr',
            __( 'Setup', 'dabl-restaurant-reservations' ),
            __( 'Setup', 'dabl-restaurant-reservations' ),
            'manage_options',
            'dabl-rr-setup',
            array( __CLASS__, 'admin_page_setup' )
        );
        add_submenu_page(
            'dabl-rr',
            __( 'Shortcodes', 'dabl-restaurant-reservations' ),
            __( 'Shortcodes', 'dabl-restaurant-reservations' ),
            'manage_options',
            'dabl-rr-shortcodes',
            array( __CLASS__, 'admin_page_shortcodes' )
        );

        // Settings sections (tabbed UI; keep only one menu item)
        add_submenu_page(
            'dabl-rr',
            __( 'Settings', 'dabl-restaurant-reservations' ),
            __( 'Settings', 'dabl-restaurant-reservations' ),
            'manage_options',
            'dabl-rr-settings-general',
            array( __CLASS__, 'admin_page_settings_general' )
        );

        // Hidden settings pages (accessible via tabs)
        add_submenu_page(
            null,
            __( 'Settings: Availability', 'dabl-restaurant-reservations' ),
            '',
            'manage_options',
            'dabl-rr-settings-availability',
            array( __CLASS__, 'admin_page_settings_availability' )
        );
        add_submenu_page(
            null,
            __( 'Settings: Design', 'dabl-restaurant-reservations' ),
            '',
            'manage_options',
            'dabl-rr-settings-design',
            array( __CLASS__, 'admin_page_settings_design' )
        );
        add_submenu_page(
            null,
            __( 'Settings: Emails', 'dabl-restaurant-reservations' ),
            '',
            'manage_options',
            'dabl-rr-settings-emails',
            array( __CLASS__, 'admin_page_settings_emails' )
        );
        add_submenu_page(
            null,
            __( 'Go Pro', 'dabl-restaurant-reservations' ),
            '',
            'manage_options',
            'dabl-rr-pro',
            array( __CLASS__, 'admin_page_pro' )
        );

        // Go Pro menu link
        add_submenu_page(
            'dabl-rr',
            __( 'Go Pro', 'dabl-restaurant-reservations' ),
            '<span style="color:#f59e0b;">&#9733; ' . __( 'Go Pro', 'dabl-restaurant-reservations' ) . '</span>',
            'manage_options',
            'dabl-rr-pro',
            array( __CLASS__, 'admin_page_pro' )
        );
    }

    public static function register_settings() {
        register_setting( 'dabl_rr_settings', self::OPT, array(
            'type'              => 'array',
            'sanitize_callback' => array( __CLASS__, 'sanitize_opts' ),
            'default'           => self::defaults(),
        ) );
    }

    private static function normalize_hex( $v ) {
        $v = strtoupper( trim( (string) $v ) );
        if ( $v === '' ) {
            return '';
        }
        if ( $v[0] !== '#' ) {
            $v = '#' . $v;
        }
        return preg_match( '/^#[0-9A-F]{6}$/', $v ) ? $v : '';
    }

    public static function sanitize_opts( $opts ) {
        $cur = self::get_opts();
        if ( ! is_array( $opts ) ) {
            return $cur;
        }

        // Simple fields
        if ( array_key_exists( 'restaurant_name', $opts ) ) {
            $cur['restaurant_name'] = sanitize_text_field( $opts['restaurant_name'] );
        }

        // General checkboxes
        foreach ( array( 'require_phone', 'require_email', 'require_name' ) as $k ) {
            if ( array_key_exists( $k, $opts ) ) {
                $cur[ $k ] = ! empty( $opts[ $k ] ) ? 1 : 0;
            }
        }

        // General numbers
        foreach ( array( 'max_party_size', 'duration_default_mins', 'min_notice_hours', 'max_days_ahead', 'time_step_mins', 'large_party_threshold', 'duration_large_party_mins' ) as $k ) {
            if ( isset( $opts[ $k ] ) ) {
                $cur[ $k ] = max( 0, intval( $opts[ $k ] ) );
            }
        }
        $cur['max_party_size'] = max( 1, min( 200, intval( $cur['max_party_size'] ) ) );
        $cur['time_step_mins'] = in_array( intval( $cur['time_step_mins'] ), array( 5, 10, 15, 20, 30, 60 ), true ) ? intval( $cur['time_step_mins'] ) : 15;

        // Layout (Free: 2 fixed options)
        if ( array_key_exists( 'layout', $opts ) ) {
            $layout         = sanitize_text_field( $opts['layout'] );
            $cur['layout'] = in_array( $layout, array( 'tall', 'wide' ), true ) ? $layout : 'tall';
        }

        // Cancellation
        if ( array_key_exists( 'cancellation_mode', $opts ) ) {
            $mode                     = sanitize_text_field( $opts['cancellation_mode'] );
            $cur['cancellation_mode'] = in_array( $mode, array( 'phone', 'email' ), true ) ? $mode : 'phone';
        }
        if ( array_key_exists( 'cancellation_phone', $opts ) ) {
            $cur['cancellation_phone'] = sanitize_text_field( $opts['cancellation_phone'] );
        }

        // Availability (Free: business hours only)
        $valid_days = array( 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' );
        if ( ! empty( $opts['weekly_hours_present'] ) && isset( $opts['weekly_hours'] ) && is_array( $opts['weekly_hours'] ) ) {
            $next = $cur['weekly_hours'];
            foreach ( $valid_days as $d ) {
                $row    = ( isset( $opts['weekly_hours'][ $d ] ) && is_array( $opts['weekly_hours'][ $d ] ) ) ? $opts['weekly_hours'][ $d ] : array();
                $open   = isset( $row['open'] ) ? trim( (string) $row['open'] ) : $next[ $d ]['open'];
                $close  = isset( $row['close'] ) ? trim( (string) $row['close'] ) : $next[ $d ]['close'];
                $closed = ! empty( $row['closed'] ) ? 1 : 0;

                if ( ! preg_match( '/^\d{2}:\d{2}$/', $open ) ) {
                    $open = $next[ $d ]['open'];
                }
                if ( ! preg_match( '/^\d{2}:\d{2}$/', $close ) ) {
                    $close = $next[ $d ]['close'];
                }

                $next[ $d ] = array(
                    'open'   => $open,
                    'close'  => $close,
                    'closed' => $closed,
                );
            }
            $cur['weekly_hours'] = $next;
        }

        // Emails (Free: editable with branded footer)
        if ( ! empty( $opts['emails_present'] ) && isset( $opts['emails'] ) && is_array( $opts['emails'] ) ) {
            $e = $opts['emails'];
            if ( isset( $e['from_name'] ) ) {
                $cur['emails']['from_name'] = sanitize_text_field( $e['from_name'] );
            }
            if ( isset( $e['from_email'] ) ) {
                $fe = sanitize_email( $e['from_email'] );
                $cur['emails']['from_email'] = is_email( $fe ) ? $fe : '';
            }
            if ( isset( $e['to_restaurant_subject'] ) ) {
                $cur['emails']['to_restaurant_subject'] = sanitize_text_field( $e['to_restaurant_subject'] );
            }
            if ( isset( $e['to_restaurant_body'] ) ) {
                $cur['emails']['to_restaurant_body'] = sanitize_textarea_field( $e['to_restaurant_body'] );
            }
            if ( isset( $e['to_customer_subject'] ) ) {
                $cur['emails']['to_customer_subject'] = sanitize_text_field( $e['to_customer_subject'] );
            }
            if ( isset( $e['to_customer_body'] ) ) {
                $cur['emails']['to_customer_body'] = sanitize_textarea_field( $e['to_customer_body'] );
            }
            if ( isset( $e['to_customer_confirm_subject'] ) ) {
                $cur['emails']['to_customer_confirm_subject'] = sanitize_text_field( $e['to_customer_confirm_subject'] );
            }
            if ( isset( $e['to_customer_confirm_body'] ) ) {
                $cur['emails']['to_customer_confirm_body'] = sanitize_textarea_field( $e['to_customer_confirm_body'] );
            }
            if ( isset( $e['to_customer_cancel_subject'] ) ) {
                $cur['emails']['to_customer_cancel_subject'] = sanitize_text_field( $e['to_customer_cancel_subject'] );
            }
            if ( isset( $e['to_customer_cancel_body'] ) ) {
                $cur['emails']['to_customer_cancel_body'] = sanitize_textarea_field( $e['to_customer_cancel_body'] );
            }
        }

        return $cur;
    }

    public static function admin_assets( $hook ) {
        if ( strpos( $hook, 'dabl-rr' ) === false ) {
            return;
        }

        wp_enqueue_style( 'dabl-rr-admin', plugins_url( 'assets/admin.css', __FILE__ ), array(), self::DBV );
        wp_enqueue_script( 'dabl-rr-admin', plugins_url( 'assets/admin.js', __FILE__ ), array( 'jquery' ), self::DBV, true );
        wp_enqueue_media();

        // Load the front-end assets for consistent styling of portal & previews in admin.
        self::front_assets();
    }

    public static function front_assets() {
        wp_enqueue_style( 'dabl-rr-front', plugins_url( 'assets/front.css', __FILE__ ), array(), self::DBV );
        wp_enqueue_script( 'dabl-rr-front-js', plugins_url( 'assets/front.js', __FILE__ ), array( 'jquery', 'jquery-ui-datepicker' ), self::DBV, true );

        $opts   = self::get_opts();
        $layout = isset( $opts['layout'] ) ? sanitize_text_field( $opts['layout'] ) : 'tall';
        $maxw   = ( $layout === 'wide' ) ? '760px' : '380px';
        $css    = '.dabl-rr-form-wrap{'
            . "--dabl-rr-max-width: {$maxw};"
            . '--dabl-rr-max-height: none;'
            . '--dabl-rr-font: 16px;'
            . '--dabl-rr-bg: #FFFFFF;'
            . '--dabl-rr-text: #000000;'
            . '--dabl-rr-border: #000000;'
            . '--dabl-rr-form-border-width: 1px;'
            . '--dabl-rr-field-border-width: 1px;'
            . '--dabl-rr-field-border: #000000;'
            . '--dabl-rr-btn: #000000;'
            . '--dabl-rr-btn-text: #FFFFFF;'
            . '--dabl-rr-btn-border-width: 0px;'
            . '--dabl-rr-btn-border: #000000;'
            . '--dabl-rr-radius: 0px;'
            . '--dabl-rr-field-radius: 0px;'
            . '--dabl-rr-btn-radius: 0px;'
            . '--dabl-rr-cal-bg: #FFFFFF;'
            . '--dabl-rr-cal-text: #000000;'
            . '--dabl-rr-cal-border: #000000;'
            . '--dabl-rr-cal-border-width: 1px;'
            . '--dabl-rr-cal-radius: 0px;'
            . '}';
        wp_add_inline_style( 'dabl-rr-front', $css );
    }

    public static function maybe_admin_notice_setup() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        if ( ! get_transient( 'dabl_rr_show_setup_notice' ) ) {
            return;
        }
        delete_transient( 'dabl_rr_show_setup_notice' );
        $url = admin_url( 'admin.php?page=dabl-rr-setup' );
        printf(
            '<div class="notice notice-info is-dismissible"><p><strong>%s</strong>: %s <a href="%s">%s</a>.</p></div>',
            esc_html__( 'DABL Restaurant Reservations', 'dabl-restaurant-reservations' ),
            esc_html__( 'set up pages & shortcodes in', 'dabl-restaurant-reservations' ),
            esc_url( $url ),
            esc_html__( 'Setup', 'dabl-restaurant-reservations' )
        );
    }

    private static function settings_tabs( $active ) {
        $tabs = array(
            'dabl-rr-settings-general'      => esc_html__( 'General', 'dabl-restaurant-reservations' ),
            'dabl-rr-settings-availability' => esc_html__( 'Availability', 'dabl-restaurant-reservations' ),
            'dabl-rr-settings-design'       => esc_html__( 'Design', 'dabl-restaurant-reservations' ) . ' <span class="dabl-rr-pro-badge">PRO</span>',
            'dabl-rr-settings-emails'       => esc_html__( 'Emails', 'dabl-restaurant-reservations' ),
            'dabl-rr-pro'                   => '<span style="color:#f59e0b;">&#9733; ' . esc_html__( 'Pro', 'dabl-restaurant-reservations' ) . '</span>',
        );
        echo '<h2 class="nav-tab-wrapper">';
        foreach ( $tabs as $slug => $label ) {
            $cls = ( $slug === $active ) ? 'nav-tab nav-tab-active' : 'nav-tab';
            printf(
                '<a class="%s" href="%s">%s</a>',
                esc_attr( $cls ),
                esc_url( admin_url( 'admin.php?page=' . $slug ) ),
                wp_kses_post( $label )
            );
        }
        echo '</h2>';
    }

    // ---- Admin Pages ----

    public static function admin_page_dashboard() {
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Reservations', 'dabl-restaurant-reservations' ); ?></h1>
            <div class="dabl-rr-card">
                <p>
                    <a class="button button-primary" href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-setup' ) ); ?>">
                        <?php esc_html_e( 'Setup', 'dabl-restaurant-reservations' ); ?>
                    </a>
                    <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-settings-general' ) ); ?>">
                        <?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?>
                    </a>
                    <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-shortcodes' ) ); ?>">
                        <?php esc_html_e( 'Shortcodes', 'dabl-restaurant-reservations' ); ?>
                    </a>
                </p>
            </div>

            <?php echo self::sc_staff_portal(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
        </div>
        <?php
    }

    public static function admin_page_setup() {
        $pages  = self::recommended_pages();
        $status = array();
        foreach ( $pages as $slug => $p ) {
            $status[ $slug ] = (bool) get_page_by_path( $slug );
        }
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Setup', 'dabl-restaurant-reservations' ); ?></h1>
            <?php
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only, nonce checked on form submission
            if ( isset( $_GET['created'] ) ) :
                ?>
                <div class="notice notice-success">
                    <p><?php esc_html_e( 'Pages created (missing pages only).', 'dabl-restaurant-reservations' ); ?></p>
                </div>
            <?php endif; ?>

            <div class="dabl-rr-card">
                <h2><?php esc_html_e( 'Create Pages (Optional)', 'dabl-restaurant-reservations' ); ?></h2>
                <p class="description">
                    <?php esc_html_e( 'Creates normal WordPress pages (works with any theme/builder). It does not create Divi modules.', 'dabl-restaurant-reservations' ); ?>
                </p>
                <table class="widefat striped">
                    <thead>
                        <tr>
                            <th><?php esc_html_e( 'Page', 'dabl-restaurant-reservations' ); ?></th>
                            <th><?php esc_html_e( 'Slug', 'dabl-restaurant-reservations' ); ?></th>
                            <th><?php esc_html_e( 'Status', 'dabl-restaurant-reservations' ); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ( $pages as $slug => $p ) : ?>
                            <tr>
                                <td><strong><?php echo esc_html( $p['title'] ); ?></strong></td>
                                <td><code><?php echo esc_html( $slug ); ?></code></td>
                                <td>
                                    <?php if ( $status[ $slug ] ) : ?>
                                        <span class="dabl-rr-pill ok"><?php esc_html_e( 'Exists', 'dabl-restaurant-reservations' ); ?></span>
                                    <?php else : ?>
                                        <span class="dabl-rr-pill"><?php esc_html_e( 'Missing', 'dabl-restaurant-reservations' ); ?></span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
                <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="margin-top:12px;">
                    <input type="hidden" name="action" value="dabl_rr_create_pages">
                    <?php wp_nonce_field( 'dabl_rr_create_pages', 'dabl_rr_nonce' ); ?>
                    <button class="button button-primary" type="submit">
                        <?php esc_html_e( 'Create Missing Pages Now', 'dabl-restaurant-reservations' ); ?>
                    </button>
                    <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-shortcodes' ) ); ?>">
                        <?php esc_html_e( 'View Shortcodes', 'dabl-restaurant-reservations' ); ?>
                    </a>
                </form>
            </div>
        </div>
        <?php
    }

    public static function admin_page_shortcodes() {
        $items = array(
            array(
                __( 'Reservations (Public Form)', 'dabl-restaurant-reservations' ),
                '[dabl_rr_form]',
            ),
            array(
                __( 'Staff Login', 'dabl-restaurant-reservations' ),
                '[dabl_rr_staff_login]',
            ),
            array(
                __( 'Staff Portal (List View)', 'dabl-restaurant-reservations' ),
                '[dabl_rr_portal]',
            ),
            array(
                __( 'Admin Calendar (Month View)', 'dabl-restaurant-reservations' ),
                '[dabl_rr_admin_calendar]',
            ),
        );
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Shortcodes', 'dabl-restaurant-reservations' ); ?></h1>
            <div class="dabl-rr-card">
                <table class="widefat striped">
                    <thead>
                        <tr>
                            <th><?php esc_html_e( 'Use', 'dabl-restaurant-reservations' ); ?></th>
                            <th><?php esc_html_e( 'Shortcode', 'dabl-restaurant-reservations' ); ?></th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ( $items as $row ) : ?>
                            <tr>
                                <td><strong><?php echo esc_html( $row[0] ); ?></strong></td>
                                <td><code class="dabl-rr-code"><?php echo esc_html( $row[1] ); ?></code></td>
                                <td>
                                    <button type="button" class="button dabl-rr-copy" data-copy="<?php echo esc_attr( $row[1] ); ?>">
                                        <?php esc_html_e( 'Copy', 'dabl-restaurant-reservations' ); ?>
                                    </button>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php
    }

    public static function admin_page_settings_general() {
        $o = self::get_opts();
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?></h1>
            <?php self::settings_tabs( 'dabl-rr-settings-general' ); ?>
            <form method="post" action="options.php">
                <?php settings_fields( 'dabl_rr_settings' ); ?>
                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Restaurant', 'dabl-restaurant-reservations' ); ?></h2>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Restaurant name', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="regular-text" name="<?php echo esc_attr( self::OPT ); ?>[restaurant_name]"
                                    value="<?php echo esc_attr( $o['restaurant_name'] ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Form layout', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <select name="<?php echo esc_attr( self::OPT ); ?>[layout]">
                                    <option value="tall" <?php selected( ( isset( $o['layout'] ) ? $o['layout'] : 'tall' ), 'tall' ); ?>>
                                        <?php esc_html_e( 'Tall & skinny', 'dabl-restaurant-reservations' ); ?>
                                    </option>
                                    <option value="wide" <?php selected( ( isset( $o['layout'] ) ? $o['layout'] : 'tall' ), 'wide' ); ?>>
                                        <?php esc_html_e( 'Short & wide', 'dabl-restaurant-reservations' ); ?>
                                    </option>
                                </select>
                                <p class="description">
                                    <?php esc_html_e( 'Free includes two fixed black & white layouts.', 'dabl-restaurant-reservations' ); ?>
                                </p>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Booking rules', 'dabl-restaurant-reservations' ); ?></h2>
                    <table class="form-table">
                        <tr>
                            <th scope="row">
                                <?php esc_html_e( 'Auto-approve', 'dabl-restaurant-reservations' ); ?>
                                <span class="dabl-rr-pro-badge">PRO</span>
                            </th>
                            <td>
                                <label style="opacity:0.6;">
                                    <input type="checkbox" disabled />
                                    <?php esc_html_e( 'Send confirmation immediately', 'dabl-restaurant-reservations' ); ?>
                                </label>
                                <p class="description">
                                    <a href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-pro' ) ); ?>"><?php esc_html_e( 'Upgrade to Pro', 'dabl-restaurant-reservations' ); ?></a>
                                    <?php esc_html_e( 'to auto-approve reservations without manual review.', 'dabl-restaurant-reservations' ); ?>
                                </p>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Max party size', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="1" max="200" name="<?php echo esc_attr( self::OPT ); ?>[max_party_size]"
                                    value="<?php echo esc_attr( $o['max_party_size'] ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Time step', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <select name="<?php echo esc_attr( self::OPT ); ?>[time_step_mins]">
                                    <?php foreach ( array( 5, 10, 15, 20, 30, 60 ) as $v ) : ?>
                                        <option value="<?php echo esc_attr( $v ); ?>" <?php selected( intval( $o['time_step_mins'] ), $v ); ?>>
                                            <?php
                                            /* translators: %d: number of minutes */
                                            printf( esc_html__( '%d minutes', 'dabl-restaurant-reservations' ), intval( $v ) );
                                            ?>
                                        </option>
                                    <?php endforeach; ?>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Minimum notice', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="0" max="720" name="<?php echo esc_attr( self::OPT ); ?>[min_notice_hours]"
                                    value="<?php echo esc_attr( $o['min_notice_hours'] ); ?>" />
                                <?php esc_html_e( 'hours', 'dabl-restaurant-reservations' ); ?>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Max days ahead', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="1" max="365" name="<?php echo esc_attr( self::OPT ); ?>[max_days_ahead]"
                                    value="<?php echo esc_attr( $o['max_days_ahead'] ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Default duration', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="15" max="360" name="<?php echo esc_attr( self::OPT ); ?>[duration_default_mins]"
                                    value="<?php echo esc_attr( $o['duration_default_mins'] ); ?>" />
                                <?php esc_html_e( 'minutes', 'dabl-restaurant-reservations' ); ?>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Large party threshold', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="2" max="50" name="<?php echo esc_attr( self::OPT ); ?>[large_party_threshold]"
                                    value="<?php echo esc_attr( $o['large_party_threshold'] ); ?>" />
                                <?php esc_html_e( 'people', 'dabl-restaurant-reservations' ); ?>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Large party duration', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="number" min="15" max="480" name="<?php echo esc_attr( self::OPT ); ?>[duration_large_party_mins]"
                                    value="<?php echo esc_attr( $o['duration_large_party_mins'] ); ?>" />
                                <?php esc_html_e( 'minutes', 'dabl-restaurant-reservations' ); ?>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Required fields', 'dabl-restaurant-reservations' ); ?></h2>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Name', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[require_name]" value="0" />
                                <label>
                                    <input type="checkbox" name="<?php echo esc_attr( self::OPT ); ?>[require_name]" value="1"
                                        <?php checked( $o['require_name'], 1 ); ?> />
                                    <?php esc_html_e( 'Required', 'dabl-restaurant-reservations' ); ?>
                                </label>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Email', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[require_email]" value="0" />
                                <label>
                                    <input type="checkbox" name="<?php echo esc_attr( self::OPT ); ?>[require_email]" value="1"
                                        <?php checked( $o['require_email'], 1 ); ?> />
                                    <?php esc_html_e( 'Required', 'dabl-restaurant-reservations' ); ?>
                                </label>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Phone', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[require_phone]" value="0" />
                                <label>
                                    <input type="checkbox" name="<?php echo esc_attr( self::OPT ); ?>[require_phone]" value="1"
                                        <?php checked( $o['require_phone'], 1 ); ?> />
                                    <?php esc_html_e( 'Required', 'dabl-restaurant-reservations' ); ?>
                                </label>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Cancellation', 'dabl-restaurant-reservations' ); ?></h2>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'How customers cancel', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <select name="<?php echo esc_attr( self::OPT ); ?>[cancellation_mode]">
                                    <option value="phone" <?php selected( isset( $o['cancellation_mode'] ) ? $o['cancellation_mode'] : 'phone', 'phone' ); ?>>
                                        <?php esc_html_e( 'Phone call required', 'dabl-restaurant-reservations' ); ?>
                                    </option>
                                    <option value="email" <?php selected( isset( $o['cancellation_mode'] ) ? $o['cancellation_mode'] : 'phone', 'email' ); ?>>
                                        <?php esc_html_e( 'Email link allowed', 'dabl-restaurant-reservations' ); ?>
                                    </option>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Phone number', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="regular-text" name="<?php echo esc_attr( self::OPT ); ?>[cancellation_phone]"
                                    value="<?php echo esc_attr( isset( $o['cancellation_phone'] ) ? $o['cancellation_phone'] : '' ); ?>" />
                            </td>
                        </tr>
                    </table>
                </div>

                <?php submit_button( __( 'Save', 'dabl-restaurant-reservations' ) ); ?>
            </form>
        </div>
        <?php
    }

    public static function admin_page_settings_availability() {
        $o    = self::get_opts();
        $days = array(
            'mon' => __( 'Monday', 'dabl-restaurant-reservations' ),
            'tue' => __( 'Tuesday', 'dabl-restaurant-reservations' ),
            'wed' => __( 'Wednesday', 'dabl-restaurant-reservations' ),
            'thu' => __( 'Thursday', 'dabl-restaurant-reservations' ),
            'fri' => __( 'Friday', 'dabl-restaurant-reservations' ),
            'sat' => __( 'Saturday', 'dabl-restaurant-reservations' ),
            'sun' => __( 'Sunday', 'dabl-restaurant-reservations' ),
        );
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?></h1>
            <?php self::settings_tabs( 'dabl-rr-settings-availability' ); ?>
            <form method="post" action="options.php">
                <?php settings_fields( 'dabl_rr_settings' ); ?>
                <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[weekly_hours_present]" value="1" />

                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Hours of operation', 'dabl-restaurant-reservations' ); ?></h2>
                    <table class="widefat striped" style="max-width:920px;">
                        <thead>
                            <tr>
                                <th style="width:180px;"><?php esc_html_e( 'Day', 'dabl-restaurant-reservations' ); ?></th>
                                <th style="width:120px;"><?php esc_html_e( 'Closed', 'dabl-restaurant-reservations' ); ?></th>
                                <th style="width:180px;"><?php esc_html_e( 'Open', 'dabl-restaurant-reservations' ); ?></th>
                                <th style="width:180px;"><?php esc_html_e( 'Close', 'dabl-restaurant-reservations' ); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            foreach ( $days as $k => $label ) :
                                $h = isset( $o['weekly_hours'][ $k ] ) ? $o['weekly_hours'][ $k ] : array(
                                    'open'   => '16:00',
                                    'close'  => '22:00',
                                    'closed' => 0,
                                );
                                ?>
                                <tr>
                                    <td><strong><?php echo esc_html( $label ); ?></strong></td>
                                    <td>
                                        <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[weekly_hours][<?php echo esc_attr( $k ); ?>][closed]" value="0" />
                                        <label>
                                            <input type="checkbox" class="dabl-rr-day-closed" name="<?php echo esc_attr( self::OPT ); ?>[weekly_hours][<?php echo esc_attr( $k ); ?>][closed]" value="1" <?php checked( ! empty( $h['closed'] ) ); ?> />
                                            <?php esc_html_e( 'Closed', 'dabl-restaurant-reservations' ); ?>
                                        </label>
                                    </td>
                                    <td>
                                        <input type="time" class="dabl-rr-day-open" name="<?php echo esc_attr( self::OPT ); ?>[weekly_hours][<?php echo esc_attr( $k ); ?>][open]" value="<?php echo esc_attr( $h['open'] ); ?>" />
                                    </td>
                                    <td>
                                        <input type="time" class="dabl-rr-day-close" name="<?php echo esc_attr( self::OPT ); ?>[weekly_hours][<?php echo esc_attr( $k ); ?>][close]" value="<?php echo esc_attr( $h['close'] ); ?>" />
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>

                <?php submit_button( __( 'Save', 'dabl-restaurant-reservations' ) ); ?>
            </form>

            <div class="dabl-rr-card" style="opacity:0.7;position:relative;">
                <div style="position:absolute;top:10px;right:10px;background:#f59e0b;color:#000;padding:4px 12px;border-radius:4px;font-size:12px;font-weight:bold;">
                    <?php esc_html_e( 'PRO FEATURE', 'dabl-restaurant-reservations' ); ?>
                </div>
                <h2><?php esc_html_e( 'Block Reservations', 'dabl-restaurant-reservations' ); ?> <span class="dabl-rr-pro-badge">PRO</span></h2>
                <p class="description">
                    <?php esc_html_e( 'Block full days or specific time ranges (holidays, private events, etc.).', 'dabl-restaurant-reservations' ); ?>
                    <a href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-pro' ) ); ?>"><?php esc_html_e( 'Upgrade to Pro', 'dabl-restaurant-reservations' ); ?></a>
                </p>

                <div style="pointer-events:none;">
                    <div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:15px;">
                        <div>
                            <label><?php esc_html_e( 'Start date', 'dabl-restaurant-reservations' ); ?></label><br>
                            <input type="date" disabled style="width:100%;" />
                        </div>
                        <div>
                            <label><?php esc_html_e( 'End date', 'dabl-restaurant-reservations' ); ?></label><br>
                            <input type="date" disabled style="width:100%;" />
                        </div>
                        <div>
                            <label><?php esc_html_e( 'Type', 'dabl-restaurant-reservations' ); ?></label><br>
                            <select disabled style="width:100%;">
                                <option><?php esc_html_e( 'Full day', 'dabl-restaurant-reservations' ); ?></option>
                                <option><?php esc_html_e( 'Time range', 'dabl-restaurant-reservations' ); ?></option>
                            </select>
                        </div>
                        <div>
                            <label><?php esc_html_e( 'From', 'dabl-restaurant-reservations' ); ?></label><br>
                            <input type="time" disabled value="16:00" style="width:100%;" />
                        </div>
                        <div>
                            <label><?php esc_html_e( 'To', 'dabl-restaurant-reservations' ); ?></label><br>
                            <input type="time" disabled value="22:00" style="width:100%;" />
                        </div>
                    </div>
                    <p style="margin-top:10px;">
                        <button type="button" class="button" disabled><?php esc_html_e( 'Add block', 'dabl-restaurant-reservations' ); ?></button>
                    </p>
                </div>
            </div>
        </div>
        <?php
    }

    public static function admin_page_settings_emails() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        $o = self::get_opts();
        $e = isset( $o['emails'] ) ? $o['emails'] : array();
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?></h1>
            <?php self::settings_tabs( 'dabl-rr-settings-emails' ); ?>

            <div class="notice notice-info" style="margin:15px 0;padding:12px 15px;">
                <p style="margin:0 0 8px;"><strong><?php esc_html_e( 'THIS IS IMPORTANT', 'dabl-restaurant-reservations' ); ?></strong></p>
                <p style="margin:0 0 8px;"><?php esc_html_e( 'This email address is where new reservation requests are sent.', 'dabl-restaurant-reservations' ); ?></p>
                <p style="margin:0;"><?php esc_html_e( 'If you leave it blank, reservations will be sent to the site\'s main admin email instead.', 'dabl-restaurant-reservations' ); ?></p>
            </div>

            <form method="post" action="options.php">
                <?php settings_fields( 'dabl_rr_settings' ); ?>
                <input type="hidden" name="<?php echo esc_attr( self::OPT ); ?>[emails_present]" value="1" />

                <div class="dabl-rr-card">
                    <h2><?php esc_html_e( 'Email templates', 'dabl-restaurant-reservations' ); ?></h2>
                    <p class="description">
                        <?php esc_html_e( 'Available tags:', 'dabl-restaurant-reservations' ); ?>
                        <code>{restaurant}</code> <code>{name}</code> <code>{email}</code> <code>{phone}</code> <code>{date}</code> <code>{time}</code> <code>{party}</code> <code>{notes}</code>
                    </p>

                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'From name', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="regular-text" name="<?php echo esc_attr( self::OPT ); ?>[emails][from_name]"
                                    value="<?php echo esc_attr( isset( $e['from_name'] ) ? $e['from_name'] : '{restaurant}' ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'From email', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="regular-text" type="email" name="<?php echo esc_attr( self::OPT ); ?>[emails][from_email]"
                                    value="<?php echo esc_attr( isset( $e['from_email'] ) ? $e['from_email'] : '' ); ?>"
                                    placeholder="<?php echo esc_attr( get_option( 'admin_email' ) ); ?>" />
                                <p class="description"><?php esc_html_e( 'Leave blank to use admin email.', 'dabl-restaurant-reservations' ); ?></p>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h3><?php esc_html_e( 'To Restaurant (Admin Notification)', 'dabl-restaurant-reservations' ); ?></h3>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Subject', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="large-text" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_restaurant_subject]"
                                    value="<?php echo esc_attr( isset( $e['to_restaurant_subject'] ) ? $e['to_restaurant_subject'] : '' ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Body', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <textarea class="large-text" rows="6" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_restaurant_body]"><?php echo esc_textarea( isset( $e['to_restaurant_body'] ) ? $e['to_restaurant_body'] : '' ); ?></textarea>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h3><?php esc_html_e( 'To Customer (Request Received)', 'dabl-restaurant-reservations' ); ?></h3>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Subject', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="large-text" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_subject]"
                                    value="<?php echo esc_attr( isset( $e['to_customer_subject'] ) ? $e['to_customer_subject'] : '' ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Body', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <textarea class="large-text" rows="6" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_body]"><?php echo esc_textarea( isset( $e['to_customer_body'] ) ? $e['to_customer_body'] : '' ); ?></textarea>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h3><?php esc_html_e( 'To Customer (Confirmed)', 'dabl-restaurant-reservations' ); ?></h3>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Subject', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="large-text" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_confirm_subject]"
                                    value="<?php echo esc_attr( isset( $e['to_customer_confirm_subject'] ) ? $e['to_customer_confirm_subject'] : '' ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Body', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <textarea class="large-text" rows="6" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_confirm_body]"><?php echo esc_textarea( isset( $e['to_customer_confirm_body'] ) ? $e['to_customer_confirm_body'] : '' ); ?></textarea>
                            </td>
                        </tr>
                    </table>
                </div>

                <div class="dabl-rr-card">
                    <h3><?php esc_html_e( 'To Customer (Cancelled)', 'dabl-restaurant-reservations' ); ?></h3>
                    <table class="form-table">
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Subject', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <input class="large-text" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_cancel_subject]"
                                    value="<?php echo esc_attr( isset( $e['to_customer_cancel_subject'] ) ? $e['to_customer_cancel_subject'] : '' ); ?>" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row"><?php esc_html_e( 'Body', 'dabl-restaurant-reservations' ); ?></th>
                            <td>
                                <textarea class="large-text" rows="6" name="<?php echo esc_attr( self::OPT ); ?>[emails][to_customer_cancel_body]"><?php echo esc_textarea( isset( $e['to_customer_cancel_body'] ) ? $e['to_customer_cancel_body'] : '' ); ?></textarea>
                            </td>
                        </tr>
                    </table>
                </div>

                <?php submit_button( __( 'Save', 'dabl-restaurant-reservations' ) ); ?>
            </form>
        </div>
        <?php
    }

    public static function admin_page_settings_design() {
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?></h1>
            <?php self::settings_tabs( 'dabl-rr-settings-design' ); ?>

            <div class="dabl-rr-card dabl-rr-pro-locked" style="background:linear-gradient(135deg,#fef3c7 0%,#fde68a 100%);border:2px solid #f59e0b;text-align:center;padding:40px;">
                <h2 style="color:#92400e;margin-top:0;">
                    <span style="font-size:32px;">&#127912;</span><br>
                    <?php esc_html_e( 'Custom Design is a Pro Feature', 'dabl-restaurant-reservations' ); ?>
                </h2>
                <p style="font-size:16px;color:#78350f;max-width:500px;margin:0 auto 20px;">
                    <?php esc_html_e( 'Unlock full control over colors, gradients, borders, radius, typography, and more to match your restaurant\'s brand.', 'dabl-restaurant-reservations' ); ?>
                </p>
                <a href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr-pro' ) ); ?>" class="button button-primary button-hero" style="background:#f59e0b;border-color:#d97706;font-size:16px;">
                    <?php esc_html_e( 'Upgrade to Pro', 'dabl-restaurant-reservations' ); ?>
                </a>
            </div>

            <div class="dabl-rr-card" style="opacity:0.6;pointer-events:none;position:relative;">
                <div style="position:absolute;top:10px;right:10px;background:#ef4444;color:#fff;padding:4px 12px;border-radius:4px;font-size:12px;font-weight:bold;">
                    <?php esc_html_e( 'PRO ONLY', 'dabl-restaurant-reservations' ); ?>
                </div>
                <h2><?php esc_html_e( 'Form Design Preview', 'dabl-restaurant-reservations' ); ?></h2>
                <table class="form-table">
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Max width / height', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input class="regular-text" disabled value="520px" /> <input class="regular-text" disabled value="none" /></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Form background', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="color" disabled value="#FFFFFF" /> <input class="regular-text" disabled value="#FFFFFF" /></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Form text color', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="color" disabled value="#111827" /> <input class="regular-text" disabled value="#111827" /></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Button background', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="color" disabled value="#111827" /> <input class="regular-text" disabled value="#111827" /></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Button text color', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="color" disabled value="#FFFFFF" /> <input class="regular-text" disabled value="#FFFFFF" /></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Border radius', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="range" disabled min="0" max="50" value="14" /> <span>14px</span></td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e( 'Gradient background', 'dabl-restaurant-reservations' ); ?></th>
                        <td><input type="checkbox" disabled /> <?php esc_html_e( 'Enable gradient', 'dabl-restaurant-reservations' ); ?></td>
                    </tr>
                </table>
            </div>
        </div>
        <?php
    }

    public static function admin_page_pro() {
        $pro_url = 'https://software.dabl.ca/restaurant-reservation-wordpress-plugin/';

        // Pro promo image (fails silently if file does not exist)
        $promo_image_path = plugin_dir_path( __FILE__ ) . 'images/pro-promo.png';
        $promo_image_url  = plugins_url( 'images/pro-promo.png', __FILE__ );
        ?>
        <div class="wrap dabl-rr-wrap">
            <h1><?php esc_html_e( 'Settings', 'dabl-restaurant-reservations' ); ?></h1>
            <?php self::settings_tabs( 'dabl-rr-pro' ); ?>

            <?php if ( file_exists( $promo_image_path ) ) : ?>
                <div style="margin-bottom:20px;text-align:center;">
                    <img
                        src="<?php echo esc_url( $promo_image_url ); ?>"
                        alt="<?php esc_attr_e( 'DABL Restaurant Reservations Pro – promotional image', 'dabl-restaurant-reservations' ); ?>"
                        style="max-width:100%;width:100%;max-width:900px;height:auto;border-radius:12px;"
                    />
                </div>
            <?php endif; ?>

            <div style="text-align:center;margin-top:20px;">
                <a href="<?php echo esc_url( $pro_url ); ?>" target="_blank" class="button button-hero" style="background:#f59e0b;border:none;color:#000;font-size:18px;padding:12px 40px;font-weight:bold;">
                    <?php esc_html_e( 'Get Pro Now', 'dabl-restaurant-reservations' ); ?> &rarr;
                </a>
            </div>
        </div>
        <?php
    }

    // ---- Page creation ----
    private static function recommended_pages() {
        return array(
            'dabl-rr-reservations'     => array(
                'title'   => __( 'Reservations', 'dabl-restaurant-reservations' ),
                'content' => '[dabl_rr_form]',
            ),
            'dabl-rr-staff-login'      => array(
                'title'   => __( 'Staff Login', 'dabl-restaurant-reservations' ),
                'content' => '[dabl_rr_staff_login]',
            ),
            'dabl-rr-staff'            => array(
                'title'   => __( 'Reservations (Staff)', 'dabl-restaurant-reservations' ),
                'content' => '[dabl_rr_portal]',
            ),
            'dabl-rr-admin-calendar'   => array(
                'title'   => __( 'Reservations Calendar (Admin)', 'dabl-restaurant-reservations' ),
                'content' => '[dabl_rr_admin_calendar]',
            ),
        );
    }

    public static function handle_create_pages() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'Not allowed', 'dabl-restaurant-reservations' ) );
        }

        if ( ! isset( $_POST['dabl_rr_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['dabl_rr_nonce'] ) ), 'dabl_rr_create_pages' ) ) {
            wp_die( esc_html__( 'Security check failed', 'dabl-restaurant-reservations' ) );
        }

        foreach ( self::recommended_pages() as $slug => $p ) {
            if ( get_page_by_path( $slug ) ) {
                continue;
            }
            wp_insert_post( array(
                'post_title'   => $p['title'],
                'post_name'    => $slug,
                'post_type'    => 'page',
                'post_status'  => 'publish',
                'post_content' => $p['content'],
            ) );
        }

        $o                   = self::get_opts();
        $o['pages_created'] = 1;
        update_option( self::OPT, $o );

        wp_safe_redirect( admin_url( 'admin.php?page=dabl-rr-setup&created=1' ) );
        exit;
    }

    // ---- Shortcodes ----
    public static function register_shortcodes() {
        add_shortcode( 'dabl_rr_form', array( __CLASS__, 'sc_form' ) );
        add_shortcode( 'dabl_rr_staff_login', array( __CLASS__, 'sc_staff_login' ) );
        add_shortcode( 'dabl_rr_portal', array( __CLASS__, 'sc_staff_portal' ) );
        add_shortcode( 'dabl_rr_admin_calendar', array( __CLASS__, 'sc_admin_calendar' ) );
    }

    // ---- Helper functions ----

    private static function is_valid_ymd( $s ) {
        if ( ! is_string( $s ) ) {
            return false;
        }
        $s = trim( $s );
        if ( ! preg_match( '/^\d{4}\-\d{2}\-\d{2}$/', $s ) ) {
            return false;
        }
        list( $y, $m, $d ) = array_map( 'intval', explode( '-', $s ) );
        return checkdate( $m, $d, $y );
    }

    private static function is_valid_hhmm( $s ) {
        if ( ! is_string( $s ) ) {
            return false;
        }
        $s = trim( $s );
        if ( ! preg_match( '/^\d{2}:\d{2}$/', $s ) ) {
            return false;
        }
        list( $h, $m ) = array_map( 'intval', explode( ':', $s ) );
        return ( $h >= 0 && $h <= 23 && $m >= 0 && $m <= 59 );
    }

    private static function hhmm_to_minutes( $hhmm ) {
        if ( ! self::is_valid_hhmm( $hhmm ) ) {
            return null;
        }
        list( $h, $m ) = array_map( 'intval', explode( ':', $hhmm ) );
        return $h * 60 + $m;
    }

    private static function weekday_key_from_date( $dateYmd ) {
        if ( ! $dateYmd || ! preg_match( '/^\d{4}\-\d{2}\-\d{2}$/', (string) $dateYmd ) ) {
            return '';
        }
        $dow = strtolower( gmdate( 'D', strtotime( $dateYmd ) ) ); // Mon, Tue...
        return strtolower( substr( $dow, 0, 3 ) ); // mon/tue/...
    }

    private static function hours_for_date( $dateYmd, $opts ) {
        $k = self::weekday_key_from_date( $dateYmd );
        if ( ! $k ) {
            return array(
                'open'   => '',
                'close'  => '',
                'closed' => 1,
            );
        }
        $h = isset( $opts['weekly_hours'][ $k ] ) ? $opts['weekly_hours'][ $k ] : null;
        if ( ! is_array( $h ) ) {
            return array(
                'open'   => '',
                'close'  => '',
                'closed' => 1,
            );
        }
        return array(
            'open'   => (string) ( isset( $h['open'] ) ? $h['open'] : '' ),
            'close'  => (string) ( isset( $h['close'] ) ? $h['close'] : '' ),
            'closed' => ! empty( $h['closed'] ) ? 1 : 0,
        );
    }

    private static function times_for_day( $open_time, $close_time, $step ) {
        $out   = array();
        $start = strtotime( '2000-01-01 ' . $open_time . ':00' );
        $end   = strtotime( '2000-01-01 ' . $close_time . ':00' );
        if ( $end <= $start ) {
            return $out;
        }
        for ( $t = $start; $t <= $end; $t += $step * 60 ) {
            if ( $t === $end ) {
                break; // don't allow exact close time
            }
            $out[] = gmdate( 'H:i', $t );
        }
        return $out;
    }

    // ---- Frontend Form ----
    public static function sc_form() {
        $o          = self::get_opts();
        $action     = esc_url( admin_url( 'admin-post.php' ) );
        $maxParty   = intval( $o['max_party_size'] );
        $hours_json = wp_json_encode( $o['weekly_hours'] );
        $step       = intval( $o['time_step_mins'] );

        ob_start();
        ?>
        <div class="dabl-rr-form-wrap" data-weekly-hours="<?php echo esc_attr( $hours_json ); ?>" data-step="<?php echo esc_attr( $step ); ?>"
            data-duration-default="<?php echo esc_attr( intval( $o['duration_default_mins'] ) ); ?>"
            data-duration-large="<?php echo esc_attr( intval( $o['duration_large_party_mins'] ) ); ?>"
            data-large-threshold="<?php echo esc_attr( intval( $o['large_party_threshold'] ) ); ?>">

            <?php
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only, checking for success message
            if ( isset( $_GET['dabl_rr'] ) && sanitize_key( wp_unslash( $_GET['dabl_rr'] ) ) === 'ok' ) :
                ?>
                <div class="dabl-rr-success"><?php esc_html_e( 'Reservation request sent.', 'dabl-restaurant-reservations' ); ?></div>
            <?php endif; ?>

            <?php
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only, showing error message
            if ( isset( $_GET['dabl_rr_error'] ) ) :
                $errs = array(
                    'closed'  => __( 'That date is closed.', 'dabl-restaurant-reservations' ),
                    'all'     => __( 'Please fill in all required fields.', 'dabl-restaurant-reservations' ),
                    'invalid' => __( 'Invalid date or time.', 'dabl-restaurant-reservations' ),
                    'time'    => __( 'That time is unavailable.', 'dabl-restaurant-reservations' ),
                    'soon'    => __( 'That time is too soon.', 'dabl-restaurant-reservations' ),
                    'far'     => __( 'That date is too far out.', 'dabl-restaurant-reservations' ),
                    'block'   => __( 'That time is blocked.', 'dabl-restaurant-reservations' ),
                    'nonce'   => __( 'Security check failed. Please try again.', 'dabl-restaurant-reservations' ),
                );
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                $c   = sanitize_key( wp_unslash( $_GET['dabl_rr_error'] ) );
                $msg = isset( $errs[ $c ] ) ? $errs[ $c ] : __( 'Something went wrong. Please try again.', 'dabl-restaurant-reservations' );
                ?>
                <div class="dabl-rr-error"><?php echo esc_html( $msg ); ?></div>
            <?php endif; ?>

            <form class="dabl-rr-form" method="post" action="<?php echo esc_url( $action ); ?>">
                <input type="hidden" name="action" value="dabl_rr_book" />
                <?php wp_nonce_field( 'dabl_rr_book', 'dabl_rr_nonce' ); ?>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Name', 'dabl-restaurant-reservations' ); ?></label>
                    <input <?php echo $o['require_name'] ? esc_attr( 'required' ) : ''; ?> name="name" />
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Email', 'dabl-restaurant-reservations' ); ?></label>
                    <input <?php echo $o['require_email'] ? esc_attr( 'required' ) : ''; ?> type="email" name="email" />
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Phone', 'dabl-restaurant-reservations' ); ?></label>
                    <input <?php echo $o['require_phone'] ? esc_attr( 'required' ) : ''; ?> name="phone" />
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Date', 'dabl-restaurant-reservations' ); ?></label>
                    <input required type="text" name="date" class="dabl-rr-date" autocomplete="off" />
                    <div class="dabl-rr-date-msg" aria-live="polite"></div>
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Time', 'dabl-restaurant-reservations' ); ?></label>
                    <select required name="time" data-dabl-rr-time>
                        <option value=""><?php esc_html_e( 'Select a date first', 'dabl-restaurant-reservations' ); ?></option>
                    </select>
                    <div class="description" data-dabl-rr-time-note></div>
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Party size', 'dabl-restaurant-reservations' ); ?></label>
                    <input required type="number" name="party" min="1" max="<?php echo esc_attr( $maxParty ); ?>" value="2" />
                </div>

                <div class="dabl-rr-row">
                    <label><?php esc_html_e( 'Notes (optional)', 'dabl-restaurant-reservations' ); ?></label>
                    <textarea name="notes" rows="3"></textarea>
                </div>

                <button class="dabl-rr-btn" type="submit"><?php esc_html_e( 'Request Reservation', 'dabl-restaurant-reservations' ); ?></button>

                <?php if ( isset( $o['cancellation_mode'] ) && $o['cancellation_mode'] === 'phone' && ! empty( $o['cancellation_phone'] ) ) : ?>
                    <p class="dabl-rr-note">
                        <?php
                        printf(
                            /* translators: %s: phone number */
                            esc_html__( 'To cancel or change last-minute, please call %s.', 'dabl-restaurant-reservations' ),
                            esc_html( $o['cancellation_phone'] )
                        );
                        ?>
                    </p>
                <?php endif; ?>
            </form>
        </div>
        <?php
        return ob_get_clean();
    }

    public static function handle_booking() {
        if ( ! isset( $_POST['dabl_rr_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['dabl_rr_nonce'] ) ), 'dabl_rr_book' ) ) {
            wp_die( esc_html__( 'Security check failed', 'dabl-restaurant-reservations' ) );
        }

        $o = self::get_opts();

        $name  = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
        $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
        $phone = isset( $_POST['phone'] ) ? sanitize_text_field( wp_unslash( $_POST['phone'] ) ) : '';
        $date  = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
        $time  = isset( $_POST['time'] ) ? sanitize_text_field( wp_unslash( $_POST['time'] ) ) : '';
        $party = isset( $_POST['party'] ) ? max( 1, min( intval( $o['max_party_size'] ), intval( $_POST['party'] ) ) ) : 2;
        $notes = isset( $_POST['notes'] ) ? sanitize_textarea_field( wp_unslash( $_POST['notes'] ) ) : '';

        // Required checks
        if ( $o['require_name'] && ! $name ) {
            wp_die( esc_html__( 'Name is required.', 'dabl-restaurant-reservations' ) );
        }
        if ( $o['require_email'] && ! $email ) {
            wp_die( esc_html__( 'Email is required.', 'dabl-restaurant-reservations' ) );
        }
        if ( $o['require_phone'] && ! $phone ) {
            wp_die( esc_html__( 'Phone is required.', 'dabl-restaurant-reservations' ) );
        }

        if ( ! $date || ! preg_match( '/^\d{4}\-\d{2}\-\d{2}$/', $date ) ) {
            wp_die( esc_html__( 'Invalid date.', 'dabl-restaurant-reservations' ) );
        }
        if ( ! $time || ! preg_match( '/^\d{2}:\d{2}$/', $time ) ) {
            wp_die( esc_html__( 'Invalid time.', 'dabl-restaurant-reservations' ) );
        }

        // Availability enforcement
        $h = self::hours_for_date( $date, $o );
        if ( ! empty( $h['closed'] ) ) {
            wp_die( esc_html__( 'That day is closed.', 'dabl-restaurant-reservations' ) );
        }

        $duration = intval( $o['duration_default_mins'] );

        // Ensure the reservation end time does not go past close time.
        $startMin = self::hhmm_to_minutes( $time );
        $closeMin = self::hhmm_to_minutes( isset( $h['close'] ) ? $h['close'] : '' );
        if ( $startMin !== null && $closeMin !== null ) {
            if ( ( $startMin + $duration ) > $closeMin ) {
                wp_die( esc_html__( 'That time is unavailable.', 'dabl-restaurant-reservations' ) );
            }
        }

        $valid_times = self::times_for_day( $h['open'], $h['close'], intval( $o['time_step_mins'] ) );
        if ( empty( $valid_times ) || ! in_array( $time, $valid_times, true ) ) {
            wp_die( esc_html__( 'That time is unavailable.', 'dabl-restaurant-reservations' ) );
        }

        // Notice & range limits
        $minNotice = intval( $o['min_notice_hours'] );
        $maxAhead  = intval( $o['max_days_ahead'] );
        $now       = current_time( 'timestamp' );
        // Use WP/site timezone so notice/ahead checks match what admins expect.
        $req_dt = date_create_from_format( 'Y-m-d H:i', $date . ' ' . $time, wp_timezone() );
        if ( ! $req_dt ) {
            wp_die( esc_html__( 'Invalid date/time.', 'dabl-restaurant-reservations' ) );
        }
        $req_ts = $req_dt->getTimestamp();
        if ( $minNotice > 0 && $req_ts < ( $now + ( $minNotice * 3600 ) ) ) {
            wp_die( esc_html__( 'That time is too soon.', 'dabl-restaurant-reservations' ) );
        }
        if ( $maxAhead > 0 && $req_ts > ( $now + ( $maxAhead * 86400 ) ) ) {
            wp_die( esc_html__( 'That date is too far out.', 'dabl-restaurant-reservations' ) );
        }

        $status = 'pending';

        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table for reservations
        $wpdb->insert(
            $table,
            array(
                'status'        => $status,
                'name'          => $name ? $name : __( '(No name)', 'dabl-restaurant-reservations' ),
                'email'         => $email ? $email : __( '(No email)', 'dabl-restaurant-reservations' ),
                'phone'         => $phone ? $phone : __( '(No phone)', 'dabl-restaurant-reservations' ),
                'party_size'    => $party,
                'res_date'      => $date,
                'res_time'      => $time . ':00',
                'notes'         => $notes,
                'duration_mins' => $duration,
                'created_at'    => current_time( 'mysql' ),
            ),
            array( '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%d', '%s' )
        );

        self::send_emails(
            $o,
            array(
                'restaurant' => $o['restaurant_name'],
                'name'       => $name,
                'email'      => $email,
                'phone'      => $phone,
                'date'       => $date,
                'time'       => $time,
                'party'      => (string) $party,
                'notes'      => $notes,
            ),
            $status
        );

        $back = wp_get_referer() ? wp_get_referer() : home_url( '/' );
        $back = add_query_arg( array( 'dabl_rr' => 'ok' ), $back );
        wp_safe_redirect( $back );
        exit;
    }

    public static function handle_portal_update() {
        if ( ! is_user_logged_in() ) {
            wp_die( esc_html__( 'Staff only.', 'dabl-restaurant-reservations' ) );
        }
        if ( ! current_user_can( 'dabl_rr_view_portal' ) && ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'Staff only.', 'dabl-restaurant-reservations' ) );
        }

        $nonce = isset( $_POST['dabl_rr_portal_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['dabl_rr_portal_nonce'] ) ) : '';
        if ( ! $nonce || ! wp_verify_nonce( $nonce, 'dabl_rr_portal_update' ) ) {
            wp_die( esc_html__( 'Security check failed', 'dabl-restaurant-reservations' ) );
        }

        $id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0;
        $do = isset( $_POST['do'] ) ? sanitize_key( wp_unslash( $_POST['do'] ) ) : '';

        if ( $id <= 0 || ! in_array( $do, array( 'confirm', 'cancel' ), true ) ) {
            wp_die( esc_html__( 'Invalid request', 'dabl-restaurant-reservations' ) );
        }

        $o = self::get_opts();
        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table, caching not needed for single reservation lookup
        $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id=%d", $id ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        if ( ! $row ) {
            wp_die( esc_html__( 'Reservation not found.', 'dabl-restaurant-reservations' ) );
        }

        // Action logic
        if ( $do === 'confirm' ) {
            if ( strtolower( (string) $row['status'] ) !== 'pending' ) {
                $back = wp_get_referer() ? wp_get_referer() : home_url( '/' );
                $back = add_query_arg( array( 'dabl_rr_portal' => 'noop' ), $back );
                wp_safe_redirect( $back );
                exit;
            }
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table update
            $updated = $wpdb->update(
                $table,
                array(
                    'status'     => 'confirmed',
                    'updated_at' => current_time( 'mysql' ),
                ),
                array( 'id' => $id ),
                array( '%s', '%s' ),
                array( '%d' )
            );

            if ( $updated !== false && ! empty( $row['email'] ) ) {
                $tags = array(
                    'restaurant' => $o['restaurant_name'],
                    'name'       => $row['name'],
                    'email'      => $row['email'],
                    'phone'      => $row['phone'],
                    'date'       => $row['res_date'],
                    'time'       => substr( $row['res_time'], 0, 5 ),
                    'party'      => (string) ( isset( $row['party_size'] ) ? $row['party_size'] : '' ),
                    'notes'      => isset( $row['notes'] ) ? $row['notes'] : '',
                );
                self::send_emails( $o, $tags, 'confirmed' );
            }
        } elseif ( $do === 'cancel' ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table update
            $updated = $wpdb->update(
                $table,
                array(
                    'status'     => 'cancelled',
                    'updated_at' => current_time( 'mysql' ),
                ),
                array( 'id' => $id ),
                array( '%s', '%s' ),
                array( '%d' )
            );

            if ( $updated !== false && ! empty( $row['email'] ) ) {
                $tags = array(
                    'restaurant' => $o['restaurant_name'],
                    'name'       => $row['name'],
                    'email'      => $row['email'],
                    'phone'      => $row['phone'],
                    'date'       => $row['res_date'],
                    'time'       => substr( $row['res_time'], 0, 5 ),
                    'party'      => (string) ( isset( $row['party_size'] ) ? $row['party_size'] : '' ),
                    'notes'      => isset( $row['notes'] ) ? $row['notes'] : '',
                );
                self::send_emails( $o, $tags, 'cancelled' );
            }
        }

        $back = wp_get_referer() ? wp_get_referer() : home_url( '/' );
        $back = add_query_arg( array( 'dabl_rr_portal' => 'updated' ), $back );
        wp_safe_redirect( $back );
        exit;
    }

    private static function send_emails( $o, $tags, $status ) {
        $e = isset( $o['emails'] ) ? $o['emails'] : array();

        $from_name  = self::apply_tags( isset( $e['from_name'] ) ? $e['from_name'] : $o['restaurant_name'], $tags );
        $from_email = isset( $e['from_email'] ) && $e['from_email'] ? sanitize_email( $e['from_email'] ) : sanitize_email( get_option( 'admin_email' ) );

        // Sanitize from_name to prevent header injection
        $from_name = str_replace( array( "\r", "\n" ), '', $from_name );

        // Admin email headers (plain text)
        $admin_headers   = array();
        $admin_headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';

        // Customer email headers (HTML)
        $customer_headers   = array();
        $customer_headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
        $customer_headers[] = 'Content-Type: text/html; charset=UTF-8';

        // Send to restaurant admin (plain text)
        $to_restaurant_subject = isset( $e['to_restaurant_subject'] ) ? $e['to_restaurant_subject'] : '';
        $to_restaurant_body    = isset( $e['to_restaurant_body'] ) ? $e['to_restaurant_body'] : '';

        if ( $to_restaurant_subject && $to_restaurant_body ) {
            wp_mail(
                sanitize_email( get_option( 'admin_email' ) ),
                self::apply_tags( $to_restaurant_subject, $tags ),
                self::apply_tags( $to_restaurant_body, $tags ),
                $admin_headers
            );
        }

        // Send to customer (only if we have customer email and templates)
        if ( ! empty( $tags['email'] ) && is_email( $tags['email'] ) ) {
            $to_customer_subject = isset( $e['to_customer_subject'] ) ? $e['to_customer_subject'] : '';
            $to_customer_body    = isset( $e['to_customer_body'] ) ? $e['to_customer_body'] : '';

            if ( $status === 'pending' && $to_customer_subject && $to_customer_body ) {
                $body_html = nl2br( esc_html( self::apply_tags( $to_customer_body, $tags ) ) );
                wp_mail(
                    sanitize_email( $tags['email'] ),
                    self::apply_tags( $to_customer_subject, $tags ),
                    $body_html,
                    $customer_headers
                );
            }

            if ( $status === 'confirmed' ) {
                $confirm_subject = isset( $e['to_customer_confirm_subject'] ) ? $e['to_customer_confirm_subject'] : '';
                $confirm_body    = isset( $e['to_customer_confirm_body'] ) ? $e['to_customer_confirm_body'] : '';
                if ( $confirm_subject && $confirm_body ) {
                    $body_html = nl2br( esc_html( self::apply_tags( $confirm_body, $tags ) ) );
                    wp_mail(
                        sanitize_email( $tags['email'] ),
                        self::apply_tags( $confirm_subject, $tags ),
                        $body_html,
                        $customer_headers
                    );
                }
            }

            if ( $status === 'cancelled' ) {
                $cancel_subject = isset( $e['to_customer_cancel_subject'] ) ? $e['to_customer_cancel_subject'] : '';
                $cancel_body    = isset( $e['to_customer_cancel_body'] ) ? $e['to_customer_cancel_body'] : '';
                if ( $cancel_subject && $cancel_body ) {
                    $body_html = nl2br( esc_html( self::apply_tags( $cancel_body, $tags ) ) );
                    wp_mail(
                        sanitize_email( $tags['email'] ),
                        self::apply_tags( $cancel_subject, $tags ),
                        $body_html,
                        $customer_headers
                    );
                }
            }
        }
    }

    private static function apply_tags( $text, $tags ) {
        foreach ( $tags as $k => $v ) {
            $text = str_replace( '{' . $k . '}', (string) $v, (string) $text );
        }
        return $text;
    }

    // ---- Staff / Admin views ----
    public static function sc_staff_login() {
        if ( is_user_logged_in() ) {
            return '<div class="dabl-rr-cardlike">' .
                esc_html__( 'You are logged in.', 'dabl-restaurant-reservations' ) .
                ' <a href="' . esc_url( wp_logout_url( get_permalink() ) ) . '">' .
                esc_html__( 'Log out', 'dabl-restaurant-reservations' ) .
                '</a></div>';
        }
        return wp_login_form( array( 'echo' => false ) );
    }

    public static function sc_staff_portal() {
        if ( ! is_user_logged_in() ) {
            return '<div class="dabl-rr-cardlike">' . esc_html__( 'Staff only. Please log in.', 'dabl-restaurant-reservations' ) . '</div>';
        }

        if ( ! current_user_can( 'dabl_rr_view_portal' ) && ! current_user_can( 'manage_options' ) ) {
            return '<div class="dabl-rr-cardlike">' . esc_html__( 'Staff only. Your account does not have access.', 'dabl-restaurant-reservations' ) . '</div>';
        }

        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;
        $today = current_time( 'Y-m-d' );

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display filtering only
        $filter_date = isset( $_GET['dabl_rr_date'] ) ? sanitize_text_field( wp_unslash( $_GET['dabl_rr_date'] ) ) : '';
        $is_filter   = ( $filter_date && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $filter_date ) );

        if ( $is_filter ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table listing
            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$table} WHERE res_date = %s AND status != 'cancelled' ORDER BY res_time ASC LIMIT 400", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    $filter_date
                ),
                ARRAY_A
            );
        } else {
            // Upcoming reservations (today and forward)
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table listing
            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$table} WHERE res_date >= %s AND status != 'cancelled' ORDER BY res_date ASC, res_time ASC LIMIT 200", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    $today
                ),
                ARRAY_A
            );
        }

        ob_start();
        ?>
        <div class="dabl-rr-portal">
            <?php if ( $is_filter ) : ?>
                <h2>
                    <?php
                    printf(
                        /* translators: %s: date */
                        esc_html__( 'Reservations for %s', 'dabl-restaurant-reservations' ),
                        esc_html( $filter_date )
                    );
                    ?>
                </h2>
                <?php if ( is_admin() ) : ?>
                    <p><a href="<?php echo esc_url( admin_url( 'admin.php?page=dabl-rr' ) ); ?>"><?php esc_html_e( 'View all upcoming', 'dabl-restaurant-reservations' ); ?></a></p>
                <?php endif; ?>
            <?php else : ?>
                <h2><?php esc_html_e( 'Upcoming Reservations', 'dabl-restaurant-reservations' ); ?></h2>
            <?php endif; ?>

            <?php
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only
            if ( ! empty( $_GET['dabl_rr_portal'] ) && sanitize_key( wp_unslash( $_GET['dabl_rr_portal'] ) ) === 'updated' ) :
                ?>
                <div class="dabl-rr-success"><?php esc_html_e( 'Reservation updated.', 'dabl-restaurant-reservations' ); ?></div>
            <?php
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only
            elseif ( ! empty( $_GET['dabl_rr_portal'] ) && sanitize_key( wp_unslash( $_GET['dabl_rr_portal'] ) ) === 'noop' ) :
                ?>
                <div class="dabl-rr-cardlike"><?php esc_html_e( 'No changes were made.', 'dabl-restaurant-reservations' ); ?></div>
            <?php endif; ?>

            <?php if ( empty( $rows ) ) : ?>
                <div class="dabl-rr-cardlike"><?php esc_html_e( 'No reservations found.', 'dabl-restaurant-reservations' ); ?></div>
            <?php else : ?>
                <div class="dabl-rr-res-list">
                    <?php foreach ( $rows as $r ) : ?>
                        <?php $status_key = strtolower( (string) ( isset( $r['status'] ) ? $r['status'] : '' ) ); ?>
                        <div class="dabl-rr-res-row dabl-rr-status-<?php echo esc_attr( $status_key ); ?>">
                            <div class="dabl-rr-res-when">
                                <strong><?php echo esc_html( $r['res_date'] ); ?></strong>
                                <span><?php echo esc_html( substr( (string) $r['res_time'], 0, 5 ) ); ?></span>
                            </div>

                            <div class="dabl-rr-res-main">
                                <div>
                                    <strong><?php echo esc_html( $r['name'] ); ?></strong> —
                                    <?php
                                    printf(
                                        /* translators: %d: party size */
                                        esc_html__( 'party of %d', 'dabl-restaurant-reservations' ),
                                        intval( $r['party_size'] )
                                    );
                                    ?>
                                </div>

                                <div class="dabl-rr-private">
                                    <button type="button" class="dabl-rr-reveal" data-target="c<?php echo intval( $r['id'] ); ?>"
                                        data-label-show="<?php echo esc_attr__( 'Show contact', 'dabl-restaurant-reservations' ); ?>"
                                        data-label-hide="<?php echo esc_attr__( 'Hide contact', 'dabl-restaurant-reservations' ); ?>">
                                        <?php esc_html_e( 'Show contact', 'dabl-restaurant-reservations' ); ?>
                                    </button>

                                    <span id="c<?php echo intval( $r['id'] ); ?>" class="dabl-rr-contact" hidden>
                                        <?php echo esc_html( $r['phone'] ); ?> · <?php echo esc_html( $r['email'] ); ?>
                                    </span>

                                    <?php if ( $status_key === 'pending' ) : ?>
                                        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="dabl-rr-inline">
                                            <input type="hidden" name="action" value="dabl_rr_portal_update" />
                                            <input type="hidden" name="do" value="confirm" />
                                            <input type="hidden" name="id" value="<?php echo intval( $r['id'] ); ?>" />
                                            <input type="hidden" name="dabl_rr_portal_nonce" value="<?php echo esc_attr( wp_create_nonce( 'dabl_rr_portal_update' ) ); ?>" />
                                            <button type="submit" class="dabl-rr-btn dabl-rr-btn-small"><?php esc_html_e( 'Confirm', 'dabl-restaurant-reservations' ); ?></button>
                                        </form>
                                    <?php endif; ?>

                                    <?php if ( $status_key === 'confirmed' || $status_key === 'pending' ) : ?>
                                        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="dabl-rr-inline"
                                            style="display:inline-block;margin-left:4px;"
                                            onsubmit="return confirm('<?php echo esc_js( __( 'This cannot be undone. Are you sure you want to cancel this reservation?', 'dabl-restaurant-reservations' ) ); ?>');">
                                            <input type="hidden" name="action" value="dabl_rr_portal_update" />
                                            <input type="hidden" name="do" value="cancel" />
                                            <input type="hidden" name="id" value="<?php echo intval( $r['id'] ); ?>" />
                                            <input type="hidden" name="dabl_rr_portal_nonce" value="<?php echo esc_attr( wp_create_nonce( 'dabl_rr_portal_update' ) ); ?>" />
                                            <button type="submit" class="dabl-rr-btn dabl-rr-btn-small" style="background:#ef4444;color:#fff;">
                                                <?php esc_html_e( 'Cancel', 'dabl-restaurant-reservations' ); ?>
                                            </button>
                                        </form>
                                    <?php endif; ?>
                                </div>

                                <?php if ( ! empty( $r['notes'] ) ) : ?>
                                    <div class="dabl-rr-notes"><?php echo esc_html( $r['notes'] ); ?></div>
                                <?php endif; ?>
                            </div>

                            <div class="dabl-rr-res-status">
                                <?php echo esc_html( ucfirst( $status_key ? $status_key : 'pending' ) ); ?>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </div>

        <?php
        return ob_get_clean();
    }

    public static function sc_admin_calendar() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return '<div class="dabl-rr-cardlike">' . esc_html__( 'Admin only.', 'dabl-restaurant-reservations' ) . '</div>';
        }

        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display filtering only
        $ym = isset( $_GET['dabl_rr_month'] ) ? preg_replace( '/[^0-9\-]/', '', sanitize_text_field( wp_unslash( $_GET['dabl_rr_month'] ) ) ) : current_time( 'Y-m' );
        if ( ! preg_match( '/^\d{4}\-\d{2}$/', $ym ) ) {
            $ym = current_time( 'Y-m' );
        }
        $first    = $ym . '-01';
        $start_ts = strtotime( $first );
        $days_in  = intval( gmdate( 't', $start_ts ) );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table aggregate query for calendar
        $counts = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT res_date, COUNT(*) c FROM {$table} WHERE (res_date BETWEEN %s AND %s) AND status != 'cancelled' GROUP BY res_date", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $first,
                $ym . '-' . $days_in
            ),
            ARRAY_A
        );

        $map = array();
        foreach ( $counts as $c ) {
            $map[ $c['res_date'] ] = intval( $c['c'] );
        }

        ob_start();
        ?>
        <div class="dabl-rr-calendar">
            <h2>
                <?php
                printf(
                    /* translators: %s: month in YYYY-MM format */
                    esc_html__( 'Reservations Calendar (%s)', 'dabl-restaurant-reservations' ),
                    esc_html( $ym )
                );
                ?>
            </h2>
            <div class="dabl-rr-cal-grid">
                <?php for ( $d = 1; $d <= $days_in; $d++ ) : ?>
                    <?php
                    $date = sprintf( '%s-%02d', $ym, $d );
                    $c    = isset( $map[ $date ] ) ? $map[ $date ] : 0;
                    ?>
                    <div class="dabl-rr-cal-day">
                        <div class="dabl-rr-cal-num"><?php echo intval( $d ); ?></div>
                        <div class="dabl-rr-cal-count">
                            <?php if ( $c ) : ?>
                                <?php $u = admin_url( 'admin.php?page=dabl-rr&dabl_rr_date=' . $date ); ?>
                                <a class="dabl-rr-cal-link" href="<?php echo esc_url( $u ); ?>">
                                    <?php
                                    printf(
                                        /* translators: %d: number of reservations */
                                        esc_html( _n( '%d reservation', '%d reservations', $c, 'dabl-restaurant-reservations' ) ),
                                        intval( $c )
                                    );
                                    ?>
                                </a>
                            <?php else : ?>
                                &nbsp;
                            <?php endif; ?>
                        </div>
                    </div>
                <?php endfor; ?>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }
}

Dabl_RR_Plugin::init();
