<?php
namespace Bookaro\Controller\Admin;

defined('ABSPATH') || exit;

// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Settings reads are sanitized tab/section params (writes go through the Settings API / check_admin_referer, flagged separately as .Missing); the booking-count SELECTs target a custom plugin table ($wpdb->prefix) and bind values via prepare().

use Bookaro\Controller\Admin\Settings\SettingsTabRegistry;
use Bookaro\Repository\BookingRepository;
use Bookaro\Repository\OptionRepository;
use Bookaro\Utils\AdminLaneSummary;
use Bookaro\Utils\BookingPolicyPresenter;
use Bookaro\Utils\Logger;
use Bookaro\Utils\TemplateLoader;

/**
 * Handles the plugin’s Settings page: registering fields,
 * rendering the page, sanitizing input, and enqueuing assets.
 */
class SettingsPageController
{
    private const SETTINGS_GROUP = 'bookaro_settings_group';
    private const GENERIC_LAUNCH_NOTICE_KEY = 'bookaro_generic_launch_validation_notice';
    private const CREATE_DASHBOARD_PAGES_FORM_ID = 'bookaro-create-dashboard-pages-form';

    private OptionRepository $options;

    public function __construct(OptionRepository $options)
    {
        $this->options = $options;
    }

    private function admin_menu_capability(): string
    {
        return function_exists( 'bookaro_get_admin_menu_capability' )
            ? bookaro_get_admin_menu_capability()
            : 'manage_options';
    }

    private function can_manage_admin(): bool
    {
        if ( function_exists( 'bookaro_current_user_can_manage_admin' ) ) {
            return bookaro_current_user_can_manage_admin();
        }
        return current_user_can( 'manage_options' );
    }

    /**
     * Register hooks for the settings page.
     */
    public function register_hooks(): void
    {
        add_action('admin_menu', [ $this, 'add_submenu_page' ], 20);
        add_action('admin_init', [ $this, 'register_settings' ]);
        add_filter( 'option_page_capability_' . self::SETTINGS_GROUP, [ $this, 'filter_option_page_capability' ] );
        add_action('admin_enqueue_scripts', [ $this, 'enqueue_assets' ]);
        add_action('admin_post_bookaro_send_test_email', [ $this, 'handle_test_email' ]);
        add_action('admin_post_bookaro_create_dashboard_pages', [ $this, 'handle_create_dashboard_pages' ]);
    }

    public function filter_option_page_capability( $default_capability ): string
    {
        $capability = $this->admin_menu_capability();
        if ( is_string( $capability ) && '' !== trim( $capability ) ) {
            return $capability;
        }
        if ( is_string( $default_capability ) && '' !== trim( $default_capability ) ) {
            return sanitize_key( $default_capability );
        }
        return 'manage_options';
    }

    /**
     * Enqueue color picker assets + inline styles/scripts on our settings page.
     *
     * @param string $hook Current admin page hook.
     */
    public function enqueue_assets(string $hook): void
    {
        $is_settings_page = ( 'bookaro-bookings_page_bookaro-booking-settings' === $hook );
        if ( ! $is_settings_page && function_exists( 'get_current_screen' ) ) {
            $screen = get_current_screen();
            if ( $screen && ! empty( $screen->id ) ) {
                $is_settings_page = ( 'bookaro-bookings_page_bookaro-booking-settings' === $screen->id );
            }
        }
        if ( ! $is_settings_page ) {
            $page = sanitize_key( wp_unslash( $_GET['page'] ?? '' ) );
            $is_settings_page = ( 'bookaro-booking-settings' === $page );
        }
        if ( ! $is_settings_page ) {
            return;
        }

        $css_rel = 'assets/css/admin-settings.css';
        $css_path = $this->plugin_asset_path( $css_rel );
        $css_ver = file_exists( $css_path ) ? (string) filemtime( $css_path ) : BOOKARO_VERSION;
        wp_enqueue_style( 'bookaro-admin-settings', $this->plugin_asset_url( $css_rel ), [], $css_ver );
        $theme_defaults = [
            'accent' => '#0f766e',
            'accent_soft' => '#ccfbf1',
            'accent_text' => '#134e4a',
            'surface' => '#ffffff',
            'surface_alt' => '#f8fafc',
            'surface_muted' => '#f1f5f9',
            'border' => '#e2e8f0',
            'text' => '#0f172a',
            'muted' => '#64748b',
            'danger' => '#dc2626',
            'warning' => '#f59e0b',
            'success' => '#16a34a',
            'shadow' => 'rgba(15, 23, 42, 0.08)',
        ];
        $theme = apply_filters( 'bookaro_admin_settings_theme', $theme_defaults );
        if ( ! is_array( $theme ) ) {
            $theme = $theme_defaults;
        }
        $vars = [
            'bookaro-accent' => $this->sanitize_css_var( $theme['accent'] ?? $theme_defaults['accent'], $theme_defaults['accent'] ),
            'bookaro-accent-soft' => $this->sanitize_css_var( $theme['accent_soft'] ?? $theme_defaults['accent_soft'], $theme_defaults['accent_soft'] ),
            'bookaro-accent-text' => $this->sanitize_css_var( $theme['accent_text'] ?? $theme_defaults['accent_text'], $theme_defaults['accent_text'] ),
            'bookaro-surface' => $this->sanitize_css_var( $theme['surface'] ?? $theme_defaults['surface'], $theme_defaults['surface'] ),
            'bookaro-surface-alt' => $this->sanitize_css_var( $theme['surface_alt'] ?? $theme_defaults['surface_alt'], $theme_defaults['surface_alt'] ),
            'bookaro-surface-muted' => $this->sanitize_css_var( $theme['surface_muted'] ?? $theme_defaults['surface_muted'], $theme_defaults['surface_muted'] ),
            'bookaro-border' => $this->sanitize_css_var( $theme['border'] ?? $theme_defaults['border'], $theme_defaults['border'] ),
            'bookaro-text' => $this->sanitize_css_var( $theme['text'] ?? $theme_defaults['text'], $theme_defaults['text'] ),
            'bookaro-muted' => $this->sanitize_css_var( $theme['muted'] ?? $theme_defaults['muted'], $theme_defaults['muted'] ),
            'bookaro-danger' => $this->sanitize_css_var( $theme['danger'] ?? $theme_defaults['danger'], $theme_defaults['danger'] ),
            'bookaro-warning' => $this->sanitize_css_var( $theme['warning'] ?? $theme_defaults['warning'], $theme_defaults['warning'] ),
            'bookaro-success' => $this->sanitize_css_var( $theme['success'] ?? $theme_defaults['success'], $theme_defaults['success'] ),
            'bookaro-shadow' => $this->sanitize_css_var( $theme['shadow'] ?? $theme_defaults['shadow'], $theme_defaults['shadow'] ),
        ];
        $theme_css = '.bookaro-admin{';
        foreach ( $vars as $name => $value ) {
            if ( $value === '' ) {
                continue;
            }
            $theme_css .= '--' . $name . ':' . $value . ';';
        }
        $theme_css .= '}';
        wp_add_inline_style( 'bookaro-admin-settings', $theme_css );
        wp_enqueue_style('wp-color-picker');
        wp_enqueue_script('wp-color-picker');

        // Load a tiny script to activate the WP color picker.
        wp_register_script( 'bookaro-settings', '', [ 'wp-color-picker' ], BOOKARO_VERSION, true );
        wp_enqueue_script('bookaro-settings');
        $color_palette_json = wp_json_encode( $this->get_default_color_palette() );
        $color_js = <<<'JS'
jQuery(function($){
  var defaultPalette = __BOOKARO_COLOR_PALETTE__;
  var normalizePalette = function(values){
    if(!Array.isArray(values)){ return defaultPalette; }
    var out = values
      .filter(function(color){ return typeof color === 'string' && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color.trim()); })
      .map(function(color){ return color.trim(); });
    return out.length ? out : defaultPalette;
  };
  $('.bookaro-color-field').each(function(){
    var palette = defaultPalette;
    var customPalette = $(this).attr('data-bookaro-palette');
    if(customPalette){
      try{
        palette = normalizePalette(JSON.parse(customPalette));
      }catch(e){
        palette = defaultPalette;
      }
    }
    $(this).wpColorPicker({ palettes: palette });
  });
});
JS;
        $color_js = str_replace( '__BOOKARO_COLOR_PALETTE__', (string) $color_palette_json, $color_js );
        wp_add_inline_script( 'bookaro-settings', $color_js );

        $tabs_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var e=document.querySelector('.bookaro-admin .bookaro-tabs');if(!e)return;var t=e.querySelectorAll('button[data-tab]'),n=document.querySelectorAll('.bookaro-settings-panel'),o=document.querySelector('form[data-bookaro-settings-main-form="1"]');if(!o){o=document.querySelector('form')}var r=o?o.querySelector('input[name="_wp_http_referer"]'):null;var a=function(s){t.forEach(function(c){var i=c.getAttribute('data-tab')===s;c.classList.toggle('active',i);c.setAttribute('aria-selected',i?'true':'false')});n.forEach(function(c){c.classList.toggle('active',c.getAttribute('data-tab')===s)});try{var i=new URL(window.location.href);i.searchParams.set('tab',s);window.history.replaceState(null,'',i.toString());if(r){r.value=i.pathname+i.search+i.hash}}catch(l){}};var d=e.getAttribute('data-current')||'general';a(d);t.forEach(function(s){s.addEventListener('click',function(){a(s.getAttribute('data-tab'))})})});
JS;
        wp_add_inline_script( 'bookaro-settings', $tabs_js );

        $help_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var e=document.querySelector('.bookaro-admin-settings');if(!e)return;var t=e.querySelector('.bookaro-help-toggle');if(!t)return;var n='bookaro_settings_help_compact';var o=false;try{var r=localStorage.getItem(n);if(r!==null){o=r==='1'}}catch(a){}var l=function(r){e.classList.toggle('bookaro-help-compact',r);t.setAttribute('aria-expanded',r?'false':'true');var a=r?t.getAttribute('data-label-collapsed'):t.getAttribute('data-label-expanded');if(a){t.textContent=a}};l(o);t.addEventListener('click',function(){var r=!e.classList.contains('bookaro-help-compact');l(r);try{localStorage.setItem(n,r?'1':'0')}catch(a){}})});
JS;
        wp_add_inline_script( 'bookaro-settings', $help_js );

        $overview_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var e=document.querySelector('[data-bookaro-overview]');if(!e)return;var t=e.querySelector('.bookaro-overview-toggle');var n=e.querySelector('.bookaro-overview-content');if(!t||!n)return;var o='bookaro_overview_collapsed';var r=function(c){e.classList.toggle('is-collapsed',c);n.hidden=c;t.setAttribute('aria-expanded',c?'false':'true');var l=c?t.getAttribute('data-label-collapsed'):t.getAttribute('data-label-expanded');if(l){t.textContent=l}};var a=false;try{a=localStorage.getItem(o)==='1'}catch(i){}r(a);t.addEventListener('click',function(){var c=!e.classList.contains('is-collapsed');r(c);try{localStorage.setItem(o,c?'1':'0')}catch(i){}})});
JS;
        wp_add_inline_script( 'bookaro-settings', $overview_js );

        $email_empty_msg = esc_js( __( 'Please enter a test email address.', 'mybookaro' ) );
        $email_sending_msg = esc_js( __( 'Sending test email...', 'mybookaro' ) );
        $email_success_msg = esc_js( __( 'Test email sent.', 'mybookaro' ) );
        $email_failed_msg = esc_js( __( 'Test email failed. Check your site mail configuration.', 'mybookaro' ) );
        // Nowdoc (no PHP interpolation) — placeholders are substituted via strtr
        // below to avoid heredoc syntax, which Plugin Check disallows.
        $email_test_js_tpl = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var e=document.querySelector('.bookaro-email-test');if(!e)return;var t=e.querySelector('input'),n=e.querySelector('button.bookaro-email-test-button');var r=e.parentElement?e.parentElement.querySelector('.bookaro-email-test-result'):null;if(!t||!n||!r)return;var a=e.getAttribute('data-url')||'',o=e.getAttribute('data-action')||'',s=e.getAttribute('data-nonce')||'';if(!a||!o||!s)return;var c=e.getAttribute('data-msg-empty')||'__BOOKARO_EMAIL_EMPTY__';var i=e.getAttribute('data-msg-sending')||'__BOOKARO_EMAIL_SENDING__';var l=e.getAttribute('data-msg-success')||'__BOOKARO_EMAIL_SUCCESS__';var d=e.getAttribute('data-msg-failed')||'__BOOKARO_EMAIL_FAILED__';var u=function(m,f){r.textContent=m||'';r.classList.remove('is-success','is-error');if(f===true){r.classList.add('is-error')}else if(f===false){r.classList.add('is-success')}};n.addEventListener('click',function(){var m=(t.value||'').trim();if(!m){u(c,true);return}n.disabled=true;u(i);var f=new URLSearchParams();f.append('action',o);f.append('bookaro_email_test_nonce',s);f.append('bookaro_test_email',m);f.append('bookaro_email_test_mode','inline');fetch(a,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8','Accept':'application/json','X-Requested-With':'XMLHttpRequest'},body:f.toString()}).then(function(p){return p.json()}).then(function(p){if(p&&p.success){u((p.data&&p.data.message)||l,false)}else{u((p&&p.data&&p.data.message)||d,true)}}).catch(function(){u(d,true)}).finally(function(){n.disabled=false})})});
JS;
        $email_test_js = strtr( $email_test_js_tpl, [
            '__BOOKARO_EMAIL_EMPTY__'   => $email_empty_msg,
            '__BOOKARO_EMAIL_SENDING__' => $email_sending_msg,
            '__BOOKARO_EMAIL_SUCCESS__' => $email_success_msg,
            '__BOOKARO_EMAIL_FAILED__'  => $email_failed_msg,
        ] );
        wp_add_inline_script( 'bookaro-settings', $email_test_js );

        $multiselect_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var e=document.querySelectorAll('.bookaro-multiselect-wrapper');if(!e.length)return;var t=function(r){if(!r||r.disabled)return;var o=r.form;if(!o)return;var n=r.getAttribute('name');if(!n)return;var l=r.getAttribute('id')||'';o.querySelectorAll('input.bookaro-multiselect-hidden[data-for="'+l+'"]').forEach(function(a){a.remove()});[].slice.call(r.options).forEach(function(a){if(!a.selected)return;var i=document.createElement('input');i.type='hidden';i.name=n;i.value=a.value;i.className='bookaro-multiselect-hidden';if(l){i.setAttribute('data-for',l)}o.appendChild(i)})};e.forEach(function(r){var o=r.querySelector('.bookaro-multiselect-search'),n=r.querySelector('select.bookaro-multiselect');if(!o||!n)return;var l=[].slice.call(n.options).map(function(a){var i=(a.textContent||'')+' '+(a.value||'');return{opt:a,hay:i.toLowerCase()}});var s=function(){var a=(o.value||'').trim().toLowerCase();l.forEach(function(i){var c=!a||i.hay.indexOf(a)!==-1;i.opt.hidden=!c})};o.addEventListener('input',s);if(n.form&&!n.form.__bookaroMultiBound){n.form.__bookaroMultiBound=true;n.form.addEventListener('submit',function(){document.querySelectorAll('select.bookaro-multiselect').forEach(function(a){t(a)})})}});
});
JS;
        wp_add_inline_script( 'bookaro-settings', $multiselect_js );

        $copy_label = esc_js( __( 'Copy', 'mybookaro' ) );
        $copied_label = esc_js( __( 'Copied', 'mybookaro' ) );
        // Nowdoc + strtr substitution (Plugin Check disallows heredoc syntax).
        $shortcodes_js_tpl = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var t=document.querySelector('.bookaro-shortcode-generator');if(!t)return;var n=function(e){return e?String(e.value||'').trim():''};var r=function(e){return e?parseInt(e.value||'0',10)||0:0};var o=function(e){return e?parseFloat(e.value||'')||'':""};var a={listing:t.querySelector('[data-bookaro-field="listing_id"]'),provider:t.querySelector('[data-bookaro-field="provider_id"]'),label:t.querySelector('[data-bookaro-field="service_label"]'),price:t.querySelector('[data-bookaro-field="price"]'),layout:t.querySelector('[data-bookaro-field="layout"]'),columns:t.querySelector('[data-bookaro-field="columns"]')};var l=function(e){return e&&e.value?e.value:''};var i=function(e){var r=[];Object.keys(e).forEach(function(t){var n=e[t];if(n===null||n===undefined)return;if(n===''||n===0){if(t==='listing_id'||t==='provider_id')return;if(t==='service_id')return;if(t==='service_label'&&n==='')return;if(t==='price'&&n==='')return}r.push(t+'="'+String(n).replace(/"/g,'&quot;')+'"')});return r.join(' ')};var s=function(e,r){var o=i(r);return o?('['+e+' '+o+']'):('['+e+']')};var c=function(e,r){var o=t.querySelector('[data-bookaro-output="'+e+'"]');if(o){o.value=r}};var d=function(e,r){var o=t.querySelector('[data-bookaro-block="'+e+'"]');if(o){o.value=r}};var u=function(){var e=r(a.listing),i=r(a.provider),u=n(a.label),f=o(a.price),m=l(a.layout)||'grid',p=r(a.columns)||3;var g={listing_id:e||'',provider_id:i||'',service_label:u||'',price:f!==''?String(f):'',button_label:''};var h=s('bookaro_booking_button',g);c('booking_button',h);var v=s('bookaro_booking_form',{listing_id:e||'',provider_id:i||'',auto_open:'false'});c('booking_form',v);var y={listing_id:e||'',provider_id:i||'',layout:m,columns:p,show_price:'1',show_duration:'1'};if(!e&&i){y.source='provider'}var b=s('bookaro_services_cards',y);c('services_cards',b);c('provider_dashboard','[bookaro_provider_dashboard]');c('customer_dashboard','[bookaro_customer_dashboard]');var k={listingId:e||0,providerId:i||0,serviceLabel:u||'',price:f!==''?String(f):'',serviceDuration:0,buttonLabel:''};d('block_booking_button','bookaro/booking-button\n'+JSON.stringify(k,null,2));var w={listingId:e||0,providerId:i||0,autoOpen:false};d('block_booking_form','bookaro/booking-form\n'+JSON.stringify(w,null,2));var x={listingId:e||0,providerId:i||0,layout:m,columns:p,showPrice:true,showDuration:true,source:(!e&&i)?'provider':'auto'};d('block_services_cards','bookaro/services-cards\n'+JSON.stringify(x,null,2));d('block_provider_dashboard','bookaro/provider-dashboard\n'+JSON.stringify({},null,2));d('block_customer_dashboard','bookaro/customer-dashboard\n'+JSON.stringify({},null,2))};t.addEventListener('input',u);t.addEventListener('change',u);u();t.querySelectorAll('[data-bookaro-copy]').forEach(function(e){e.addEventListener('click',function(){var r=t.querySelector('[data-bookaro-output="'+e.getAttribute('data-bookaro-copy')+'"]');if(!r)return;r.select();try{document.execCommand('copy');e.textContent=e.getAttribute('data-bookaro-copied')||'__BOOKARO_COPIED__';setTimeout(function(){e.textContent=e.getAttribute('data-bookaro-label')||'__BOOKARO_COPY__'},1200)}catch(o){}})});t.querySelectorAll('[data-bookaro-copy-block]').forEach(function(e){e.addEventListener('click',function(){var r=t.querySelector('[data-bookaro-block="'+e.getAttribute('data-bookaro-copy-block')+'"]');if(!r)return;r.select();try{document.execCommand('copy');e.textContent=e.getAttribute('data-bookaro-copied')||'__BOOKARO_COPIED__';setTimeout(function(){e.textContent=e.getAttribute('data-bookaro-label')||'__BOOKARO_COPY__'},1200)}catch(o){}})})});
JS;
        $shortcodes_js = strtr( $shortcodes_js_tpl, [
            '__BOOKARO_COPIED__' => $copied_label,
            '__BOOKARO_COPY__'   => $copy_label,
        ] );
        wp_add_inline_script( 'bookaro-settings', $shortcodes_js );

        $dependency_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var t=document.querySelector('.bookaro-admin');if(!t)return;var e=[].slice.call(t.querySelectorAll('[data-bookaro-setting]'));var r=[].slice.call(t.querySelectorAll('[data-bookaro-depends]'));var n=function(a){if(!a)return null;try{return JSON.parse(a)}catch(o){return null}};var i=function(a){if(!a)return'';if(a.type==='checkbox'){return a.checked}if(a.tagName==='SELECT'&&a.multiple){return[].slice.call(a.options).filter(function(o){return o.selected}).map(function(o){return o.value})}return a.value!==undefined?a.value:''};var s=function(a){var o=t.querySelector('[data-bookaro-setting="'+a+'"]');if(!o){return''}return i(o)};var l=function(a){var o=a.operator||'truthy';var d=s(a.field||'');var u=a.value;var c=function(m){return m===null||m===undefined?''+m:String(m)};if(o==='equals'){if(Array.isArray(d)){return d.map(c).indexOf(c(u))!==-1}return c(d)===c(u)}if(o==='not_equals'){if(Array.isArray(d)){return d.map(c).indexOf(c(u))===-1}return c(d)!==c(u)}if(o==='in'){var m=Array.isArray(u)?u:[u];m=m.map(c);if(Array.isArray(d)){return d.map(c).some(function(f){return m.indexOf(f)!==-1})}return m.indexOf(c(d))!==-1}if(o==='not_in'){var p=Array.isArray(u)?u:[u];p=p.map(c);if(Array.isArray(d)){return d.map(c).every(function(f){return p.indexOf(f)===-1})}return p.indexOf(c(d))===-1}if(o==='contains'){if(Array.isArray(d)){return d.map(c).indexOf(c(u))!==-1}if(typeof d==='string'){return d.indexOf(c(u))!==-1}return false}if(o==='falsy'){if(Array.isArray(d)){return d.length===0}return !d}if(Array.isArray(d)){return d.length>0}return !!d};var v=function(a){if(!a){return true}var o=a.rules||a;var d=a.mode||'all';if(!Array.isArray(o)||!o.length){return true}var u=o.map(l);return d==='any'?u.some(Boolean):u.every(Boolean)};var g=function(){var o=[].slice.call(t.querySelectorAll('.form-table'));if(!o.length){return}o.forEach(function(f){var h=[].slice.call(f.querySelectorAll('tr'));if(!h.length){return}h.forEach(function(p){p.classList.remove('bookaro-group-row','bookaro-group-start','bookaro-group-end')});var c=[];var m=function(){if(!c.length){return}c.forEach(function(p){p.classList.add('bookaro-group-row')});c[0].classList.add('bookaro-group-start');c[c.length-1].classList.add('bookaro-group-end');c=[]};h.forEach(function(p){if(p.classList.contains('bookaro-setting-hidden')){return}var y=p.classList.contains('bookaro-setting-heading');if(y&&c.length){m()}c.push(p)});m()})};var b=function(){r.forEach(function(a){var o=n(a.getAttribute('data-bookaro-depends'));var d=v(o);var u=a.closest('tr')||a;if(d){u.classList.remove('bookaro-setting-hidden')}else{u.classList.add('bookaro-setting-hidden')}});g()};e.forEach(function(a){a.addEventListener('change',b);a.addEventListener('input',b)});t.querySelectorAll('[data-bookaro-field-type]').forEach(function(a){var o=a.closest('tr');if(!o){return}var d=a.getAttribute('data-bookaro-field-type');if(d==='heading'){o.classList.add('bookaro-setting-heading')}if(d==='note'){o.classList.add('bookaro-setting-note')}if(d==='template'){o.classList.add('bookaro-setting-template')}});b()});
JS;
        wp_add_inline_script( 'bookaro-settings', $dependency_js );

        $tokens_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var t=[].slice.call(document.querySelectorAll('.bookaro-email-template'));if(!t.length)return;var r=function(e,o){var n=e.selectionStart;var i=e.selectionEnd;if(typeof n==='number'&&typeof i==='number'){var a=e.value||'';e.value=a.slice(0,n)+o+a.slice(i);var l=n+o.length;e.setSelectionRange(l,l)}else{e.value=(e.value||'')+o}};var s=function(e){if(!window.tinymce||!window.tinymce.activeEditor)return null;var o=window.tinymce.activeEditor;if(o.isHidden&&o.isHidden())return null;var n=o.getElement?o.getElement():document.getElementById(o.id||'');if(!n)return null;if(!e.contains(n))return null;return o};t.forEach(function(e){var o=null;e.addEventListener('focusin',function(n){var i=n.target;if(i&&((i.tagName==='INPUT'&&i.type==='text')||i.tagName==='TEXTAREA')){o=i}});e.querySelectorAll('.bookaro-token-chip').forEach(function(n){n.addEventListener('click',function(){var i=n.getAttribute('data-token')||'';if(!i)return;var a=s(e);if(a){a.focus();a.insertContent(i);return}var l=o;if(!l){l=e.querySelector('input[type="text"], textarea')}if(!l)return;var d=l.getAttribute('id')||'';if(window.tinymce&&d&&window.tinymce.get(d)){window.tinymce.get(d).focus();window.tinymce.get(d).insertContent(i);return}r(l,i);l.focus()})})})});
JS;
        wp_add_inline_script( 'bookaro-settings', $tokens_js );

        $email_preview_js = <<<'JS'
document.addEventListener('DOMContentLoaded',function(){var cards=[].slice.call(document.querySelectorAll('.bookaro-email-template'));if(!cards.length)return;var parseSamples=function(card){var raw=card.getAttribute('data-preview-samples')||'{}';try{var out=JSON.parse(raw);return out&&typeof out==='object'?out:{}}catch(e){return{}}};var replaceTokens=function(value,samples){var out=String(value||'');Object.keys(samples).forEach(function(key){var token='{'+key+'}';out=out.split(token).join(String(samples[key]||''))});return out};var toText=function(html){var tmp=document.createElement('div');tmp.innerHTML=String(html||'');return (tmp.textContent||tmp.innerText||'').trim()};var getBodyValue=function(card){var textarea=card.querySelector('textarea.bookaro-email-template__body-input, textarea[id]');if(!textarea){return''}var id=textarea.getAttribute('id')||'';if(window.tinymce&&id){var editor=window.tinymce.get(id);if(editor&&!editor.isHidden()){return editor.getContent()}}return textarea.value||''};cards.forEach(function(card){var toggle=card.querySelector('.bookaro-email-preview-toggle');var preview=card.querySelector('.bookaro-email-preview');var subjectInput=card.querySelector('.bookaro-email-template__subject-input');var subjectOutput=card.querySelector('.bookaro-email-preview__subject-output');var bodyOutput=card.querySelector('.bookaro-email-preview__body-output');if(!toggle||!preview||!subjectOutput||!bodyOutput){return}var samples=parseSamples(card);var update=function(){var subject=subjectInput?subjectInput.value:'';var body=getBodyValue(card);subjectOutput.textContent=replaceTokens(subject,samples);var renderedBody=replaceTokens(body,samples);if(renderedBody.trim()===''){bodyOutput.textContent='';return}bodyOutput.innerHTML='<div class="bookaro-email-preview__body-html"></div><div class="bookaro-email-preview__body-text"></div>';var htmlNode=bodyOutput.querySelector('.bookaro-email-preview__body-html');var textNode=bodyOutput.querySelector('.bookaro-email-preview__body-text');if(htmlNode){htmlNode.textContent=renderedBody}if(textNode){textNode.textContent=toText(renderedBody)}};toggle.addEventListener('click',function(){var hidden=preview.hasAttribute('hidden');if(hidden){preview.removeAttribute('hidden');update()}else{preview.setAttribute('hidden','hidden')}});if(subjectInput){subjectInput.addEventListener('input',update)}var textarea=card.querySelector('textarea.bookaro-email-template__body-input, textarea[id]');if(textarea){textarea.addEventListener('input',update);var id=textarea.getAttribute('id')||'';if(window.tinymce&&id){var bindEditor=function(){var editor=window.tinymce.get(id);if(!editor){setTimeout(bindEditor,250);return}editor.on('input change keyup SetContent',update)};bindEditor()}}})});
JS;
        wp_add_inline_script( 'bookaro-settings', $email_preview_js );

    }

    /**
     * Add our Settings submenu under "MyBookaro".
     */
    public function add_submenu_page(): void
    {
        add_submenu_page(
            'bookaro-bookings',
            __( 'Settings', 'mybookaro' ),
            __( 'Settings', 'mybookaro' ),
            $this->admin_menu_capability(),
            'bookaro-booking-settings',
            [ $this, 'render_page' ]
        );
    }

    /**
     * Register all settings sections and fields via configuration arrays.
     */
    public function register_settings(): void
    {
        register_setting(
            self::SETTINGS_GROUP,
            OptionRepository::OPTION_KEY,
            [ $this, 'sanitize_settings' ]
        );

        $context = $this->build_settings_context();
        $tabs = SettingsTabRegistry::build( $context );
        $tabs = apply_filters( 'bookaro_settings_register_tabs', $tabs, $context );
        $sections = [];
        $fields = [];

        foreach ( $tabs as $tab ) {
            $section = $tab->get_section();
            if ( ! empty( $section ) ) {
                $sections[] = $section;
            }
            $tab_fields = $tab->get_fields();
            if ( ! empty( $tab_fields ) ) {
                $fields = array_merge( $fields, $tab_fields );
            }
        }

        foreach ( $sections as $section ) {
            add_settings_section(
                $section['id'],
                $section['title'],
                [ $this, 'render_section_description' ],
                $section['page'],
                $section
            );
        }

        foreach ( $fields as $field ) {
            $label_raw = (string) ( $field['label'] ?? '' );
            $args = $field['args'];
            $args['id'] = $field['id'];
            $callback = $field['callback'];
            $field_id = $field['id'];

            if ( is_string( $callback ) && method_exists( $this, $callback ) ) {
                $callback_handler = [ $this, $callback ];
            } elseif ( is_callable( $callback ) ) {
                $callback_handler = $callback;
            } else {
                continue;
            }

            $label_display = $this->build_field_label( $label_raw, $args );
            add_settings_field(
                $field_id,
                $label_display,
                $callback_handler,
                $field['page'],
                $field['section'],
                $args
            );
        }
    }

    private function build_settings_context(): array
    {
        self::register_pro_only_strings();
        $roles = wp_roles()->get_names();
        $site_type = function_exists( 'bookaro_get_site_type_setting' )
            ? sanitize_key( (string) bookaro_get_site_type_setting() )
            : sanitize_key( (string) $this->options->get( 'site_type', 'directory' ) );
        if ( ! in_array( $site_type, [ 'directory', 'single_business', 'advanced_mapping' ], true ) ) {
            $site_type = 'directory';
        }
        $integration_mode = sanitize_key( (string) $this->options->get( 'integration_mode', 'auto' ) );
        if ( ! in_array( $integration_mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $integration_mode = 'auto';
        }
        if ( in_array( $site_type, [ 'single_business', 'advanced_mapping' ], true ) ) {
            $integration_mode = 'generic';
        }
        $default_resource_singular = ( 'single_business' === $site_type )
            ? __( 'Business', 'mybookaro' )
            : __( 'Listing', 'mybookaro' );
        $default_resource_plural = ( 'single_business' === $site_type )
            ? __( 'Services', 'mybookaro' )
            : __( 'Listings', 'mybookaro' );
        $resource_label_singular = sanitize_text_field( (string) $this->options->get( 'resource_label_singular', '' ) );
        if ( '' === $resource_label_singular ) {
            $resource_label_singular = $default_resource_singular;
        }
        $resource_label_plural = sanitize_text_field( (string) $this->options->get( 'resource_label_plural', '' ) );
        if ( '' === $resource_label_plural ) {
            $resource_label_plural = $default_resource_plural;
        }
        $default_listing_types = function_exists( 'bookaro_get_allowed_listing_post_types' )
            ? bookaro_get_allowed_listing_post_types()
            : [ 'job_listing' ];
        $listing_post_types = $this->get_listing_post_type_options();
        $listing_pages = $this->get_listing_page_options();
        $provider_users = $this->get_provider_user_options();
        $email_templates = [
            'new_booking'   => __( 'New Booking', 'mybookaro' ),
            'approved'      => __( 'Booking Approved', 'mybookaro' ),
            'rejected'      => __( 'Booking Rejected', 'mybookaro' ),
            'cancelled'     => __( 'Booking Cancelled', 'mybookaro' ),
            'rescheduled'   => __( 'Booking Rescheduled', 'mybookaro' ),
            'reminder_24h'  => __( '24h Reminder', 'mybookaro' ),
            'reminder_1h'   => __( '1h Reminder', 'mybookaro' ),
            'review_invite' => __( 'Review Invite', 'mybookaro' ),
        ];
        $email_default_subjects = [];
        $email_default_bodies = [];
        foreach ( $email_templates as $key => $label ) {
            $email_default_subjects[ $key ] = $this->get_default_subject( $key );
            $email_default_bodies[ $key ] = $this->get_default_body( $key );
        }

        return [
            'roles' => $roles,
            'site_type' => $site_type,
            'integration_mode' => $integration_mode,
            'resource_label_singular' => $resource_label_singular,
            'resource_label_plural' => $resource_label_plural,
            'default_listing_types' => $default_listing_types,
            'listing_post_types' => $listing_post_types,
            'listing_pages' => $listing_pages,
            'provider_users' => $provider_users,
            'email_templates' => $email_templates,
            'email_default_subjects' => $email_default_subjects,
            'email_default_bodies' => $email_default_bodies,
        ];
    }

    public function render_section_description( array $section ): void
    {
        $desc = $section['desc'] ?? '';
        if ( $desc !== '' ) {
            echo '<p>' . esc_html( $desc ) . '</p>';
        }
    }

    private function build_field_label( string $label, array $args ): string
    {
        $label = trim( $label );
        if ( $label === '' ) {
            return '';
        }
        $field_type = (string) ( $args['field_type'] ?? '' );
        if ( in_array( $field_type, [ 'heading', 'note' ], true ) ) {
            return '';
        }
        $label_text = esc_html( $label );
        $tooltip_text = $this->build_tooltip_text_from_args( $args );
        if ( $tooltip_text === '' ) {
            return $label_text;
        }
        return sprintf(
            '<span class="bookaro-field-label"><span class="bookaro-field-label__text">%1$s</span>%2$s</span>',
            $label_text,
            $this->build_tooltip_icon_html( $tooltip_text )
        );
    }

    private function build_tooltip_text_from_args( array $args ): string
    {
        $tooltip = '';
        if ( isset( $args['tooltip'] ) && is_string( $args['tooltip'] ) ) {
            $tooltip = trim( $args['tooltip'] );
        }
        if ( $tooltip === '' && isset( $args['description'] ) && is_string( $args['description'] ) ) {
            $tooltip = trim( $args['description'] );
        }
        if ( $tooltip === '' ) {
            return '';
        }

        $example = '';
        if ( isset( $args['tooltip_example'] ) && is_string( $args['tooltip_example'] ) ) {
            $example = trim( $args['tooltip_example'] );
        }
        if ( $example === '' ) {
            $example = $this->build_tooltip_auto_example( $args );
        }

        return $this->build_tooltip_text( $tooltip, $example );
    }

    private function build_tooltip_text( string $tooltip, string $example = '' ): string
    {
        $tooltip = trim( wp_strip_all_tags( $tooltip ) );
        if ( $tooltip === '' ) {
            return '';
        }

        $example = trim( wp_strip_all_tags( $example ) );
        if ( $example !== '' ) {
            if ( stripos( $example, 'example:' ) !== 0 ) {
                $example = sprintf(
                    /* translators: %s: tooltip usage example */
                    __( 'Example: %s', 'mybookaro' ),
                    $example
                );
            }
            return $tooltip . "\n" . $example;
        }

        return $tooltip;
    }

    private function build_tooltip_auto_example( array $args ): string
    {
        $default = $args['default'] ?? null;
        if ( isset( $args['options'] ) && is_array( $args['options'] ) && ! empty( $args['options'] ) ) {
            $options = $args['options'];
            $sample_key = null;
            if ( is_string( $default ) || is_int( $default ) ) {
                $candidate_key = (string) $default;
                if ( array_key_exists( $candidate_key, $options ) ) {
                    $sample_key = $candidate_key;
                } elseif ( is_int( $default ) && array_key_exists( $default, $options ) ) {
                    $sample_key = $default;
                }
            }
            if ( $sample_key === null ) {
                $keys = array_keys( $options );
                $sample_key = $keys[0] ?? null;
            }
            if ( $sample_key !== null && array_key_exists( $sample_key, $options ) ) {
                $sample_label = wp_strip_all_tags( (string) $options[ $sample_key ] );
                if ( $sample_label !== '' ) {
                    return sprintf(
                        /* translators: %s: option label */
                        __( 'Set this to "%s" and save to apply that mode.', 'mybookaro' ),
                        $sample_label
                    );
                }
            }
        }

        if ( is_numeric( $default ) ) {
            return sprintf(
                /* translators: %s: numeric default value */
                __( 'Set this to %s and save to apply that threshold.', 'mybookaro' ),
                (string) $default
            );
        }

        if ( is_bool( $default ) || $default === 0 || $default === 1 || $default === '0' || $default === '1' ) {
            return __( 'Turn this on and save to enable this behavior.', 'mybookaro' );
        }

        if ( is_string( $default ) ) {
            $default = trim( $default );
            if ( $default !== '' ) {
                return sprintf(
                    /* translators: %s: sample setting value */
                    __( 'Enter a value like "%s" and save to apply it.', 'mybookaro' ),
                    $default
                );
            }
        }

        return __( 'Change this setting and save to apply it immediately.', 'mybookaro' );
    }

    private function build_tooltip_icon_html( string $tooltip_text ): string
    {
        $tooltip_text = trim( wp_strip_all_tags( $tooltip_text ) );
        if ( $tooltip_text === '' ) {
            return '';
        }
        return sprintf(
            '<span class="bookaro-tooltip" tabindex="0" role="button" aria-label="%1$s" data-tooltip="%1$s">i</span>',
            esc_attr( $tooltip_text )
        );
    }

    private function render_field_wrapper_open( array $args ): void
    {
        $attrs = [
            'class' => 'bookaro-setting-field',
        ];
        if ( ! empty( $args['wrapper_class'] ) && is_string( $args['wrapper_class'] ) ) {
            $attrs['class'] .= ' ' . $args['wrapper_class'];
        }
        $field_type = (string) ( $args['field_type'] ?? '' );
        if ( $field_type !== '' ) {
            $attrs['data-bookaro-field-type'] = $field_type;
        }
        if ( ! empty( $args['depends'] ) ) {
            $depends = $args['depends'];
            if ( is_array( $depends ) && ! isset( $depends['rules'] ) ) {
                $depends = [
                    'mode' => 'all',
                    'rules' => $depends,
                ];
            }
            if ( is_array( $depends ) ) {
                $attrs['data-bookaro-depends'] = wp_json_encode( $depends );
            }
        }
        // format_html_attributes() runs esc_attr() on every key and value internally
        // (see definition below). Plugin Check can't trace through the helper.
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        echo '<div ' . $this->format_html_attributes( $attrs ) . '>';
    }

    private function render_field_wrapper_close(): void
    {
        echo '</div>';
    }

    private function format_html_attributes( array $attrs ): string
    {
        $pairs = [];
        foreach ( $attrs as $key => $value ) {
            if ( $value === null || $value === '' ) {
                continue;
            }
            $pairs[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( (string) $value ) );
        }
        return implode( ' ', $pairs );
    }

    private function get_listing_post_type_options(): array
    {
        $options = [];
        $post_types = get_post_types([], 'objects');
        foreach ( $post_types as $slug => $obj ) {
            if ( ! $obj ) {
                continue;
            }
            $is_visible = ! empty( $obj->public ) || ! empty( $obj->show_ui );
            if ( ! $is_visible ) {
                continue;
            }
            if ( in_array( $slug, [ 'attachment', BOOKARO_SERVICE_POST_TYPE ], true ) ) {
                continue;
            }
            $label = $obj->labels->singular_name ?? $obj->label ?? $slug;
            $options[ $slug ] = $label;
        }
        asort( $options );
        $mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : 'mylisting';
        $blocked = apply_filters( 'bookaro_blocked_listing_post_types', [ 'job_listing' ], $mode );
        if ( is_array( $blocked ) && ! empty( $blocked ) ) {
            foreach ( $blocked as $slug ) {
                $slug = sanitize_key( $slug );
                if ( isset( $options[ $slug ] ) ) {
                    unset( $options[ $slug ] );
                }
            }
        }
        return $options;
    }

    private function get_listing_page_options(): array
    {
        $options = [];
        $pages = get_posts( [
            'post_type'      => 'page',
            'post_status'    => [ 'publish', 'private', 'draft' ],
            'posts_per_page' => -1,
            'orderby'        => 'title',
            'order'          => 'ASC',
            'fields'         => 'ids',
        ] );
        foreach ( $pages as $page_id ) {
            $page_id = (int) $page_id;
            if ( $page_id < 1 ) {
                continue;
            }
            $title = get_the_title( $page_id );
            if ( $title === '' ) {
                /* translators: %d: numeric value, %s: dynamic string */
                $title = sprintf( __( 'Page #%d', 'mybookaro' ), $page_id );
            }
            $options[ (string) $page_id ] = sprintf( '%s (#%d)', $title, $page_id );
        }
        return $options;
    }

    private function get_provider_user_options(): array
    {
        $options = [ '' => __( 'Select a provider', 'mybookaro' ) ];

        $selected_default_provider_id = absint( $this->options->get( 'default_provider_id', 0 ) );
        $provider_role = sanitize_key( (string) $this->options->get( 'provider_role', '' ) );
        $args = [
            'orderby' => 'display_name',
            'order' => 'ASC',
            'fields' => [ 'ID', 'display_name', 'user_email', 'user_login' ],
            'number' => -1,
        ];
        if ( $provider_role !== '' ) {
            $args['role__in'] = [ $provider_role ];
        }

        $users = get_users( $args );
        foreach ( $users as $user ) {
            if ( ! $user || empty( $user->ID ) ) {
                continue;
            }
            $name = $user->display_name ?: $user->user_login;
            $label = $name;
            if ( ! empty( $user->user_email ) ) {
                $label .= ' (' . $user->user_email . ')';
            }
            $options[ (string) $user->ID ] = $label;
        }

        if ( $selected_default_provider_id > 0 && ! isset( $options[ (string) $selected_default_provider_id ] ) ) {
            $selected_user = get_user_by( 'ID', $selected_default_provider_id );
            if ( $selected_user ) {
                $selected_label = $selected_user->display_name ?: $selected_user->user_login;
                if ( ! empty( $selected_user->user_email ) ) {
                    $selected_label .= ' (' . $selected_user->user_email . ')';
                }
                $options[ (string) $selected_default_provider_id ] = $selected_label;
            }
        }

        return $options;
    }

    private function get_default_subject( string $key ): string {
        $defaults = [
            'new_booking'  => __( 'New booking request: {service}', 'mybookaro' ),
            'approved'     => __( 'Your booking is approved',       'mybookaro' ),
            'rejected'     => __( 'Your booking is rejected',       'mybookaro' ),
            'cancelled'    => __( 'Booking cancelled',             'mybookaro' ),
            'rescheduled'  => __( 'Booking rescheduled: {service}', 'mybookaro' ),
            'reminder_24h' => __( 'Reminder: Your booking is tomorrow', 'mybookaro' ),
            'reminder_1h'  => __( 'Reminder: Your booking is in 1 hour', 'mybookaro' ),
            'review_invite'=> __( 'Please leave a review for your recent booking', 'mybookaro' ),
        ];
        return $defaults[ $key ] ?? '';
    }

    private function get_default_body( string $key ): string {
        $bodies = [
            'new_booking'  => __( "Hello {provider},\n\nYou have a new booking for \"{service}\" on {date} from customer {owner} ({email}).", 'mybookaro' ),
            'approved'     => __( "Hello {owner},\n\nYour booking for \"{service}\" on {date} has been approved by {provider}.", 'mybookaro' ),
            'rejected'     => __( "Hello {owner},\n\nWe're sorry - your booking for \"{service}\" on {date} was rejected by {provider}.", 'mybookaro' ),
            'cancelled'    => __( "Hello {owner},\n\nYour booking for \"{service}\" on {date} was cancelled. Reason: {reason}", 'mybookaro' ),
            'rescheduled'  => __( "Hello {recipient},\n\nThe booking for \"{service}\" was rescheduled by {actor}. New time: {date_range}. Previous time: {old_date_range}.", 'mybookaro' ),
            'reminder_24h' => __( "Hello {owner},\n\nJust a reminder: your booking for \"{service}\" is tomorrow at {date}.", 'mybookaro' ),
            'reminder_1h'  => __( "Hello {owner},\n\nJust a heads-up: your booking for \"{service}\" is in one hour at {date}.", 'mybookaro' ),
            'review_invite'=> __( "Hi {owner_name},\n\nThanks for completing your booking on {booking_date}! Please leave us a review: {review_link}", 'mybookaro' ),
        ];
        return $bodies[ $key ] ?? '';
    }

    /**
     * Pro-only strings kept in Lite for translations.
     */
    private static function register_pro_only_strings(): void
    {
        __( 'Chat Message', 'mybookaro' );
        __( 'New chat message from {sender}', 'mybookaro' );
        __( "Hello {recipient},\n\n{sender} sent you a new message about \"{service}\" on {date}:\n\n{message}\n\nOpen the chat: {chat_url}", 'mybookaro' );
    }

    public function render_select_field(array $args): void
    {
        $opts    = $this->options->get_all();
        $id      = $args['id'];
        $val     = $opts[$id] ?? ($args['default'] ?? '');
        if ( 'services_model' === $id && ( '' === $val || 'admin_catalog' === $val ) ) {
            $legacy = $opts['generic_services_mode'] ?? '';
            if ( 'admin_catalog' === $legacy ) {
                $legacy = 'admin_only';
            }
            if ( in_array( $legacy, [ 'admin_only', 'provider_only', 'hybrid' ], true ) ) {
                $val = $legacy;
            }
        }
        $options = $args['options'] ?? [];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );

        $this->render_field_wrapper_open( $args );
        echo '<select name="' . esc_attr(OptionRepository::OPTION_KEY) . '[' . esc_attr($id) . ']" class="' . esc_attr( $class ) . '" data-bookaro-setting="' . esc_attr( $id ) . '"' . disabled( $disabled, true, false ) . '>';
        foreach ($options as $key => $label) {
            printf(
                '<option value="%s"%s>%s</option>',
                esc_attr($key),
                selected($val, $key, false),
                esc_html($label)
            );
        }
        echo '</select>';
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    public function render_shortcodes_field( array $args ): void
    {
        $opts = $this->options->get_all();
        $ui_mode = $this->resolve_ui_mode_from_options( $opts );
        $resource_label_singular = function_exists( 'bookaro_get_resource_label_singular' )
            ? sanitize_text_field( (string) bookaro_get_resource_label_singular() )
            : __( 'Listing', 'mybookaro' );
        if ( '' === $resource_label_singular ) {
            $resource_label_singular = __( 'Listing', 'mybookaro' );
        }
        $resource_label_plural = function_exists( 'bookaro_get_resource_label_plural' )
            ? sanitize_text_field( (string) bookaro_get_resource_label_plural() )
            : __( 'Listings', 'mybookaro' );
        if ( '' === $resource_label_plural ) {
            $resource_label_plural = __( 'Listings', 'mybookaro' );
        }
        $resource_label_singular_lower = function_exists( 'bookaro_get_resource_label_singular_lower' )
            ? sanitize_text_field( (string) bookaro_get_resource_label_singular_lower() )
            : strtolower( $resource_label_singular );
        if ( '' === $resource_label_singular_lower ) {
            $resource_label_singular_lower = $resource_label_singular;
        }
        $resource_label_plural_lower = function_exists( 'bookaro_get_resource_label_plural_lower' )
            ? sanitize_text_field( (string) bookaro_get_resource_label_plural_lower() )
            : strtolower( $resource_label_plural );
        if ( '' === $resource_label_plural_lower ) {
            $resource_label_plural_lower = $resource_label_plural;
        }
        $site_type = function_exists( 'bookaro_get_site_type' )
            ? sanitize_key( (string) bookaro_get_site_type() )
            : sanitize_key( (string) ( $opts['site_type'] ?? 'directory' ) );
        $is_single_business = ( 'single_business' === $site_type );
        $default_provider_id = absint( $opts['default_provider_id'] ?? 0 );
        if ( $default_provider_id > 0 && ! get_user_by( 'ID', $default_provider_id ) ) {
            $default_provider_id = 0;
        }
        $selected_provider_value = ( $is_single_business && $default_provider_id > 0 )
            ? (string) $default_provider_id
            : '';

        $this->render_field_wrapper_open( $args );
        if ( 'shortcodes' !== $ui_mode ) {
            echo '<p class="description">' .
                esc_html__( 'UI mode is WooCommerce; dashboards live in My Account. Switch to Shortcodes to use the embeds below.', 'mybookaro' ) .
                '</p>';
        }

        echo '<h4 style="margin:16px 0 6px;">' . esc_html__( 'Generator (Shortcodes + Blocks)', 'mybookaro' ) . '</h4>';
        $listing_options = [
            '' => sprintf(
                /* translators: %s: lowercase resource label singular */
                __( 'Select a %s', 'mybookaro' ),
                $resource_label_singular_lower
            ),
        ];
        $listing_types = function_exists( 'bookaro_get_allowed_listing_post_types' ) ? bookaro_get_allowed_listing_post_types() : [];
        if ( ! empty( $listing_types ) ) {
            $listing_posts = get_posts( [
                'post_type'      => $listing_types,
                'post_status'    => 'publish',
                'posts_per_page' => 50,
                'orderby'        => 'date',
                'order'          => 'DESC',
            ] );
            $allowed_pages = function_exists( 'bookaro_get_allowed_listing_pages' ) ? bookaro_get_allowed_listing_pages() : [];
            foreach ( $listing_posts as $listing_post ) {
                if ( ! $listing_post instanceof \WP_Post ) {
                    continue;
                }
                if ( $listing_post->post_type === 'page' && ! empty( $allowed_pages ) ) {
                    if ( ! in_array( (int) $listing_post->ID, $allowed_pages, true ) ) {
                        continue;
                    }
                }
                $label = $listing_post->post_title !== '' ? $listing_post->post_title : sprintf(
                    /* translators: 1: resource label singular, 2: post ID */
                    __( '%1$s #%2$d', 'mybookaro' ),
                    $resource_label_singular,
                    $listing_post->ID
                );
                $listing_options[ (string) $listing_post->ID ] = sprintf( '%s (#%d, %s)', $label, $listing_post->ID, $listing_post->post_type );
            }
        }
        $provider_options = $this->get_provider_user_options();

        echo '<div class="bookaro-shortcode-generator">';
        echo '<div class="bookaro-generator-grid">';
        echo '<label>' . esc_html( $resource_label_singular ) . '<br />';
        echo '<select data-bookaro-field="listing_id" class="regular-text">';
        foreach ( $listing_options as $value => $label ) {
            echo '<option value="' . esc_attr( $value ) . '"' . selected( (string) $value, '', false ) . '>' . esc_html( $label ) . '</option>';
        }
        echo '</select></label>';

        echo '<label>' . esc_html__( 'Provider', 'mybookaro' ) . '<br />';
        echo '<select data-bookaro-field="provider_id" class="regular-text">';
        foreach ( $provider_options as $value => $label ) {
            echo '<option value="' . esc_attr( $value ) . '"' . selected( (string) $value, $selected_provider_value, false ) . '>' . esc_html( $label ) . '</option>';
        }
        echo '</select></label>';

        echo '<label>' . esc_html__( 'Service Label', 'mybookaro' ) . '<br />';
        echo '<input type="text" data-bookaro-field="service_label" class="regular-text" placeholder="' . esc_attr__( 'Consultation', 'mybookaro' ) . '"></label>';

        echo '<label>' . esc_html__( 'Price', 'mybookaro' ) . '<br />';
        echo '<input type="number" data-bookaro-field="price" class="small-text" placeholder="49" step="0.01" min="0"></label>';

        echo '<label>' . esc_html__( 'Layout', 'mybookaro' ) . '<br />';
        echo '<select data-bookaro-field="layout" class="regular-text">';
        echo '<option value="grid">' . esc_html__( 'Grid', 'mybookaro' ) . '</option>';
        echo '<option value="list">' . esc_html__( 'List', 'mybookaro' ) . '</option>';
        echo '</select></label>';

        echo '<label>' . esc_html__( 'Columns', 'mybookaro' ) . '<br />';
        echo '<select data-bookaro-field="columns" class="regular-text">';
        for ( $i = 1; $i <= 6; $i++ ) {
            $selected = selected( 3, $i, false );
            // $selected is the output of selected() which returns either ' selected="selected"' or '' (safe HTML).
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo '<option value="' . esc_attr( $i ) . '"' . $selected . '>' . esc_html( (string) $i ) . '</option>';
        }
        echo '</select></label>';
        echo '</div>';

        echo '<p class="description" style="margin:8px 0 0;">' . esc_html__( 'Use Copy to grab shortcodes or block JSON.', 'mybookaro' ) . '</p>';

        echo '<div class="bookaro-generator-output">';
        $generator_rows = [
            'booking_button' => __( 'Booking button shortcode', 'mybookaro' ),
            'booking_form' => __( 'Booking form shortcode', 'mybookaro' ),
            'services_cards' => __( 'Services cards shortcode', 'mybookaro' ),
            'provider_dashboard' => __( 'Provider dashboard shortcode', 'mybookaro' ),
            'customer_dashboard' => __( 'Customer dashboard shortcode', 'mybookaro' ),
        ];
        foreach ( $generator_rows as $key => $label ) {
            echo '<div class="bookaro-generator-row">';
            echo '<div class="description">' . esc_html( $label ) . '</div>';
            echo '<div class="bookaro-generator-field">';
            echo '<input type="text" class="large-text code" readonly data-bookaro-output="' . esc_attr( $key ) . '">';
            echo '<button type="button" class="button" data-bookaro-copy="' . esc_attr( $key ) . '" data-bookaro-label="' . esc_attr__( 'Copy', 'mybookaro' ) . '" data-bookaro-copied="' . esc_attr__( 'Copied', 'mybookaro' ) . '">' . esc_html__( 'Copy', 'mybookaro' ) . '</button>';
            echo '</div></div>';
        }
        echo '</div>';

        echo '<div class="bookaro-generator-output">';
        echo '<h4 style="margin:0 0 6px;">' . esc_html__( 'Blocks (name + attributes)', 'mybookaro' ) . '</h4>';
        echo '<p class="description" style="margin:0 0 10px;">' . esc_html__( 'Paste these into the block editor or reusable patterns.', 'mybookaro' ) . '</p>';
        $block_rows = [
            'block_booking_button' => __( 'Booking button block', 'mybookaro' ),
            'block_booking_form' => __( 'Booking form block', 'mybookaro' ),
            'block_services_cards' => __( 'Services cards block', 'mybookaro' ),
            'block_provider_dashboard' => __( 'Provider dashboard block', 'mybookaro' ),
            'block_customer_dashboard' => __( 'Customer dashboard block', 'mybookaro' ),
        ];
        foreach ( $block_rows as $key => $label ) {
            echo '<div class="bookaro-generator-row">';
            echo '<div class="description">' . esc_html( $label ) . '</div>';
            echo '<div class="bookaro-generator-field">';
            echo '<textarea class="large-text code" rows="4" readonly data-bookaro-block="' . esc_attr( $key ) . '"></textarea>';
            echo '<button type="button" class="button" data-bookaro-copy-block="' . esc_attr( $key ) . '" data-bookaro-label="' . esc_attr__( 'Copy', 'mybookaro' ) . '" data-bookaro-copied="' . esc_attr__( 'Copied', 'mybookaro' ) . '">' . esc_html__( 'Copy', 'mybookaro' ) . '</button>';
            echo '</div></div>';
        }
        echo '<p class="description" style="margin-top:6px;">' . esc_html__( 'Tip: search for these blocks in the block editor under the “MyBookaro” category.', 'mybookaro' ) . '</p>';
        echo '</div>';

        echo '</div>';

        echo '<h4 style="margin:20px 0 6px;">' . esc_html__( 'Shortcodes', 'mybookaro' ) . '</h4>';
        echo '<ul class="list-disc pl-5 text-sm text-slate-700" style="margin: 8px 0 0 0;">';
        echo '<li><code>[bookaro_provider_dashboard]</code> - ' . esc_html__( 'Provider dashboard', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_customer_dashboard]</code> - ' . esc_html__( 'Customer dashboard', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_booking_button listing_id=&quot;123&quot;]</code> - ' . esc_html__( 'Booking button (directory listing)', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_booking_button provider_id=&quot;45&quot;]</code> - ' . esc_html__( 'Booking button (single-business / provider)', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_booking_form listing_id=&quot;123&quot; auto_open=&quot;false&quot;]</code> - ' . esc_html__( 'Booking form (auto_open default: true)', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_services_cards listing_id=&quot;123&quot; layout=&quot;grid&quot; columns=&quot;3&quot; show_price=&quot;1&quot; show_duration=&quot;1&quot;]</code> - ' . esc_html__( 'Services cards', 'mybookaro' ) . '</li>';
        echo '<li><code>[bookaro_services_cards provider_id=&quot;45&quot; source=&quot;provider&quot;]</code> - ' . esc_html__( 'Services cards (provider-only)', 'mybookaro' ) . '</li>';
        echo '</ul>';
        echo '<p class="description">' .
            esc_html__( 'Optional attributes: provider_id, service_id, service_label/label, price, duration, button_label/button_text, class. Services cards: provider_id, source/mode, layout, columns, show_price, show_duration.', 'mybookaro' ) .
            '</p>';

        echo '<div style="margin-top:16px;">';
        echo '<strong>' . esc_html__( 'Copy-ready examples', 'mybookaro' ) . '</strong>';
        echo '<div style="display:grid; gap:10px; margin-top:8px; max-width:820px;">';

        $snippet_provider_id = $default_provider_id > 0 ? $default_provider_id : 45;
        if ( $is_single_business ) {
            $snippets = [
                [
                    'label' => __( 'Booking button (single business)', 'mybookaro' ),
                    'code'  => sprintf( '[bookaro_booking_button provider_id="%d" service_label="Consultation" price="49" duration="30"]', $snippet_provider_id ),
                ],
                [
                    'label' => __( 'Services cards (single business)', 'mybookaro' ),
                    'code'  => sprintf( '[bookaro_services_cards provider_id="%d" source="provider" columns="3" show_price="1" show_duration="1"]', $snippet_provider_id ),
                ],
                [
                    'label' => __( 'Booking form (provider context)', 'mybookaro' ),
                    'code'  => sprintf( '[bookaro_booking_form provider_id="%d" auto_open="true"]', $snippet_provider_id ),
                ],
                [
                    'label' => __( 'Provider dashboard', 'mybookaro' ),
                    'code'  => '[bookaro_provider_dashboard]',
                ],
                [
                    'label' => __( 'Customer dashboard', 'mybookaro' ),
                    'code'  => '[bookaro_customer_dashboard]',
                ],
                [
                    'label' => __( 'Booking button (directory listing)', 'mybookaro' ),
                    'code'  => '[bookaro_booking_button listing_id="123"]',
                ],
            ];
        } else {
            $snippets = [
                [
                    'label' => __( 'Booking button (with service + price)', 'mybookaro' ),
                    'code'  => '[bookaro_booking_button listing_id="123" provider_id="45" service_label="Consultation" price="49" button_label="Book now"]',
                ],
                [
                    'label' => __( 'Booking button (single-business)', 'mybookaro' ),
                    'code'  => '[bookaro_booking_button provider_id="45" service_label="Consultation" price="49" duration="30"]',
                ],
                [
                    'label' => __( 'Booking button (directory listing)', 'mybookaro' ),
                    'code'  => '[bookaro_booking_button listing_id="123"]',
                ],
                [
                    'label' => __( 'Booking form (auto-open)', 'mybookaro' ),
                    'code'  => '[bookaro_booking_form listing_id="123" provider_id="45" auto_open="true"]',
                ],
                [
                    'label' => __( 'Services cards (grid)', 'mybookaro' ),
                    'code'  => '[bookaro_services_cards listing_id="123" columns="3" show_price="1" show_duration="1" source="auto"]',
                ],
                [
                    'label' => __( 'Services cards (provider-only)', 'mybookaro' ),
                    'code'  => '[bookaro_services_cards provider_id="45" source="provider"]',
                ],
                [
                    'label' => __( 'Provider dashboard', 'mybookaro' ),
                    'code'  => '[bookaro_provider_dashboard]',
                ],
                [
                    'label' => __( 'Customer dashboard', 'mybookaro' ),
                    'code'  => '[bookaro_customer_dashboard]',
                ],
            ];
        }

        foreach ( $snippets as $snippet ) {
            echo '<div>';
            echo '<div class="description" style="margin-bottom:4px;">' . esc_html( $snippet['label'] ) . '</div>';
            printf(
                '<input type="text" class="large-text code" readonly value="%s" onclick="this.select();">',
                esc_attr( $snippet['code'] )
            );
            echo '</div>';
        }

        echo '</div>';
        echo '</div>';

        echo '<h4 style="margin:20px 0 6px;">' . esc_html__( 'Blocks (Gutenberg editor)', 'mybookaro' ) . '</h4>';
        echo '<p class="description" style="margin:0 0 8px;">' . esc_html__( 'Use the "MyBookaro" block category to insert booking UI without shortcodes.', 'mybookaro' ) . '</p>';
        echo '<ul class="list-disc pl-5 text-sm text-slate-700" style="margin: 0 0 10px 0;">';
        echo '<li>' . esc_html__( 'MyBookaro: Booking Button', 'mybookaro' ) . '</li>';
        echo '<li>' . esc_html__( 'MyBookaro: Booking Form', 'mybookaro' ) . '</li>';
        echo '<li>' . esc_html__( 'MyBookaro: Provider Dashboard', 'mybookaro' ) . '</li>';
        echo '<li>' . esc_html__( 'MyBookaro: Customer Dashboard', 'mybookaro' ) . '</li>';
        echo '<li>' . esc_html__( 'MyBookaro: Services Cards', 'mybookaro' ) . '</li>';
        echo '</ul>';

        echo '<h4 style="margin:20px 0 6px;">' . esc_html__( 'Resolved pages', 'mybookaro' ) . '</h4>';
        $mode = function_exists( 'bookaro_get_integration_mode' ) ? bookaro_get_integration_mode() : 'auto';
        $post_types = function_exists( 'bookaro_get_allowed_listing_post_types' ) ? bookaro_get_allowed_listing_post_types() : [];
        if ( empty( $post_types ) ) {
            echo '<p class="description">' . esc_html(
                $is_single_business
                    ? sprintf(
                        /* translators: %s: resource label singular */
                        __( '%s post types are optional in Single Business mode. Global page overrides are used when post types are empty.', 'mybookaro' ),
                        $resource_label_singular
                    )
                    : sprintf(
                        /* translators: %s: lowercase resource label plural */
                        __( 'No %s post types selected yet. Configure them in Settings > Setup.', 'mybookaro' ),
                        $resource_label_plural_lower
                    )
            ) . '</p>';
            $this->render_field_wrapper_close();
            return;
        }

        echo '<table class="widefat striped" style="max-width:820px;">';
        echo '<thead><tr>';
        echo '<th>' . esc_html(
            sprintf(
                /* translators: %s: resource label singular */
                __( '%s Type', 'mybookaro' ),
                $resource_label_singular
            )
        ) . '</th>';
        echo '<th>' . esc_html__( 'Booking Page', 'mybookaro' ) . '</th>';
        echo '<th>' . esc_html__( 'Provider Dashboard', 'mybookaro' ) . '</th>';
        echo '<th>' . esc_html__( 'Customer Dashboard', 'mybookaro' ) . '</th>';
        echo '</tr></thead><tbody>';

        foreach ( $post_types as $post_type ) {
            $post_type = sanitize_key( (string) $post_type );
            if ( $post_type === '' ) {
                continue;
            }
            $label = $post_type;
            $obj = function_exists( 'get_post_type_object' ) ? get_post_type_object( $post_type ) : null;
            if ( $obj && ! empty( $obj->labels->singular_name ) ) {
                $label = $obj->labels->singular_name;
            }

            $pages = [
                'booking' => function_exists( 'bookaro_get_page_for_context' )
                    ? bookaro_get_page_for_context( $mode, $post_type, 'booking' )
                    : 0,
                'provider_dashboard' => function_exists( 'bookaro_get_page_for_context' )
                    ? bookaro_get_page_for_context( $mode, $post_type, 'provider_dashboard' )
                    : 0,
                'customer_dashboard' => function_exists( 'bookaro_get_page_for_context' )
                    ? bookaro_get_page_for_context( $mode, $post_type, 'customer_dashboard' )
                    : 0,
            ];

            echo '<tr>';
            echo '<td><strong>' . esc_html( $label ) . '</strong><div class="description">' . esc_html( $post_type ) . '</div></td>';
            foreach ( [ 'booking', 'provider_dashboard', 'customer_dashboard' ] as $context ) {
                $page_id = absint( $pages[ $context ] ?? 0 );
                echo '<td>';
                if ( $page_id > 0 && get_post( $page_id ) ) {
                    $view = get_permalink( $page_id );
                    $edit = get_edit_post_link( $page_id, '' );
                    $links = [];
                    if ( $view ) {
                        $links[] = '<a href="' . esc_url( $view ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'View', 'mybookaro' ) . '</a>';
                    }
                    if ( $edit ) {
                        $links[] = '<a href="' . esc_url( $edit ) . '">' . esc_html__( 'Edit', 'mybookaro' ) . '</a>';
                    }
                    // Each $links entry was built above with esc_url() + esc_html__() escaping; this is just gluing them with literal ' | '.
                    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                    echo ! empty( $links ) ? implode( ' | ', $links ) : esc_html__( 'Set', 'mybookaro' );
                } else {
                    echo '<span class="description">' . esc_html__( 'Not set', 'mybookaro' ) . '</span>';
                }
                echo '</td>';
            }
            echo '</tr>';
        }

        echo '</tbody></table>';
        $this->render_field_wrapper_close();
    }

    /**
     * Render a multi-select field.
     */
    public function render_multiselect_field( array $args ): void
    {
        $opts    = $this->options->get_all();
        $id      = $args['id'];
        $val     = $opts[ $id ] ?? ( $args['default'] ?? [] );
        $options = $args['options'] ?? [];
        $size    = isset( $args['size'] ) ? (int) $args['size'] : 5;
        $search_placeholder = (string) ( $args['search_placeholder'] ?? __( 'Search…', 'mybookaro' ) );
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );
        $search_class = $this->input_base_class( 'bookaro-multiselect-search' );

        if ( ! is_array( $val ) ) {
            $val = [ (string) $val ];
        }

        $this->render_field_wrapper_open( $args );
        echo '<div class="bookaro-multiselect-wrapper">';
        printf(
            '<label class="screen-reader-text" for="bookaro-multiselect-%1$s">%2$s</label>',
            esc_attr( $id ),
            esc_html__( 'Search options', 'mybookaro' )
        );
        printf(
            '<input type="search" class="%1$s" placeholder="%2$s" %3$s>',
            esc_attr( $search_class ),
            esc_attr( $search_placeholder ),
            disabled( $disabled, true, false )
        );

        echo '<select multiple="multiple" id="bookaro-multiselect-' . esc_attr( $id ) . '" size="' . esc_attr( $size ) . '" name="'
            . esc_attr( OptionRepository::OPTION_KEY ) . '[' . esc_attr( $id ) . '][]"'
            . ' class="bookaro-multiselect ' . esc_attr( $class ) . '" data-bookaro-setting="' . esc_attr( $id ) . '"' . disabled( $disabled, true, false ) . '>';
        foreach ( $options as $key => $label ) {
            printf(
                '<option value="%s"%s>%s</option>',
                esc_attr( $key ),
                selected( in_array( $key, $val, true ), true, false ),
                esc_html( $label )
            );
        }
        echo '</select>';
        echo '</div>';
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . wp_kses_post( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render per-post-type page override selectors.
     */
    public function render_listing_page_overrides_field( array $args ): void
    {
        $opts = $this->options->get_all();
        $id = $args['id'] ?? 'listing_post_type_pages';
        $overrides = $opts[ $id ] ?? [];
        if ( ! is_array( $overrides ) ) {
            $overrides = [];
        }

        $this->render_field_wrapper_open( $args );

        $listing_post_types = $args['listing_post_types'] ?? [];
        if ( ! is_array( $listing_post_types ) ) {
            $listing_post_types = [];
        }
        $page_options = $args['page_options'] ?? [];
        if ( ! is_array( $page_options ) ) {
            $page_options = [];
        }

        $selected_types = $opts['allowed_listing_post_types'] ?? [];
        if ( empty( $selected_types ) && ! empty( $opts['listing_post_type'] ) ) {
            $selected_types = [ $opts['listing_post_type'] ];
        }
        if ( empty( $selected_types ) && ! empty( $args['default_listing_types'] ) ) {
            $selected_types = $args['default_listing_types'];
        }
        if ( ! is_array( $selected_types ) ) {
            $selected_types = [ $selected_types ];
        }
        $selected_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $selected_types ) ) ) );

        $available_types = array_keys( $listing_post_types );
        if ( ! empty( $available_types ) ) {
            $selected_types = array_values( array_intersect( $selected_types, $available_types ) );
        }
        if ( empty( $selected_types ) ) {
            $selected_types = $available_types;
        }

        if ( empty( $selected_types ) ) {
            echo '<p class="description">' . esc_html__( 'Select listing post types above to configure per-type pages.', 'mybookaro' ) . '</p>';
            return;
        }

        $contexts = [
            'booking' => __( 'Booking Page', 'mybookaro' ),
            'provider_dashboard' => __( 'Provider Dashboard Page', 'mybookaro' ),
            'customer_dashboard' => __( 'Customer Dashboard Page', 'mybookaro' ),
        ];

        echo '<table class="widefat striped" style="max-width:820px;">';
        echo '<thead><tr><th>' . esc_html__( 'Listing Type', 'mybookaro' ) . '</th>';
        foreach ( $contexts as $label ) {
            echo '<th>' . esc_html( $label ) . '</th>';
        }
        echo '</tr></thead><tbody>';

        foreach ( $selected_types as $post_type ) {
            $label = $listing_post_types[ $post_type ] ?? $post_type;
            echo '<tr>';
            echo '<td style="width:200px;"><strong>' . esc_html( $label ) . '</strong><div class="description">' . esc_html( $post_type ) . '</div></td>';

            $row = $overrides[ $post_type ] ?? [];
            if ( ! is_array( $row ) ) {
                $row = [];
            }

            foreach ( $contexts as $context_key => $context_label ) {
                $selected = isset( $row[ $context_key ] ) ? absint( $row[ $context_key ] ) : 0;
                $name = sprintf(
                    '%s[%s][%s][%s]',
                    OptionRepository::OPTION_KEY,
                    $id,
                    $post_type,
                    $context_key
                );
                echo '<td style="vertical-align:top;">';
                echo '<select class="' . esc_attr( $this->input_base_class() ) . '" name="' . esc_attr( $name ) . '">';
                echo '<option value="">' . esc_html__( 'Use default', 'mybookaro' ) . '</option>';
                foreach ( $page_options as $page_id => $page_label ) {
                    printf(
                        '<option value="%1$s"%2$s>%3$s</option>',
                        esc_attr( $page_id ),
                        selected( (string) $page_id, (string) $selected, false ),
                        esc_html( $page_label )
                    );
                }
                echo '</select>';
                echo '</td>';
            }

            echo '</tr>';
        }

        echo '</tbody></table>';

        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render helper actions for dashboard/booking pages in Shortcodes mode.
     */
    public function render_dashboard_pages_helper_field( array $args ): void
    {
        $opts = $this->options->get_all();
        $ui_mode = $this->resolve_ui_mode_from_options( $opts );

        $this->render_field_wrapper_open( $args );

        if ( 'shortcodes' !== $ui_mode ) {
            echo '<p class="description">' . esc_html__( 'This section is shortcode/block-first. Switch to Shortcodes mode to manage booking and dashboard pages here (recommended for Generic).', 'mybookaro' ) . '</p>';
            $this->render_field_wrapper_close();
            return;
        }

        $pages = [
            'booking' => [
                'label' => __( 'Booking page', 'mybookaro' ),
                'id' => absint( $opts['booking_page_id'] ?? 0 ),
            ],
            'provider' => [
                'label' => __( 'Provider dashboard page', 'mybookaro' ),
                'id' => absint( $opts['provider_dashboard_page_id'] ?? 0 ),
            ],
            'customer' => [
                'label' => __( 'Customer dashboard page', 'mybookaro' ),
                'id' => absint( $opts['customer_dashboard_page_id'] ?? 0 ),
            ],
        ];
        $missing = 0;
        foreach ( $pages as $key => $page ) {
            $post = $page['id'] > 0 ? get_post( $page['id'] ) : null;
            if ( ! $post ) {
                $missing++;
            }
            $pages[ $key ]['post'] = $post;
        }

        $variant = $missing > 0 ? 'warning' : 'success';
        $embed_url = admin_url( 'admin.php?page=bookaro-booking-settings&tab=shortcodes#bookaro-settings-tabs' );
        ?>
        <div class="bookaro-note bookaro-note--<?php echo esc_attr( $variant ); ?> bookaro-pages-helper">
            <strong><?php esc_html_e( 'Dashboard pages', 'mybookaro' ); ?></strong>
            <p class="description">
                <?php esc_html_e( 'Create pages that contain the booking form and dashboard blocks, then add them to your menu.', 'mybookaro' ); ?>
            </p>
            <div class="bookaro-status-list">
                <?php foreach ( $pages as $page ) : ?>
                    <?php
                    $post = $page['post'] ?? null;
                    $page_id = $page['id'];
                    $ok = $post instanceof \WP_Post;
                    $title = $ok ? $post->post_title : '';
                    if ( $ok && $title === '' ) {
                        /* translators: %d: numeric value */
                        $title = sprintf( __( 'Page #%d', 'mybookaro' ), $page_id );
                    }
                    $edit_url = $ok ? get_edit_post_link( $page_id, 'url' ) : '';
                    $view_url = $ok ? get_permalink( $page_id ) : '';
                    ?>
                    <div class="bookaro-status-item">
                        <div class="bookaro-status-item__label"><?php echo esc_html( $page['label'] ); ?></div>
                        <div class="bookaro-status-item__value">
                            <span class="bookaro-status-pill <?php echo esc_attr( $ok ? 'bookaro-status-pill--ok' : 'bookaro-status-pill--warn' ); ?>">
                                <?php echo esc_html( $ok ? __( 'Set', 'mybookaro' ) : __( 'Not set', 'mybookaro' ) ); ?>
                            </span>
                            <div class="bookaro-status-item__meta">
                                <?php
                                /* translators: %s: assigned page title */
                                echo esc_html( $ok ? sprintf( __( 'Page: %s', 'mybookaro' ), $title ) : __( 'Create a new page with the MyBookaro block.', 'mybookaro' ) ); ?>
                            </div>
                            <?php if ( $ok && ( $edit_url || $view_url ) ) : ?>
                                <div class="bookaro-status-item__actions">
                                    <?php if ( $edit_url ) : ?>
                                        <a class="button button-small" href="<?php echo esc_url( $edit_url ); ?>"><?php esc_html_e( 'Edit', 'mybookaro' ); ?></a>
                                    <?php endif; ?>
                                    <?php if ( $view_url ) : ?>
                                        <a class="button button-small" href="<?php echo esc_url( $view_url ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'View', 'mybookaro' ); ?></a>
                                    <?php endif; ?>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>
                <?php endforeach; ?>
            </div>
            <div class="bookaro-pages-helper__actions">
                <a class="button button-secondary" href="<?php echo esc_url( $embed_url ); ?>">
                    <?php esc_html_e( 'Open Embed tab', 'mybookaro' ); ?>
                </a>
                <?php if ( $missing > 0 ) : ?>
                    <button
                        type="submit"
                        class="button button-primary"
                        form="<?php echo esc_attr( self::CREATE_DASHBOARD_PAGES_FORM_ID ); ?>"
                    >
                        <?php esc_html_e( 'Create missing pages', 'mybookaro' ); ?>
                    </button>
                <?php endif; ?>
            </div>
        </div>
        <?php
        $this->render_field_wrapper_close();
    }

    private function render_create_dashboard_pages_form(): void
    {
        ?>
        <form
            method="post"
            action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"
            id="<?php echo esc_attr( self::CREATE_DASHBOARD_PAGES_FORM_ID ); ?>"
        >
            <input type="hidden" name="action" value="bookaro_create_dashboard_pages">
            <?php wp_nonce_field( 'bookaro_create_dashboard_pages', 'bookaro_create_dashboard_pages_nonce' ); ?>
        </form>
        <?php
    }

    /**
     * Render a subject text input.
     */
    public function render_email_subject_field( array $args ): void {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[ $id ] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="text" name="%1$s[%2$s]" value="%3$s" class="%4$s" data-bookaro-setting="%2$s"%5$s>',
            esc_attr( OptionRepository::OPTION_KEY ),
            esc_attr( $id ),
            esc_attr( $val ),
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render a WYSIWYG body editor.
     */
    public function render_email_body_field( array $args ): void {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[ $id ] ?? $args['default'];
        $this->render_field_wrapper_open( $args );
        wp_editor(
            $val,
            $id,
            [
                'textarea_name' => OptionRepository::OPTION_KEY . "[{$id}]",
                'media_buttons' => false,
                'textarea_rows' => 6,
            ]
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render a combined email template card (subject + body + token chips).
     */
    public function render_email_template_field( array $args ): void {
        $opts = $this->options->get_all();
        $template_key = (string) ( $args['template_key'] ?? '' );
        $template_label = (string) ( $args['template_label'] ?? '' );
        $subject_id = (string) ( $args['subject_id'] ?? '' );
        $body_id = (string) ( $args['body_id'] ?? '' );
        $enabled_id = (string) ( $args['enabled_id'] ?? '' );
        $enabled_label = (string) ( $args['enabled_label'] ?? __( 'Enable this email template', 'mybookaro' ) );
        $enabled_default = ! empty( $args['enabled_default'] );
        $subject_default = (string) ( $args['subject_default'] ?? '' );
        $body_default = (string) ( $args['body_default'] ?? '' );
        $tokens = $args['tokens'] ?? [];
        if ( ! is_array( $tokens ) ) {
            $tokens = [];
        }
        $sample_values = $this->get_email_preview_samples( $template_key );
        $sample_json = wp_json_encode( $sample_values );
        if ( ! is_string( $sample_json ) || $sample_json === '' ) {
            $sample_json = '{}';
        }

        $subject_value = $subject_id !== '' ? ( $opts[ $subject_id ] ?? $subject_default ) : $subject_default;
        $body_value = $body_id !== '' ? ( $opts[ $body_id ] ?? $body_default ) : $body_default;
        $enabled_checked = $enabled_default;
        if ( $enabled_id !== '' ) {
            $enabled_checked = array_key_exists( $enabled_id, $opts ) ? ! empty( $opts[ $enabled_id ] ) : $enabled_default;
        }
        $disabled = $this->get_pro_disabled_attr( $args );
        $input_class = $this->get_pro_field_class( $args, $this->input_base_class() );

        $this->render_field_wrapper_open( $args );
        echo '<div class="bookaro-email-template" data-template="' . esc_attr( $template_key ) . '" data-preview-samples="' . esc_attr( $sample_json ) . '">';
        echo '<div class="bookaro-email-template__header">';
        echo '<div>';
        if ( $template_label !== '' ) {
            echo '<h4 class="bookaro-email-template__title">' . esc_html( $template_label ) . '</h4>';
        }
        echo '<p class="description">' . esc_html__( 'Customize the subject and body for this notification.', 'mybookaro' ) . '</p>';
        echo '</div>';
        echo '<div class="bookaro-email-template__header-actions">';
        if ( $enabled_id !== '' ) {
            printf(
                '<input type="hidden" name="%1$s[%2$s]" value="0"%3$s>',
                esc_attr( OptionRepository::OPTION_KEY ),
                esc_attr( $enabled_id ),
                disabled( $disabled, true, false )
            );
            printf(
                '<label class="bookaro-email-template__toggle"><input type="checkbox" name="%1$s[%2$s]" value="1" %3$s data-bookaro-setting="%2$s"%4$s> <span>%5$s</span></label>',
                esc_attr( OptionRepository::OPTION_KEY ),
                esc_attr( $enabled_id ),
                checked( $enabled_checked, true, false ),
                disabled( $disabled, true, false ),
                esc_html( $enabled_label )
            );
        }
        echo '<button type="button" class="button button-secondary bookaro-email-preview-toggle">' . esc_html__( 'Preview with sample data', 'mybookaro' ) . '</button>';
        echo '</div>';
        echo '</div>';

        if ( ! empty( $tokens ) ) {
            echo '<div class="bookaro-email-template__tokens">';
            echo '<span class="bookaro-email-template__tokens-label">' . esc_html__( 'Insert token:', 'mybookaro' ) . '</span>';
            foreach ( $tokens as $token ) {
                $token = (string) $token;
                if ( $token === '' ) {
                    continue;
                }
                echo '<button type="button" class="bookaro-token-chip" data-token="' . esc_attr( $token ) . '">' . esc_html( $token ) . '</button>';
            }
            echo '</div>';
        }

        echo '<div class="bookaro-email-template__field">';
        echo '<label for="' . esc_attr( $subject_id ) . '">' . esc_html__( 'Subject', 'mybookaro' ) . '</label>';
        printf(
            '<input type="text" name="%1$s[%2$s]" id="%2$s" value="%3$s" class="%4$s bookaro-email-template__subject-input" data-bookaro-setting="%2$s"%5$s>',
            esc_attr( OptionRepository::OPTION_KEY ),
            esc_attr( $subject_id ),
            esc_attr( $subject_value ),
            esc_attr( $input_class ),
            disabled( $disabled, true, false )
        );
        echo '</div>';

        echo '<div class="bookaro-email-template__field">';
        echo '<label for="' . esc_attr( $body_id ) . '">' . esc_html__( 'Body', 'mybookaro' ) . '</label>';
        wp_editor(
            $body_value,
            $body_id,
            [
                'textarea_name' => OptionRepository::OPTION_KEY . "[{$body_id}]",
                'media_buttons' => false,
                'textarea_rows' => 6,
                'textarea_class' => 'bookaro-email-template__body-input',
            ]
        );
        echo '</div>';

        echo '<div class="bookaro-email-preview" hidden>';
        echo '<p class="description">' . esc_html__( 'Preview uses sample booking values so you can validate token output before sending.', 'mybookaro' ) . '</p>';
        if ( ! empty( $sample_values ) ) {
            echo '<div class="bookaro-email-preview__samples">';
            foreach ( $sample_values as $token_key => $token_value ) {
                $token_key = sanitize_key( (string) $token_key );
                if ( $token_key === '' ) {
                    continue;
                }
                $token_display = '{' . $token_key . '}';
                $token_value = is_scalar( $token_value ) ? (string) $token_value : '';
                if ( $token_value === '' ) {
                    continue;
                }
                echo '<span class="bookaro-email-preview__sample"><code>' . esc_html( $token_display ) . '</code> = ' . esc_html( $token_value ) . '</span>';
            }
            echo '</div>';
        }
        echo '<div class="bookaro-email-preview__panel">';
        echo '<div class="bookaro-email-preview__label">' . esc_html__( 'Subject preview', 'mybookaro' ) . '</div>';
        echo '<div class="bookaro-email-preview__subject-output"></div>';
        echo '</div>';
        echo '<div class="bookaro-email-preview__panel">';
        echo '<div class="bookaro-email-preview__label">' . esc_html__( 'Body preview', 'mybookaro' ) . '</div>';
        echo '<div class="bookaro-email-preview__body-output"></div>';
        echo '</div>';
        echo '</div>';

        $this->render_pro_badge( $args );
        echo '</div>';
        $this->render_disabled_reason( $args );
        $this->render_field_wrapper_close();
    }

    private function get_email_preview_samples( string $template_key ): array
    {
        $template_key = sanitize_key( $template_key );
        $date_format = get_option( 'date_format' ) ?: 'Y-m-d';
        $time_format = get_option( 'time_format' ) ?: 'H:i';
        // Bug 32: wp_date() applies wp_timezone to a UTC timestamp.
        // current_time('timestamp') is already WP-localized, so feeding it
        // here double-applied gmt_offset and the email preview showed dates
        // gmt_offset hours off. Use time() for true UTC.
        $now = time();
        $start = $now + DAY_IN_SECONDS + ( 2 * HOUR_IN_SECONDS );
        $end = $start + HOUR_IN_SECONDS;
        $old_start = $start - DAY_IN_SECONDS;
        $old_end = $end - DAY_IN_SECONDS;

        $date = wp_date( $date_format . ' ' . $time_format, $start );
        $date_to = wp_date( $date_format . ' ' . $time_format, $end );
        $old_date = wp_date( $date_format . ' ' . $time_format, $old_start );
        $old_date_to = wp_date( $date_format . ' ' . $time_format, $old_end );

        $timezone = function_exists( 'wp_timezone_string' ) ? (string) wp_timezone_string() : '';
        if ( $timezone === '' ) {
            $timezone_object = wp_timezone();
            $timezone = $timezone_object instanceof \DateTimeZone ? (string) $timezone_object->getName() : '';
        }
        if ( $timezone === '' ) {
            $timezone = 'UTC';
        }

        $samples = [
            'service' => __( 'Consultation Session', 'mybookaro' ),
            'date' => $date,
            'date_to' => $date_to,
            'date_range' => sprintf(
                /* translators: 1: start date, 2: end date */
                __( '%1$s to %2$s', 'mybookaro' ),
                $date,
                $date_to
            ),
            'provider' => __( 'Alex Provider', 'mybookaro' ),
            'provider_email' => 'provider@example.com',
            'provider_phone' => '+15551234567',
            'owner' => __( 'Jamie Customer', 'mybookaro' ),
            'owner_name' => __( 'Jamie Customer', 'mybookaro' ),
            'email' => 'customer@example.com',
            'customer_phone' => '+15557654321',
            'recipient' => __( 'Jamie Customer', 'mybookaro' ),
            'actor' => __( 'Alex Provider', 'mybookaro' ),
            'reason' => __( 'Schedule conflict', 'mybookaro' ),
            'old_date' => $old_date,
            'old_date_to' => $old_date_to,
            'old_date_range' => sprintf(
                /* translators: 1: start date, 2: end date */
                __( '%1$s to %2$s', 'mybookaro' ),
                $old_date,
                $old_date_to
            ),
            'review_link' => home_url( '/listing/sample-listing/#reviews' ),
            'listing_title' => __( 'Sample Listing', 'mybookaro' ),
            'listing_url' => home_url( '/listing/sample-listing/' ),
            'booking_id' => '1001',
            'manage_booking_url' => home_url( '/my-account/bookings-and-management/?tab=bookings' ),
            'timezone' => $timezone,
        ];

        /**
         * Filter sample token values used by the admin email template preview.
         *
         * @param array<string,string> $samples
         * @param string               $template_key
         */
        $samples = apply_filters( 'bookaro_email_template_preview_samples', $samples, $template_key );
        return is_array( $samples ) ? $samples : [];
    }

    /**
     * Render a section heading inside a settings table.
     */
    public function render_heading_field( array $args ): void
    {
        $title = isset( $args['title'] ) ? (string) $args['title'] : '';
        $description = isset( $args['description'] ) ? (string) $args['description'] : '';
        $this->render_field_wrapper_open( $args );
        echo '<div class="bookaro-section-heading">';
        if ( $title !== '' ) {
            echo '<h3>' . esc_html( $title ) . '</h3>';
        }
        if ( $description !== '' ) {
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        echo '</div>';
        $this->render_field_wrapper_close();
    }

    /**
     * Render a callout note inside a settings table.
     */
    public function render_note_field( array $args ): void
    {
        $title = isset( $args['title'] ) ? (string) $args['title'] : '';
        $description = isset( $args['description'] ) ? (string) $args['description'] : '';
        $variant = isset( $args['variant'] ) ? (string) $args['variant'] : 'info';
        $this->render_field_wrapper_open( $args );
        echo '<div class="bookaro-note bookaro-note--' . esc_attr( $variant ) . '">';
        if ( $title !== '' ) {
            echo '<strong>' . esc_html( $title ) . '</strong>';
        }
        if ( $description !== '' ) {
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        echo '</div>';
        $this->render_field_wrapper_close();
    }

    /**
     * Sanitize and merge incoming settings with existing values.
     *
     * @param array $input Raw POSTed settings.
     * @return array Sanitized settings array.
     */
    public function sanitize_settings(array $input): array
    {
        $input = $this->strip_placeholder_input( $input );
        $input = $this->strip_unregistered_input( $input );
        $existing = $this->options->get_all();
        $out      = $existing;
        $invalid_colors = [];

        // ---- Appearance ----
        $out['appearance_primary_color']         = sanitize_hex_color(
            $input['appearance_primary_color']        ?? $existing['appearance_primary_color']        ?? ''
        );
        if ( empty( $out['appearance_primary_color'] ) ) {
            $out['appearance_primary_color'] = '#007cba';
            $invalid_colors[] = 'appearance_primary_color';
        }
        $out['appearance_secondary_color']       = sanitize_hex_color(
            $input['appearance_secondary_color']      ?? $existing['appearance_secondary_color']      ?? '#6c757d'
        );
        if ( empty( $out['appearance_secondary_color'] ) ) {
            $out['appearance_secondary_color'] = '#6c757d';
            $invalid_colors[] = 'appearance_secondary_color';
        }
        $out['appearance_modal_bg_color']        = sanitize_hex_color(
            $input['appearance_modal_bg_color']       ?? $existing['appearance_modal_bg_color']       ?? '#ffffff'
        );
        if ( empty( $out['appearance_modal_bg_color'] ) ) {
            $out['appearance_modal_bg_color'] = '#ffffff';
            $invalid_colors[] = 'appearance_modal_bg_color';
        }
        $out['appearance_close_btn_color']       = sanitize_hex_color(
            $input['appearance_close_btn_color']      ?? $existing['appearance_close_btn_color']      ?? '#000000'
        );
        if ( empty( $out['appearance_close_btn_color'] ) ) {
            $out['appearance_close_btn_color'] = '#000000';
            $invalid_colors[] = 'appearance_close_btn_color';
        }
        $out['appearance_close_btn_hover_color'] = sanitize_hex_color(
            $input['appearance_close_btn_hover_color'] ?? $existing['appearance_close_btn_hover_color'] ?? '#333333'
        );
        if ( empty( $out['appearance_close_btn_hover_color'] ) ) {
            $out['appearance_close_btn_hover_color'] = '#333333';
            $invalid_colors[] = 'appearance_close_btn_hover_color';
        }
        $out['appearance_overlay_color']         = sanitize_text_field(
            $input['appearance_overlay_color']        ?? $existing['appearance_overlay_color']        ?? 'rgba(0,0,0,0.5)'
        );
        // Typography presets. The *_preset key holds the chosen preset token
        // (resolved to an actual font stack at render time); the legacy
        // *_font key now stores the optional custom CSS font stack.
        $body_font_preset = sanitize_key(
            $input['appearance_body_font_preset'] ?? $existing['appearance_body_font_preset'] ?? 'system'
        );
        if ( ! in_array( $body_font_preset, [ 'system', 'theme', 'sans', 'serif', 'mono', 'custom' ], true ) ) {
            $body_font_preset = 'system';
        }
        $out['appearance_body_font_preset'] = $body_font_preset;
        $out['appearance_body_font']             = sanitize_text_field(
            $input['appearance_body_font']            ?? $existing['appearance_body_font']            ?? 'system-ui, sans-serif'
        );
        $heading_font_preset = sanitize_key(
            $input['appearance_heading_font_preset'] ?? $existing['appearance_heading_font_preset'] ?? 'body'
        );
        if ( ! in_array( $heading_font_preset, [ 'body', 'theme', 'system', 'sans', 'serif', 'mono', 'custom' ], true ) ) {
            $heading_font_preset = 'body';
        }
        $out['appearance_heading_font_preset'] = $heading_font_preset;
        $out['appearance_heading_font']          = sanitize_text_field(
            $input['appearance_heading_font']         ?? $existing['appearance_heading_font']         ?? ''
        );
        $out['appearance_modal_max_width']       = sanitize_text_field(
            $input['appearance_modal_max_width']      ?? $existing['appearance_modal_max_width']      ?? 'min(600px,90%)'
        );
        $out['appearance_dashboard_max_width']       = sanitize_text_field(
            $input['appearance_dashboard_max_width']      ?? $existing['appearance_dashboard_max_width']      ?? '800px'
        );
        $out['appearance_modal_padding']         = sanitize_text_field(
            $input['appearance_modal_padding']        ?? $existing['appearance_modal_padding']        ?? '2rem'
        );
        $out['appearance_border_radius']         = sanitize_text_field(
            $input['appearance_border_radius']        ?? $existing['appearance_border_radius']        ?? '1.5rem'
        );
        if ( isset($input['appearance_shadow_opacity']) ) {
            $opacity = floatval( $input['appearance_shadow_opacity'] );
            if ( $opacity < 0 || $opacity > 1 ) {
                $opacity = min( 1, max( 0, $opacity ) );
                $this->add_settings_error_safe(
                    'bookaro_shadow_opacity',
                    __( 'Shadow opacity must be between 0 and 1.', 'mybookaro' )
                );
            }
            $out['appearance_shadow_opacity'] = $opacity;
        } else {
            $out['appearance_shadow_opacity'] = floatval( $existing['appearance_shadow_opacity'] ?? 0.1 );
        }
        $out['appearance_calendar_header_color'] = sanitize_hex_color(
            $input['appearance_calendar_header_color'] ?? $existing['appearance_calendar_header_color'] ?? '#f7f7f7'
        );
        if ( empty( $out['appearance_calendar_header_color'] ) ) {
            $out['appearance_calendar_header_color'] = '#f7f7f7';
            $invalid_colors[] = 'appearance_calendar_header_color';
        }
        $out['appearance_slot_border_color']     = sanitize_hex_color(
            $input['appearance_slot_border_color']    ?? $existing['appearance_slot_border_color']    ?? '#dee2e6'
        );
        if ( empty( $out['appearance_slot_border_color'] ) ) {
            $out['appearance_slot_border_color'] = '#dee2e6';
            $invalid_colors[] = 'appearance_slot_border_color';
        }
        $out['appearance_slot_selected_color']   = sanitize_hex_color(
            $input['appearance_slot_selected_color']  ?? $existing['appearance_slot_selected_color']  ?? '#007cba'
        );
        if ( empty( $out['appearance_slot_selected_color'] ) ) {
            $out['appearance_slot_selected_color'] = '#007cba';
            $invalid_colors[] = 'appearance_slot_selected_color';
        }
        $out['appearance_slot_disabled_color']   = sanitize_hex_color(
            $input['appearance_slot_disabled_color']  ?? $existing['appearance_slot_disabled_color']  ?? '#cccccc'
        );
        if ( empty( $out['appearance_slot_disabled_color'] ) ) {
            $out['appearance_slot_disabled_color'] = '#cccccc';
            $invalid_colors[] = 'appearance_slot_disabled_color';
        }
        $out['appearance_action_approve_color'] = sanitize_hex_color(
            $input['appearance_action_approve_color'] ?? $existing['appearance_action_approve_color'] ?? '#22c55e'
        );
        if ( empty( $out['appearance_action_approve_color'] ) ) {
            $out['appearance_action_approve_color'] = '#22c55e';
            $invalid_colors[] = 'appearance_action_approve_color';
        }
        $out['appearance_action_reject_color'] = sanitize_hex_color(
            $input['appearance_action_reject_color'] ?? $existing['appearance_action_reject_color'] ?? '#ef4444'
        );
        if ( empty( $out['appearance_action_reject_color'] ) ) {
            $out['appearance_action_reject_color'] = '#ef4444';
            $invalid_colors[] = 'appearance_action_reject_color';
        }
        $out['appearance_action_view_color'] = sanitize_hex_color(
            $input['appearance_action_view_color'] ?? $existing['appearance_action_view_color'] ?? '#2563eb'
        );
        if ( empty( $out['appearance_action_view_color'] ) ) {
            $out['appearance_action_view_color'] = '#2563eb';
            $invalid_colors[] = 'appearance_action_view_color';
        }
        $out['appearance_action_note_color'] = sanitize_hex_color(
            $input['appearance_action_note_color'] ?? $existing['appearance_action_note_color'] ?? '#0ea5e9'
        );
        if ( empty( $out['appearance_action_note_color'] ) ) {
            $out['appearance_action_note_color'] = '#0ea5e9';
            $invalid_colors[] = 'appearance_action_note_color';
        }
        $out['appearance_action_cancel_color'] = sanitize_hex_color(
            $input['appearance_action_cancel_color'] ?? $existing['appearance_action_cancel_color'] ?? '#dc2626'
        );
        if ( empty( $out['appearance_action_cancel_color'] ) ) {
            $out['appearance_action_cancel_color'] = '#dc2626';
            $invalid_colors[] = 'appearance_action_cancel_color';
        }
        $out['appearance_action_reschedule_color'] = sanitize_hex_color(
            $input['appearance_action_reschedule_color'] ?? $existing['appearance_action_reschedule_color'] ?? '#6366f1'
        );
        if ( empty( $out['appearance_action_reschedule_color'] ) ) {
            $out['appearance_action_reschedule_color'] = '#6366f1';
            $invalid_colors[] = 'appearance_action_reschedule_color';
        }
        $out['appearance_action_complete_color'] = sanitize_hex_color(
            $input['appearance_action_complete_color'] ?? $existing['appearance_action_complete_color'] ?? '#16a34a'
        );
        if ( empty( $out['appearance_action_complete_color'] ) ) {
            $out['appearance_action_complete_color'] = '#16a34a';
            $invalid_colors[] = 'appearance_action_complete_color';
        }
        $dashboard_density = sanitize_key(
            $input['appearance_dashboard_density'] ?? $existing['appearance_dashboard_density'] ?? 'comfortable'
        );
        if ( ! in_array( $dashboard_density, [ 'comfortable', 'compact' ], true ) ) {
            $dashboard_density = 'comfortable';
        }
        $out['appearance_dashboard_density'] = $dashboard_density;

        $dashboard_surface = sanitize_key(
            $input['appearance_dashboard_surface_style'] ?? $existing['appearance_dashboard_surface_style'] ?? 'soft'
        );
        if ( ! in_array( $dashboard_surface, [ 'soft', 'flat' ], true ) ) {
            $dashboard_surface = 'soft';
        }
        $out['appearance_dashboard_surface_style'] = $dashboard_surface;

        $dashboard_shadow_depth = sanitize_key(
            $input['appearance_dashboard_shadow_depth'] ?? $existing['appearance_dashboard_shadow_depth'] ?? 'medium'
        );
        if ( ! in_array( $dashboard_shadow_depth, [ 'subtle', 'medium', 'deep' ], true ) ) {
            $dashboard_shadow_depth = 'medium';
        }
        $out['appearance_dashboard_shadow_depth'] = $dashboard_shadow_depth;

        $dashboard_shadow_opacity_raw = $input['appearance_dashboard_shadow_opacity'] ?? $existing['appearance_dashboard_shadow_opacity'] ?? '0.1';
        $dashboard_shadow_opacity = max( 0, min( 1, floatval( $dashboard_shadow_opacity_raw ) ) );
        $out['appearance_dashboard_shadow_opacity'] = $dashboard_shadow_opacity;

        $out['appearance_dashboard_border_color'] = sanitize_hex_color(
            $input['appearance_dashboard_border_color'] ?? $existing['appearance_dashboard_border_color'] ?? '#e2e8f0'
        );
        if ( empty( $out['appearance_dashboard_border_color'] ) ) {
            $out['appearance_dashboard_border_color'] = '#e2e8f0';
            $invalid_colors[] = 'appearance_dashboard_border_color';
        }

        $dashboard_border_width = absint(
            $input['appearance_dashboard_border_width'] ?? $existing['appearance_dashboard_border_width'] ?? 1
        );
        if ( $dashboard_border_width > 6 ) {
            $dashboard_border_width = 6;
        }
        $out['appearance_dashboard_border_width'] = $dashboard_border_width;

        // ---- Booking Modes ----
        $site_type = sanitize_key(
            $input['site_type']
            ?? $existing['site_type']
            ?? ( function_exists( 'bookaro_get_site_type_setting' ) ? bookaro_get_site_type_setting() : 'directory' )
        );
        if ( ! in_array( $site_type, [ 'directory', 'single_business', 'advanced_mapping' ], true ) ) {
            $site_type = 'directory';
        }
        $out['site_type'] = $site_type;
        $resource_label_singular = sanitize_text_field(
            $input['resource_label_singular'] ?? $existing['resource_label_singular'] ?? ''
        );
        $resource_label_plural = sanitize_text_field(
            $input['resource_label_plural'] ?? $existing['resource_label_plural'] ?? ''
        );
        if ( '' === $resource_label_singular ) {
            $resource_label_singular = ( 'single_business' === $site_type )
                ? __( 'Business', 'mybookaro' )
                : __( 'Listing', 'mybookaro' );
        }
        if ( '' === $resource_label_plural ) {
            $resource_label_plural = ( 'single_business' === $site_type )
                ? __( 'Services', 'mybookaro' )
                : __( 'Listings', 'mybookaro' );
        }
        $out['resource_label_singular'] = $resource_label_singular;
        $out['resource_label_plural'] = $resource_label_plural;

        $integration_mode = sanitize_key(
            $input['integration_mode'] ?? $existing['integration_mode'] ?? 'auto'
        );
        if ( ! in_array( $integration_mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $integration_mode = 'auto';
        }
        if ( in_array( $site_type, [ 'single_business', 'advanced_mapping' ], true ) ) {
            $integration_mode = 'generic';
        }
        $out['integration_mode'] = $integration_mode;
        $generic_advanced_setup_enabled = 0;
        if ( array_key_exists( 'generic_advanced_setup_enabled', $input ) ) {
            $generic_advanced_setup_enabled = ! empty( $input['generic_advanced_setup_enabled'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'generic_advanced_setup_enabled', $existing ) ) {
            $generic_advanced_setup_enabled = ! empty( $existing['generic_advanced_setup_enabled'] ) ? 1 : 0;
        }
        if ( 'advanced_mapping' === $site_type ) {
            $generic_advanced_setup_enabled = 1;
        } elseif ( 'single_business' === $site_type ) {
            $generic_advanced_setup_enabled = 0;
        }
        $out['generic_advanced_setup_enabled'] = $generic_advanced_setup_enabled;
        $generic_setup_preset = sanitize_key(
            $input['generic_setup_preset'] ?? $existing['generic_setup_preset'] ?? ''
        );
        if ( 'single_business' === $site_type ) {
            $generic_setup_preset = 'author_provider';
        } elseif ( ! in_array( $generic_setup_preset, [ 'author_provider', 'meta_key_provider', 'author_admin_catalog' ], true ) ) {
            $existing_provider_mode_for_preset = sanitize_key( (string) ( $existing['provider_resolution_mode'] ?? '' ) );
            $existing_services_model_for_preset = sanitize_key( (string) ( $existing['services_model'] ?? $existing['generic_services_mode'] ?? '' ) );
            if ( 'admin_catalog' === $existing_services_model_for_preset ) {
                $existing_services_model_for_preset = 'admin_only';
            }
            if ( 'meta_key' === $existing_provider_mode_for_preset ) {
                $generic_setup_preset = 'meta_key_provider';
            } elseif ( 'admin_only' === $existing_services_model_for_preset ) {
                $generic_setup_preset = 'author_admin_catalog';
            } else {
                $generic_setup_preset = 'author_provider';
            }
        }
        $out['generic_setup_preset'] = $generic_setup_preset;
        $is_generic_context = ( 'generic' === $integration_mode )
            || (
                'auto' === $integration_mode
                && ! ( function_exists( 'bookaro_is_mylisting_active' ) && bookaro_is_mylisting_active() )
            );

        $ui_mode_default = $is_generic_context ? 'shortcodes' : 'woocommerce';
        $ui_mode = sanitize_key(
            $input['ui_mode'] ?? $existing['ui_mode'] ?? $ui_mode_default
        );
        if ( ! in_array( $ui_mode, [ 'woocommerce', 'shortcodes' ], true ) ) {
            $ui_mode = $ui_mode_default;
        }
        $out['ui_mode'] = $ui_mode;

        $currency_symbol = sanitize_text_field(
            $input['currency_symbol'] ?? $existing['currency_symbol'] ?? ''
        );
        $out['currency_symbol'] = $currency_symbol;

        $listing_post_types = $this->get_listing_post_type_options();
        $available_listing_types = array_keys( $listing_post_types );
        $default_listing_type = ( 'single_business' === $site_type && defined( 'BOOKARO_PROVIDER_POST_TYPE' ) )
            ? sanitize_key( (string) BOOKARO_PROVIDER_POST_TYPE )
            : 'job_listing';
        if ( ! empty( $listing_post_types ) && ! isset( $listing_post_types[ $default_listing_type ] ) ) {
            $default_listing_type = (string) array_key_first( $listing_post_types );
        }
        $raw_listing_types = $input['allowed_listing_post_types'] ?? $existing['allowed_listing_post_types'] ?? [];
        if ( empty( $raw_listing_types ) ) {
            $legacy_listing_type = $input['listing_post_type'] ?? $existing['listing_post_type'] ?? '';
            if ( $legacy_listing_type !== '' ) {
                $raw_listing_types = [ $legacy_listing_type ];
            }
        }
        if ( ! is_array( $raw_listing_types ) ) {
            $raw_listing_types = [ $raw_listing_types ];
        }
        $raw_listing_types = array_values( array_filter( $raw_listing_types, static function ( $value ): bool {
            return (string) $value !== 'allowed_listing_post_types';
        } ) );
        $selected_listing_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $raw_listing_types ) ) ) );
        if ( ! empty( $available_listing_types ) ) {
            $selected_listing_types = array_values( array_intersect( $selected_listing_types, $available_listing_types ) );
        }
        $listing_types_required = ( 'single_business' !== $site_type );
        if ( empty( $selected_listing_types ) && $listing_types_required ) {
            // Round 9 fix (Issue #24): before falling back to
            // $default_listing_type (which can drift to an alphabetically-
            // first post type like `kbs_company` when the lane's expected
            // type — e.g. `bookaro_provider` for SB — isn't registered yet),
            // prefer the existing saved value if it's still valid. This
            // prevents silent data loss on a Settings Save where the
            // multiselect was empty in the POST but a valid value was
            // already persisted.
            $existing_listing_types = is_array( $existing['allowed_listing_post_types'] ?? null )
                ? array_values( array_unique( array_filter( array_map( 'sanitize_key', $existing['allowed_listing_post_types'] ) ) ) )
                : [];
            if ( ! empty( $available_listing_types ) ) {
                $existing_listing_types = array_values( array_intersect( $existing_listing_types, $available_listing_types ) );
            }
            $selected_listing_types = ! empty( $existing_listing_types )
                ? $existing_listing_types
                : [ $default_listing_type ];
        }
        $out['allowed_listing_post_types'] = $selected_listing_types;
        if ( ! empty( $selected_listing_types ) ) {
            $out['listing_post_type'] = $selected_listing_types[0];
        } else {
            $fallback_listing_type = $default_listing_type;
            if ( 'single_business' === $site_type && defined( 'BOOKARO_PROVIDER_POST_TYPE' ) ) {
                $fallback_listing_type = sanitize_key( (string) BOOKARO_PROVIDER_POST_TYPE );
            }
            $out['listing_post_type'] = $fallback_listing_type;
        }

        $raw_allowed_pages = $input['allowed_listing_pages'] ?? $existing['allowed_listing_pages'] ?? [];
        if ( ! is_array( $raw_allowed_pages ) ) {
            $raw_allowed_pages = [ $raw_allowed_pages ];
        }
        $raw_allowed_pages = array_values( array_filter( $raw_allowed_pages, static function ( $value ): bool {
            return (string) $value !== 'allowed_listing_pages';
        } ) );
        $allowed_pages = [];
        foreach ( $raw_allowed_pages as $page_id ) {
            $page_id = absint( $page_id );
            if ( $page_id < 1 ) {
                continue;
            }
            if ( ! get_post( $page_id ) ) {
                continue;
            }
            $allowed_pages[] = $page_id;
        }
        $allowed_pages = array_values( array_unique( $allowed_pages ) );
        $out['allowed_listing_pages'] = array_map( 'strval', $allowed_pages );

        $raw_page_overrides = $input['listing_post_type_pages'] ?? $existing['listing_post_type_pages'] ?? [];
        if ( ! is_array( $raw_page_overrides ) ) {
            $raw_page_overrides = [];
        }
        $page_overrides = [];
        $page_contexts = [ 'booking', 'provider_dashboard', 'customer_dashboard' ];
        foreach ( $selected_listing_types as $post_type ) {
            if ( empty( $raw_page_overrides[ $post_type ] ) || ! is_array( $raw_page_overrides[ $post_type ] ) ) {
                continue;
            }
            $row = $raw_page_overrides[ $post_type ];
            foreach ( $page_contexts as $context_key ) {
                $page_id = absint( $row[ $context_key ] ?? 0 );
                if ( $page_id < 1 ) {
                    continue;
                }
                if ( 'page' !== get_post_type( $page_id ) ) {
                    continue;
                }
                $page_overrides[ $post_type ][ $context_key ] = $page_id;
            }
        }
        $out['listing_post_type_pages'] = $page_overrides;

        $page_override_keys = [
            'booking_page_id' => 'booking',
            'provider_dashboard_page_id' => 'provider_dashboard',
            'customer_dashboard_page_id' => 'customer_dashboard',
        ];
        foreach ( $page_override_keys as $key => $context ) {
            $page_id = absint( $input[ $key ] ?? $existing[ $key ] ?? 0 );
            if ( $page_id > 0 && 'page' !== get_post_type( $page_id ) ) {
                $page_id = 0;
            }
            $out[ $key ] = $page_id;
        }

        $has_provider_resolution_input = array_key_exists( 'provider_resolution_mode', $input )
            || array_key_exists( 'provider_meta_key', $input )
            || array_key_exists( 'listing_provider_meta_key', $input );
        $has_provider_resolution_existing = array_key_exists( 'provider_resolution_mode', $existing )
            || array_key_exists( 'provider_meta_key', $existing )
            || array_key_exists( 'listing_provider_meta_key', $existing );
        $provider_resolution_default = 'author';
        $provider_meta_key_default = '';
        if ( $is_generic_context && ! $generic_advanced_setup_enabled && ! $has_provider_resolution_input && ! $has_provider_resolution_existing ) {
            if ( 'meta_key_provider' === $generic_setup_preset ) {
                $provider_resolution_default = 'meta_key';
                $provider_meta_key_default = 'provider_user_id';
            }
        }
        $provider_resolution_mode = sanitize_key(
            $input['provider_resolution_mode'] ?? $existing['provider_resolution_mode'] ?? $provider_resolution_default
        );
        if ( ! in_array( $provider_resolution_mode, [ 'author', 'meta_key' ], true ) ) {
            $provider_resolution_mode = $provider_resolution_default;
        }

        $provider_meta_key = sanitize_key(
            $input['provider_meta_key'] ?? $existing['provider_meta_key'] ?? $provider_meta_key_default
        );
        if ( $provider_meta_key === '' ) {
            $legacy_meta_key = sanitize_key(
                $input['listing_provider_meta_key'] ?? $existing['listing_provider_meta_key'] ?? ''
            );
            if ( $legacy_meta_key !== '' ) {
                $provider_meta_key = $legacy_meta_key;
            }
        }
        if ( $provider_meta_key === '' && $provider_meta_key_default !== '' ) {
            $provider_meta_key = $provider_meta_key_default;
        }
        // Legacy generic_provider_user_meta_key removed as of v22+.
        if ( '' === $provider_resolution_mode ) {
            $provider_resolution_mode = $provider_meta_key !== '' ? 'meta_key' : 'author';
        }
        if ( 'single_business' === $site_type ) {
            $provider_resolution_mode = 'author';
            $provider_meta_key = '';
        }

        $out['provider_resolution_mode'] = $provider_resolution_mode;
        $out['provider_meta_key'] = $provider_meta_key;
        // Back-compat: keep legacy option populated when present.
        if ( $provider_meta_key !== '' ) {
            $out['listing_provider_meta_key'] = $provider_meta_key;
        }

        $default_provider_id = absint( $input['default_provider_id'] ?? $existing['default_provider_id'] ?? 0 );
        if ( $default_provider_id > 0 && ! get_user_by( 'ID', $default_provider_id ) ) {
            $default_provider_id = 0;
        }
        if ( 'single_business' === $site_type && $default_provider_id < 1 ) {
            $fallback_provider_id = get_current_user_id();
            if ( $fallback_provider_id > 0 && get_user_by( 'ID', $fallback_provider_id ) ) {
                $default_provider_id = $fallback_provider_id;
            }
        }
        $out['default_provider_id'] = $default_provider_id;

        $has_services_model_input = array_key_exists( 'services_model', $input ) || array_key_exists( 'generic_services_mode', $input );
        $has_services_model_existing = array_key_exists( 'services_model', $existing ) || array_key_exists( 'generic_services_mode', $existing );
        $preset_services_model_default = ( 'single_business' === $site_type )
            ? 'provider_only'
            : ( ( 'author_admin_catalog' === $generic_setup_preset ) ? 'admin_only' : 'provider_only' );
        // Bug 71: only let the guided preset's implied services model become the
        // default when the preset is EXPLICITLY submitted in this request (the admin
        // is on the Lane/Setup tab actively confirming it). On a legacy-auto Generic
        // site with no stored services_model, the runtime resolver
        // bookaro_get_services_model() returns 'hybrid'. If the inferred preset
        // default ('provider_only') were allowed to win on every save, then saving an
        // UNRELATED tab (e.g. Branding colours) would silently flip effective services
        // hybrid -> provider_only and drop the admin catalogue from the booking UI
        // with no warning. Keep the write-path default aligned with the read-path
        // default ('hybrid') unless the lane is being explicitly (re)confirmed.
        $preset_explicitly_selected = array_key_exists( 'generic_setup_preset', $input )
            && in_array(
                sanitize_key( (string) $input['generic_setup_preset'] ),
                [ 'author_provider', 'meta_key_provider', 'author_admin_catalog' ],
                true
            );
        $services_model_default = ( 'single_business' === $site_type )
            ? 'provider_only'
            : (
                ( $is_generic_context && ! $has_services_model_input && ! $has_services_model_existing && $preset_explicitly_selected )
            ? $preset_services_model_default
            : 'hybrid'
            );
        $services_model = sanitize_key(
            $input['services_model']
            ?? $existing['services_model']
            ?? $input['generic_services_mode']
            ?? $existing['generic_services_mode']
            ?? $services_model_default
        );
        if ( 'admin_catalog' === $services_model ) {
            $services_model = 'admin_only';
        }
        if ( ! in_array( $services_model, [ 'admin_only', 'provider_only', 'hybrid' ], true ) ) {
            $services_model = $services_model_default;
        }
        if ( 'single_business' === $site_type ) {
            $services_model = 'provider_only';
        }
        $out['services_model'] = $services_model;
        $out['generic_services_mode'] = ( 'admin_only' === $services_model ) ? 'admin_catalog' : $services_model;
        if ( array_key_exists( 'allow_provider_custom_services', $input ) ) {
            $out['allow_provider_custom_services'] = ! empty( $input['allow_provider_custom_services'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'allow_provider_custom_services', $existing ) ) {
            $out['allow_provider_custom_services'] = ! empty( $existing['allow_provider_custom_services'] ) ? 1 : 0;
        } else {
            $out['allow_provider_custom_services'] = 0;
        }
        if ( array_key_exists( 'allow_provider_override_catalog', $input ) ) {
            $out['allow_provider_override_catalog'] = ! empty( $input['allow_provider_override_catalog'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'allow_provider_override_catalog', $existing ) ) {
            $out['allow_provider_override_catalog'] = ! empty( $existing['allow_provider_override_catalog'] ) ? 1 : 0;
        } else {
            $out['allow_provider_override_catalog'] = 0;
        }
        if ( array_key_exists( 'provider_services_enabled', $input ) ) {
            $out['provider_services_enabled'] = ! empty( $input['provider_services_enabled'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'provider_services_enabled', $existing ) ) {
            $out['provider_services_enabled'] = ! empty( $existing['provider_services_enabled'] ) ? 1 : 0;
        } else {
            $out['provider_services_enabled'] = ( $is_generic_context && ! $generic_advanced_setup_enabled && 'author_admin_catalog' === $generic_setup_preset ) ? 0 : 1;
        }
        if ( 'single_business' === $site_type ) {
            $out['provider_services_enabled'] = 1;
            $out['allow_provider_custom_services'] = 0;
            $out['allow_provider_override_catalog'] = 0;
        }

        $mylisting_class = sanitize_key(
            $input['mylisting_services_repeater_class'] ?? $existing['mylisting_services_repeater_class'] ?? 'mybookaro_services'
        );
        if ( $mylisting_class === '' ) {
            $mylisting_class = 'mybookaro_services';
        }
        $out['mylisting_services_repeater_class'] = $mylisting_class;

        // Booking access ("Require login to book", default 1 = logged-in only).
        // Read the submitted checkbox, but mirror the frontend_enabled
        // partial-payload guard below: a save from another tab omits this
        // General-tab field, so without preserving the existing value the
        // absent key would be read as "0" and silently flip guest bookings on.
        if ( array_key_exists( 'require_login', $input ) ) {
            $out['require_login'] = ! empty( $input['require_login'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'require_login', $existing ) ) {
            $out['require_login'] = ! empty( $existing['require_login'] ) ? 1 : 0;
        } else {
            $out['require_login'] = 1;
        }
        // Preserve existing `frontend_enabled` when the current form doesn't
        // include the checkbox (e.g. when this sanitizer is invoked with a
        // partial payload that doesn't touch the General tab). Without this
        // guard, a save from any other tab silently flips the booking UI off
        // because absent-key was being read as "user wants 0".
        if ( array_key_exists( 'frontend_enabled', $input ) ) {
            $out['frontend_enabled'] = ! empty( $input['frontend_enabled'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'frontend_enabled', $existing ) ) {
            $out['frontend_enabled'] = ! empty( $existing['frontend_enabled'] ) ? 1 : 0;
        } else {
            // First-time save with no existing record — match the
            // GeneralTab field default of 1 (enabled).
            $out['frontend_enabled'] = 1;
        }
        $default_placement_strategy = ( 'single_business' === $site_type ) ? 'normal_page' : 'directory_template';
        $placement_strategy = $this->normalize_booking_placement_strategy_setting(
            $input['booking_placement_strategy']
            ?? $existing['booking_placement_strategy']
            ?? '',
            ''
        );
        if ( '' === $placement_strategy ) {
            $legacy_auto_insert = sanitize_key(
                $input['auto_insert_booking_button']
                ?? $existing['auto_insert_booking_button']
                ?? ''
            );
            $legacy_enabled = null;
            if ( array_key_exists( 'auto_insert_booking_button_enabled', $input ) ) {
                $legacy_enabled = ! empty( $input['auto_insert_booking_button_enabled'] );
            } elseif ( array_key_exists( 'auto_insert_booking_button_enabled', $existing ) ) {
                $legacy_enabled = ! empty( $existing['auto_insert_booking_button_enabled'] );
            }
            if ( null === $legacy_enabled ) {
                $legacy_enabled = ( '' !== $legacy_auto_insert );
            }
            if ( ! $legacy_enabled ) {
                $placement_strategy = 'shortcode_only';
            } elseif ( 'selector' === $legacy_auto_insert ) {
                $placement_strategy = 'css_selector';
            } else {
                $placement_strategy = $default_placement_strategy;
            }
        }
        $placement_strategy = $this->normalize_booking_placement_strategy_setting( $placement_strategy, $default_placement_strategy );
        $out['booking_placement_strategy'] = $placement_strategy;

        $auto_insert = sanitize_key(
            $input['auto_insert_booking_button'] ?? $existing['auto_insert_booking_button'] ?? ''
        );
        if ( ! in_array( $auto_insert, [ 'before_content', 'after_content', 'selector' ], true ) ) {
            $auto_insert = '';
        }
        if ( array_key_exists( 'auto_insert_booking_button_enabled', $input ) ) {
            $auto_insert_enabled = ! empty( $input['auto_insert_booking_button_enabled'] ) ? 1 : 0;
        } elseif ( array_key_exists( 'auto_insert_booking_button_enabled', $existing ) ) {
            $auto_insert_enabled = ! empty( $existing['auto_insert_booking_button_enabled'] ) ? 1 : 0;
        } else {
            $auto_insert_enabled = in_array( $placement_strategy, [ 'directory_template', 'normal_page', 'css_selector' ], true ) ? 1 : 0;
        }
        if ( 'shortcode_only' === $placement_strategy ) {
            $auto_insert_enabled = 0;
            $auto_insert = '';
        } elseif ( 'css_selector' === $placement_strategy ) {
            $auto_insert_enabled = 1;
            $auto_insert = 'selector';
        } else {
            $auto_insert_enabled = 1;
            if ( ! in_array( $auto_insert, [ 'before_content', 'after_content' ], true ) ) {
                $auto_insert = 'after_content';
            }
        }
        $out['auto_insert_booking_button'] = $auto_insert;
        $out['auto_insert_booking_button_enabled'] = $auto_insert_enabled;
        $out['auto_insert_booking_button_selector'] = sanitize_text_field(
            $input['auto_insert_booking_button_selector'] ?? $existing['auto_insert_booking_button_selector'] ?? ''
        );
        $out['setup_notice_enabled'] = ! empty( $input['setup_notice_enabled'] ) ? 1 : 0;
        $out['setup_notice_timeslots'] = ! empty( $input['setup_notice_timeslots'] ) ? 1 : 0;
        $out['setup_notice_services'] = ! empty( $input['setup_notice_services'] ) ? 1 : 0;
        $gcal_feature_enabled = function_exists( 'bookaro_feature_enabled' ) && bookaro_feature_enabled( 'gcal' );
        if ( $gcal_feature_enabled ) {
            $out['setup_notice_gcal_provider'] = ! empty( $input['setup_notice_gcal_provider'] ) ? 1 : 0;
            $out['setup_notice_gcal_customer'] = ! empty( $input['setup_notice_gcal_customer'] ) ? 1 : 0;
        } else {
            if ( array_key_exists( 'setup_notice_gcal_provider', $existing ) ) {
                $out['setup_notice_gcal_provider'] = ! empty( $existing['setup_notice_gcal_provider'] ) ? 1 : 0;
            }
            if ( array_key_exists( 'setup_notice_gcal_customer', $existing ) ) {
                $out['setup_notice_gcal_customer'] = ! empty( $existing['setup_notice_gcal_customer'] ) ? 1 : 0;
            }
        }
        $out['setup_notice_exceptions'] = ! empty( $input['setup_notice_exceptions'] ) ? 1 : 0;
        $out['setup_notice_payouts'] = ! empty( $input['setup_notice_payouts'] ) ? 1 : 0;
        $out['delete_data_on_uninstall'] = ! empty( $input['delete_data_on_uninstall'] ) ? 1 : 0;
        $out['notification_log_retention_days'] = absint(
            $input['notification_log_retention_days'] ?? $existing['notification_log_retention_days'] ?? 180
        );
        $out['log_file_retention_days'] = absint(
            $input['log_file_retention_days'] ?? $existing['log_file_retention_days'] ?? 0
        );
        $out['webhook_queue_retention_days'] = absint(
            $input['webhook_queue_retention_days'] ?? $existing['webhook_queue_retention_days'] ?? 30
        );

        // ---- Setup wizard state ----
        foreach ( $this->get_setup_wizard_flag_keys() as $key ) {
            $out[ $key ] = ! empty( $input[ $key ] ?? $existing[ $key ] ?? 0 ) ? 1 : 0;
        }
        foreach ( $this->get_setup_wizard_id_keys() as $key ) {
            $out[ $key ] = absint( $input[ $key ] ?? $existing[ $key ] ?? 0 );
        }
        foreach ( $this->get_setup_wizard_string_keys() as $key ) {
            $out[ $key ] = sanitize_key( (string) ( $input[ $key ] ?? $existing[ $key ] ?? '' ) );
        }

        $out['booking_min_notice_hours'] = absint(
            $input['booking_min_notice_hours'] ?? $existing['booking_min_notice_hours'] ?? 0
        );
        $out['booking_max_window_days'] = absint(
            $input['booking_max_window_days'] ?? $existing['booking_max_window_days'] ?? 0
        );
        $out['booking_buffer_before_minutes'] = absint(
            $input['booking_buffer_before_minutes'] ?? $existing['booking_buffer_before_minutes'] ?? 0
        );
        $out['booking_buffer_after_minutes'] = absint(
            $input['booking_buffer_after_minutes'] ?? $existing['booking_buffer_after_minutes'] ?? 0
        );
        $out['booking_cancel_window_hours'] = absint(
            $input['booking_cancel_window_hours'] ?? $existing['booking_cancel_window_hours'] ?? 0
        );
        $out['booking_cancel_policy_apply_providers'] = ! empty( $input['booking_cancel_policy_apply_providers'] ) ? 1 : 0;
        $out['booking_cancel_policy_text'] = sanitize_text_field(
            $input['booking_cancel_policy_text'] ?? $existing['booking_cancel_policy_text'] ?? ''
        );
        $out['booking_reschedule_window_hours'] = absint(
            $input['booking_reschedule_window_hours'] ?? $existing['booking_reschedule_window_hours'] ?? 0
        );
        $out['booking_reschedule_policy_text'] = sanitize_text_field(
            $input['booking_reschedule_policy_text'] ?? $existing['booking_reschedule_policy_text'] ?? ''
        );
        $out['auto_approve_bookings'] = ! empty( $input['auto_approve_bookings'] ) ? 1 : 0;
        $out['auto_complete_enabled'] = ! empty( $input['auto_complete_enabled'] ) ? 1 : 0;
        $out['auto_complete_delay_hours'] = absint(
            $input['auto_complete_delay_hours'] ?? $existing['auto_complete_delay_hours'] ?? 0
        );
        $out['auto_expire_enabled'] = ! empty( $input['auto_expire_enabled'] ) ? 1 : 0;
        $out['auto_expire_delay_hours'] = absint(
            $input['auto_expire_delay_hours'] ?? $existing['auto_expire_delay_hours'] ?? 0
        );
        $out['booking_spam_honeypot_enabled'] = ! empty( $input['booking_spam_honeypot_enabled'] ) ? 1 : 0;
        $out['booking_spam_min_fill_seconds'] = absint(
            $input['booking_spam_min_fill_seconds'] ?? $existing['booking_spam_min_fill_seconds'] ?? 4
        );
        $burst_window = absint(
            $input['booking_spam_burst_window_seconds'] ?? $existing['booking_spam_burst_window_seconds'] ?? 300
        );
        if ( $burst_window < 30 ) {
            $burst_window = 30;
        }
        $out['booking_spam_burst_window_seconds'] = $burst_window;
        $out['booking_spam_burst_ip_limit'] = absint(
            $input['booking_spam_burst_ip_limit'] ?? $existing['booking_spam_burst_ip_limit'] ?? 8
        );
        $out['booking_spam_burst_email_limit'] = absint(
            $input['booking_spam_burst_email_limit'] ?? $existing['booking_spam_burst_email_limit'] ?? 5
        );
        $out['booking_spam_duplicate_pending_hours'] = absint(
            $input['booking_spam_duplicate_pending_hours'] ?? $existing['booking_spam_duplicate_pending_hours'] ?? 24
        );
        $out['booking_spam_blacklist_words'] = sanitize_textarea_field(
            $input['booking_spam_blacklist_words'] ?? $existing['booking_spam_blacklist_words'] ?? ''
        );

        // ---- Notifications ----
        $email_template_keys = [ 'new_booking', 'approved', 'rejected', 'cancelled', 'rescheduled', 'reminder_24h', 'reminder_1h', 'review_invite' ];
        $out['notify_new_booking']    = ! empty($input['notify_new_booking'])    ? 1 : 0;
        $out['notify_reminder']       = ! empty($input['notify_reminder'])       ? 1 : 0;
        $out['notify_review_invite']  = ! empty($input['notify_review_invite'])  ? 1 : 0;
        foreach ( $email_template_keys as $template_key ) {
            $toggle_key = "notify_template_{$template_key}";
            $out[ $toggle_key ] = ! empty( $input[ $toggle_key ] ) ? 1 : 0;
        }
        $out['allow_admin_edit_bookings'] = ! empty( $input['allow_admin_edit_bookings'] ) ? 1 : 0;
        $raw_log_email = $input['log_email'] ?? $existing['log_email'] ?? '';
        $out['log_email'] = sanitize_email( $raw_log_email );
        if ( $raw_log_email !== '' && ! is_email( $raw_log_email ) ) {
            $out['log_email'] = '';
            $this->add_settings_error_safe(
                'bookaro_log_email',
                __( 'Error Log Email must be a valid email address.', 'mybookaro' )
            );
        }
        $out['log_php_errors'] = ! empty( $input['log_php_errors'] ) ? 1 : 0;
        $out['log_file_enabled'] = ! empty( $input['log_file_enabled'] ) ? 1 : 0;

        foreach ( $email_template_keys as $key ) {
            $subj = "{$key}_subject";
            $body = "{$key}_body";
            if ( isset($input[$subj]) ) {
                $out[$subj] = sanitize_text_field( $input[$subj] );
            }
            if ( isset($input[$body]) ) {
                $out[$body] = wp_kses_post( $input[$body] );
            }
        }


        // ---- Roles ----
        if ( isset( $input['provider_role'] ) ) {
            $out['provider_role'] = sanitize_text_field( $input['provider_role'] );
        }
        if ( isset( $input['owner_role'] ) ) {
            $out['owner_role'] = sanitize_text_field( $input['owner_role'] );
        }
        if ( function_exists( 'wp_roles' ) ) {
            $roles = wp_roles();
            $role_names = $roles ? array_keys( $roles->get_names() ) : [];
            if ( $out['provider_role'] ?? '' ) {
                if ( ! in_array( $out['provider_role'], $role_names, true ) ) {
                    $out['provider_role'] = '';
                    $this->add_settings_error_safe(
                        'bookaro_provider_role',
                        __( 'Service Provider Role is invalid.', 'mybookaro' )
                    );
                }
            }
            if ( $out['owner_role'] ?? '' ) {
                if ( ! in_array( $out['owner_role'], $role_names, true ) ) {
                    $out['owner_role'] = '';
                    $this->add_settings_error_safe(
                        'bookaro_owner_role',
                        __( 'Customer Role is invalid.', 'mybookaro' )
                    );
                }
            }
        }
        if ( function_exists( 'bookaro_sync_role_caps' ) ) {
            bookaro_sync_role_caps( $out['provider_role'] ?? '', $out['owner_role'] ?? '' );
        }

        $context = [
            'existing' => $existing,
            'settings_context' => $this->build_settings_context(),
        ];
        $filtered = apply_filters( 'bookaro_settings_sanitize', $out, $input, $context );
        if ( is_array( $filtered ) ) {
            $out = $filtered;
        }

        $this->maybe_set_generic_launch_validation_notice_for_settings_update( $existing, $out );

        if ( ! empty( $invalid_colors ) ) {
            $this->add_settings_error_safe(
                'bookaro_invalid_colors',
                __( 'One or more color settings were invalid and have been reset to defaults.', 'mybookaro' )
            );
        }

        if ( ! empty( $out['log_file_enabled'] ) ) {
            Logger::ensure_log_file();
        }

        return $out;
    }

    /**
     * Surface a settings-screen notice if and only if the admin notice API
     * is loaded.
     *
     * Bug 24: add_settings_error() lives in wp-admin/includes/template.php,
     * which is only auto-loaded on admin requests. sanitize_settings() runs
     * not just from the options.php POST flow but ALSO whenever anything
     * calls update_option( OPTION_KEY, … ) — CLI tools, cron migrations,
     * REST endpoints, plugin update routines, our own setup wizard, etc.
     * In those contexts the function is undefined and a bare call fatals
     * the entire option write. Guard it: if the API isn't loaded we
     * silently skip the UI notice (there's no screen to display it on
     * anyway), keeping the option write intact.
     */
    private function add_settings_error_safe( string $code, string $message, string $type = 'error' ): void
    {
        if ( ! function_exists( 'add_settings_error' ) ) {
            return;
        }
        add_settings_error( self::SETTINGS_GROUP, $code, $message, $type );
    }

    private function strip_placeholder_input( array $input ): array
    {
        foreach ( array_keys( $input ) as $key ) {
            if ( ! is_string( $key ) ) {
                continue;
            }
            if (
                strpos( $key, 'bookaro_pro_' ) === 0
                || strpos( $key, 'bookaro_pro_placeholder_' ) === 0
            ) {
                unset( $input[ $key ] );
            }
        }
        return $input;
    }

    private function strip_unregistered_input( array $input ): array
    {
        $context = $this->build_settings_context();
        $tabs = SettingsTabRegistry::build( $context );
        // Use the full UI tab set so Pro fields aren't stripped during sanitize.
        $tabs = apply_filters( 'bookaro_settings_tabs', $tabs, $context );
        $allowed_ids = [];

        foreach ( $tabs as $tab ) {
            if ( ! is_object( $tab ) || ! method_exists( $tab, 'get_fields' ) ) {
                continue;
            }
            $fields = $tab->get_fields();
            if ( empty( $fields ) || ! is_array( $fields ) ) {
                continue;
            }
            foreach ( $fields as $field ) {
                if ( empty( $field['id'] ) ) {
                    continue;
                }
                $allowed_ids[] = (string) $field['id'];
                $args = $field['args'] ?? [];
                if ( is_array( $args ) ) {
                    $allowed_ids = array_merge( $allowed_ids, $this->extract_additional_option_ids( $args ) );
                }
            }
        }

        if ( empty( $allowed_ids ) ) {
            return $input;
        }

        $allowed_ids = array_merge(
            $allowed_ids,
            $this->get_setup_wizard_flag_keys(),
            $this->get_setup_wizard_id_keys(),
            $this->get_setup_wizard_string_keys()
        );
        $allowed_ids = array_unique( array_filter( $allowed_ids ) );
        foreach ( array_keys( $input ) as $key ) {
            if ( ! is_string( $key ) ) {
                continue;
            }
            if ( ! in_array( $key, $allowed_ids, true ) ) {
                unset( $input[ $key ] );
            }
        }

        return $input;
    }

    private function extract_additional_option_ids( array $args ): array
    {
        $ids = [];
        foreach ( [ 'subject_id', 'body_id', 'enabled_id' ] as $arg_key ) {
            $value = isset( $args[ $arg_key ] ) ? sanitize_key( (string) $args[ $arg_key ] ) : '';
            if ( $value !== '' ) {
                $ids[] = $value;
            }
        }
        if ( isset( $args['option_ids'] ) && is_array( $args['option_ids'] ) ) {
            foreach ( $args['option_ids'] as $option_id ) {
                $value = sanitize_key( (string) $option_id );
                if ( $value !== '' ) {
                    $ids[] = $value;
                }
            }
        }
        return array_values( array_unique( $ids ) );
    }

    private function get_setup_wizard_flag_keys(): array
    {
        return [
            'mylisting_setup_completed',
            'generic_setup_completed',
            // Persist the explicit Generic-lane confirmation flag. Without this
            // the sanitizer rebuilt the option from $existing and silently
            // dropped a freshly-written generic_setup_confirmed, so confirming
            // the lane while integration_mode stayed on "auto" never stuck (and
            // any later Settings save would re-trigger the legacy-fallback
            // notice on an auto+Generic site).
            'generic_setup_confirmed',
        ];
    }

    private function get_setup_wizard_id_keys(): array
    {
        return [
            'generic_setup_provider_user_id',
            'generic_setup_listing_id',
            'generic_setup_service_id',
            'generic_setup_booking_page_id',
            'generic_setup_provider_dashboard_page_id',
            'generic_setup_customer_dashboard_page_id',
            'generic_setup_services_page_id',
            // Timestamp of the explicit Generic-lane confirmation (see note in
            // get_setup_wizard_flag_keys()).
            'generic_setup_confirmed_at',
        ];
    }

    private function get_setup_wizard_string_keys(): array
    {
        return [
            // Records which surface confirmed the Generic lane (e.g.
            // wizard_modes / wizard_pages). Preserved so the explicit
            // confirmation survives the settings sanitizer.
            'generic_setup_confirmation_source',
        ];
    }

    /**
     * Render the Settings page HTML (tabs + form).
     */
    public function render_page(): void
    {
        $tabs = [
            'general'       => [ 'label' => __( 'Setup', 'mybookaro' ), 'pro' => false ],
            'appearance'    => [ 'label' => __( 'Branding', 'mybookaro' ), 'pro' => false ],
            'notifications' => [ 'label' => __( 'Email', 'mybookaro' ), 'pro' => false ],
            'roles'         => [ 'label' => __( 'Access', 'mybookaro' ), 'pro' => false ],
            'shortcodes'    => [ 'label' => __( 'Embed', 'mybookaro' ), 'pro' => false ],
        ];
        /**
         * Filters the Settings-page tab navigation and panel list.
         *
         * Each entry is keyed by the tab slug and may provide:
         *   - 'label' => string  Tab button text (required).
         *   - 'pro'   => bool    Optional. Flags a Pro tab for styling.
         *   - 'page'  => string  Optional. Settings API page slug whose sections
         *                        render inside the panel. Defaults to
         *                        "bookaro-booking-settings-{slug}" when omitted.
         *
         * MyBookaro Pro hooks this filter (see bookaro_pro_inject_render_tabs)
         * to expose its eight licensed Settings tabs. Without any filter the
         * five built-in Lite tabs render exactly as before.
         *
         * @param array $tabs    Tab definitions keyed by slug.
         * @param array $context Settings context from build_settings_context().
         */
        $tabs = apply_filters( 'bookaro_settings_render_tabs', $tabs, $this->build_settings_context() );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $current = sanitize_key( wp_unslash( $_GET['tab'] ?? 'general' ) );
        if (! isset($tabs[$current])) {
            $current = 'general';
        }
        $docs_url = $this->build_docs_url( 'general' );
        $next_action = $this->get_next_setup_action();
        $next_url = $next_action['url'] ?? '';
        $next_label = $next_action['label'] ?? '';
        $fallback_url = admin_url( 'admin.php?page=bookaro-bookings' );
        $overview_title = __( 'Setup overview', 'mybookaro' );
        $overview_description = __( 'Quick access to setup guidance, integration health, dashboards, and support.', 'mybookaro' );
        $overview_show_label = __( 'Show overview', 'mybookaro' );
        $overview_hide_label = __( 'Hide overview', 'mybookaro' );
        $help_show_label = __( 'Show help text', 'mybookaro' );
        $help_hide_label = __( 'Hide help text', 'mybookaro' );
        $opts = $this->options->get_all();
        $setup_checklist_items = $this->get_setup_checklist_items();
        $generic_validation_prompt = $this->get_generic_validation_prompt( $opts );
        $show_generic_validation_prompt = ! empty( $generic_validation_prompt['active'] ) && ! empty( $generic_validation_prompt['needs_validation'] );
        $generic_validation_url = $this->get_generic_validation_status_url();
        $generic_validation_message = $this->get_generic_launch_readiness_notice_message();
        $generic_validation_action_label = $this->get_generic_launch_readiness_notice_action_label();
        $lane_summary = $this->build_lane_summary_banner(
            $opts,
            $setup_checklist_items,
            $next_action,
            $generic_validation_prompt
        );
        $options_form_action = admin_url( 'options.php', 'relative' );
        $admin_host_mismatch_notice = $this->get_admin_host_mismatch_notice();
        ?>
        <div class="wrap bookaro-admin bookaro-admin-settings">
            <div class="bookaro-card bookaro-hero">
                <div class="bookaro-hero__content">
                    <div class="bookaro-hero__brand">
                        <img src="<?php echo esc_url( BOOKARO_PLUGIN_URL . 'assets/img/mb-logo.svg' ); ?>" alt="<?php esc_attr_e( 'MyBookaro', 'mybookaro' ); ?>" width="120" height="36" />
                    </div>
                    <h1><?php esc_html_e( 'MyBookaro Settings', 'mybookaro' ); ?></h1>
                    <p class="description"><?php esc_html_e( 'MyBookaro helps listing owners manage booking requests, availability, and customer communication. Compatible with MyListing.', 'mybookaro' ); ?></p>
                </div>
                <div class="bookaro-hero__actions">
                    <?php if ( $next_url !== '' && $next_label !== '' ) : ?>
                        <a class="button button-primary" href="<?php echo esc_url( $next_url ); ?>">
                            <?php
                            printf(
                                /* translators: %s: next step label */
                                esc_html__( 'Next: %s', 'mybookaro' ),
                                esc_html( $next_label )
                            );
                            ?>
                        </a>
                    <?php else : ?>
                        <a class="button button-primary" href="<?php echo esc_url( $fallback_url ); ?>">
                            <?php esc_html_e( 'View bookings', 'mybookaro' ); ?>
                        </a>
                    <?php endif; ?>
                    <a class="button button-secondary" href="<?php echo esc_url( $docs_url ); ?>" target="_blank" rel="noopener">
                        <?php esc_html_e( 'View docs', 'mybookaro' ); ?>
                    </a>
                </div>
            </div>
            <?php TemplateLoader::render( 'admin/components/lane-summary-banner.php', [ 'lane_summary' => $lane_summary ] ); ?>
            <div class="bookaro-overview-panel bookaro-overview-panel--highlight" data-bookaro-overview>
                <div class="bookaro-overview-header">
                    <div class="bookaro-overview-header__copy">
                        <h2><?php echo esc_html( $overview_title ); ?></h2>
                        <p class="description"><?php echo esc_html( $overview_description ); ?></p>
                    </div>
                    <button
                        type="button"
                        class="button bookaro-overview-toggle"
                        aria-expanded="true"
                        aria-controls="bookaro-overview-content"
                        data-label-expanded="<?php echo esc_attr( $overview_hide_label ); ?>"
                        data-label-collapsed="<?php echo esc_attr( $overview_show_label ); ?>"
                    >
                        <?php echo esc_html( $overview_hide_label ); ?>
                    </button>
                </div>
                <div class="bookaro-overview-content" id="bookaro-overview-content">
                    <div class="bookaro-overview-columns">
                        <div class="bookaro-overview-column">
                            <?php $this->render_bookings_snapshot_card(); ?>
                            <?php $this->render_overview_counts_card(); ?>
                            <?php $this->render_integration_health_card(); ?>
                            <?php $this->render_support_resources_card(); ?>
                        </div>
                        <div class="bookaro-overview-column">
                            <?php $this->render_overview_controls_card(); ?>
                            <?php $this->render_setup_checklist_card(); ?>
                            <?php $this->render_dashboard_status_card(); ?>
                        </div>
                    </div>
                </div>
            </div>
            <?php
            $rendered_generic_launch_notice = false;
            $notice_keys = [
                'bookaro_email_test_notice',
                'bookaro_settings_notice',
                self::GENERIC_LAUNCH_NOTICE_KEY,
            ];
            foreach ( $notice_keys as $notice_key ) {
                $notice = get_transient( $notice_key );
                if ( ! is_array( $notice ) ) {
                    continue;
                }
                delete_transient( $notice_key );
                $notice_type = sanitize_html_class( (string) ( $notice['type'] ?? 'success' ) );
                $notice_message = (string) ( $notice['message'] ?? '' );
                if ( $notice_message !== '' ) {
                    echo '<div class="notice notice-' . esc_attr( $notice_type ) . '"><p>' . esc_html( $notice_message ) . '</p>';
                    $notice_action_url = isset( $notice['action_url'] ) ? (string) $notice['action_url'] : '';
                    $notice_action_label = isset( $notice['action_label'] ) ? (string) $notice['action_label'] : '';
                    if ( '' !== trim( $notice_action_url ) && '' !== trim( $notice_action_label ) ) {
                        echo '<p><a class="button button-secondary" href="' . esc_url( $notice_action_url ) . '">' . esc_html( $notice_action_label ) . '</a></p>';
                    }
                    echo '</div>';
                    if ( self::GENERIC_LAUNCH_NOTICE_KEY === $notice_key ) {
                        $rendered_generic_launch_notice = true;
                    }
                }
            }
            if ( $show_generic_validation_prompt && ! $rendered_generic_launch_notice ) {
                echo '<div class="notice notice-warning"><p>' . esc_html( $generic_validation_message ) . '</p>';
                echo '<p><a class="button button-secondary" href="' . esc_url( $generic_validation_url ) . '">' . esc_html( $generic_validation_action_label ) . '</a></p></div>';
            }
            if ( ! empty( $admin_host_mismatch_notice['active'] ) ) {
                $host_settings_url = admin_url( 'options-general.php' );
                echo '<div class="notice notice-warning"><p>' . esc_html( (string) $admin_host_mismatch_notice['message'] ) . '</p>';
                echo '<p><a class="button button-secondary" href="' . esc_url( $host_settings_url ) . '">' . esc_html__( 'Check WordPress Address settings', 'mybookaro' ) . '</a></p></div>';
            }
            ?>
            <?php settings_errors(); ?>
            <div class="bookaro-card bookaro-tabs-card" id="bookaro-settings-tabs">
                <div class="bookaro-tabs-toolbar">
                    <button
                        type="button"
                        class="button bookaro-help-toggle"
                        aria-expanded="true"
                        data-label-expanded="<?php echo esc_attr( $help_hide_label ); ?>"
                        data-label-collapsed="<?php echo esc_attr( $help_show_label ); ?>"
                    >
                        <?php echo esc_html( $help_hide_label ); ?>
                    </button>
                </div>
                <div class="bookaro-tabs" data-current="<?php echo esc_attr( $current ); ?>" role="tablist">
                    <?php $this->render_settings_tab_buttons( $tabs, $current ); ?>
                </div>
            </div>

            <?php $this->render_create_dashboard_pages_form(); ?>
            <form method="post" action="<?php echo esc_url( $options_form_action ); ?>" data-bookaro-settings-main-form="1">
                <?php settings_fields(self::SETTINGS_GROUP); ?>

                <?php $this->render_settings_panels( $tabs, $current ); ?>

                <div class="bookaro-actions">
                    <?php submit_button(); ?>
                </div>
            </form>
            <p class="description bookaro-footer-note">
                <?php esc_html_e( 'MyListing is a third-party product and trademark of its respective owner. MyBookaro is an independent plugin and is not affiliated with, endorsed by, or sponsored by 27collective.', 'mybookaro' ); ?>
            </p>
        </div>
        <?php
    }

    /**
     * Render the Settings-page tab navigation buttons.
     *
     * Extracted from render_page() so the (now filterable) tab list drives the
     * nav uniformly for Lite and Pro tabs and so the markup can be unit-tested.
     * Output is identical to the previous inline foreach.
     *
     * @param array<string,mixed> $tabs    Tab definitions keyed by slug (post-filter).
     * @param string              $current Currently active tab slug.
     */
    private function render_settings_tab_buttons( array $tabs, string $current ): void
    {
        foreach ( $tabs as $slug => $tab ) {
            $slug = sanitize_key( (string) $slug );
            if ( $slug === '' ) {
                continue;
            }
            $label = is_array( $tab ) ? ( $tab['label'] ?? '' ) : $tab;
            $is_active = ( $slug === $current );
            ?>
            <button type="button"
                    id="bookaro-tab-<?php echo esc_attr( $slug ); ?>"
                    role="tab"
                    aria-controls="bookaro-panel-<?php echo esc_attr( $slug ); ?>"
                    aria-selected="<?php echo esc_attr( $is_active ? 'true' : 'false' ); ?>"
                    data-tab="<?php echo esc_attr( $slug ); ?>"
                    class="<?php echo esc_attr( $is_active ? 'active' : '' ); ?>">
                <?php echo esc_html( $label ); ?>
            </button>
            <?php
        }
    }

    /**
     * Render the Settings-page tab panels.
     *
     * Replaces the five previously hardcoded Lite panel <div>s with a single
     * loop over the (filtered) tab list, so Pro tabs injected through
     * bookaro_settings_render_tabs render their own panels. Each Lite panel
     * renders exactly as before.
     *
     * @param array<string,mixed> $tabs    Tab definitions keyed by slug (post-filter).
     * @param string              $current Currently active tab slug.
     */
    private function render_settings_panels( array $tabs, string $current ): void
    {
        foreach ( $tabs as $slug => $tab_meta ) {
            $slug = sanitize_key( (string) $slug );
            if ( $slug === '' ) {
                continue;
            }
            $tab_meta = is_array( $tab_meta ) ? $tab_meta : [];
            $page_slug = $this->get_settings_panel_page_slug( $slug, $tab_meta );
            $is_active = ( $slug === $current );
            $is_pro = ! empty( $tab_meta['pro'] );
            ?>
            <div class="bookaro-card bookaro-settings-panel<?php echo $is_active ? ' active' : ''; ?><?php echo $is_pro ? ' bookaro-settings-panel--pro' : ''; ?>"
                 data-tab="<?php echo esc_attr( $slug ); ?>"
                 id="bookaro-panel-<?php echo esc_attr( $slug ); ?>"
                 role="tabpanel"
                 aria-labelledby="bookaro-tab-<?php echo esc_attr( $slug ); ?>">
                <?php $this->render_tab_help( $slug ); ?>
                <?php do_settings_sections( $page_slug ); ?>
            </div>
            <?php
        }
    }

    /**
     * Resolve the Settings API page slug whose sections render inside a tab panel.
     *
     * Defaults to the Lite convention "bookaro-booking-settings-{slug}" but
     * honours an explicit 'page' on the tab meta. Pro relies on the override:
     * its Custom Forms tab key is "custom_forms" while its sections register
     * against "bookaro-booking-settings-custom-forms" (hyphen, not underscore),
     * so deriving from the slug alone would render an empty panel.
     *
     * @param string              $slug     Tab slug.
     * @param array<string,mixed> $tab_meta Tab definition.
     */
    private function get_settings_panel_page_slug( string $slug, array $tab_meta ): string
    {
        if ( ! empty( $tab_meta['page'] ) ) {
            return sanitize_key( (string) $tab_meta['page'] );
        }

        return 'bookaro-booking-settings-' . sanitize_key( $slug );
    }

    private function render_tab_help( string $tab ): void
    {
        $help = $this->get_tab_help_map();
        if ( empty( $help[ $tab ] ) ) {
            return;
        }

        $item = $help[ $tab ];
        $docs_url = $this->build_docs_url( $tab );
        ?>
        <div class="bookaro-card bookaro-help">
            <div class="bookaro-help__title"><?php esc_html_e( 'Quick guidance', 'mybookaro' ); ?></div>
            <div class="bookaro-help__item">
                <strong><?php esc_html_e( 'What this does:', 'mybookaro' ); ?></strong>
                <?php echo esc_html( $item['what'] ?? '' ); ?>
            </div>
            <div class="bookaro-help__item">
                <strong><?php esc_html_e( 'Recommended default:', 'mybookaro' ); ?></strong>
                <?php echo esc_html( $item['default'] ?? '' ); ?>
            </div>
            <div class="bookaro-help__item">
                <strong><?php esc_html_e( 'Common pitfalls:', 'mybookaro' ); ?></strong>
                <?php echo esc_html( $item['pitfalls'] ?? '' ); ?>
            </div>
            <div class="bookaro-help__link">
                <a href="<?php echo esc_url( $docs_url ); ?>" target="_blank" rel="noopener">
                    <?php esc_html_e( 'Read the docs', 'mybookaro' ); ?>
                </a>
            </div>
        </div>
        <?php
    }

    private function build_docs_url( string $tab ): string
    {
        $base = (string) apply_filters( 'bookaro_docs_url', 'https://mybookaro.com/docs' );
        $base = rtrim( $base, '/' );
        return $base . '/settings#' . rawurlencode( $tab );
    }

    private function build_support_url(): string
    {
        $base = (string) apply_filters( 'bookaro_support_url', 'https://mybookaro.com/support' );
        $base = trim( $base );
        return $base;
    }

    private function get_tab_help_map(): array
    {
        return [
            'general' => [
                'what' => __( 'Guided setup for listings, pages, booking rules, and automation.', 'mybookaro' ),
                'default' => __( 'Generic quick-start: choose a guided path, assign pages, place Booking Button/Services Cards blocks or shortcodes, run System Status validation, then test one booking.', 'mybookaro' ),
                'pitfalls' => __( 'Wrong listing post types, UI mode, or Generic mapping assumptions can prevent booking buttons and provider resolution.', 'mybookaro' ),
            ],
            'appearance' => [
                'what' => __( 'Controls booking UI colors, typography, and modal layout.', 'mybookaro' ),
                'default' => __( 'Keep defaults and adjust the primary color to match your brand.', 'mybookaro' ),
                'pitfalls' => __( 'Low contrast colors or extreme spacing can hurt conversion.', 'mybookaro' ),
            ],
            'notifications' => [
                'what' => __( 'Configure email delivery, diagnostics, and templates.', 'mybookaro' ),
                'default' => __( 'Enable new booking + reminder emails and send a test email.', 'mybookaro' ),
                'pitfalls' => __( 'SMTP misconfiguration causes failed emails; retest after changes.', 'mybookaro' ),
            ],
            'roles' => [
                'what' => __( 'Choose which roles can access provider and customer dashboards.', 'mybookaro' ),
                'default' => __( 'Use MyBookaro roles unless you maintain custom roles.', 'mybookaro' ),
                'pitfalls' => __( 'Incorrect roles or missing capabilities block access.', 'mybookaro' ),
            ],
            'shortcodes' => [
                'what' => __( 'Generate shortcodes and blocks for embedding booking UI.', 'mybookaro' ),
                'default' => __( 'Generic step 3: place Booking Button/Services Cards blocks or shortcodes after Step 2 (assign pages).', 'mybookaro' ),
                'pitfalls' => __( 'Missing booking context (listing_id or provider_id) can cause empty booking forms. Some themes ignore shortcodes.', 'mybookaro' ),
            ],
        ];
    }

    public function handle_test_email(): void
    {
        if ( ! $this->can_manage_admin() ) {
            wp_die( esc_html__( 'You do not have permission to perform this action.', 'mybookaro' ) );
        }

        check_admin_referer( 'bookaro_email_test', 'bookaro_email_test_nonce' );

        $payload = is_array( $_POST ) ? wp_unslash( $_POST ) : [];
        $opts = $this->options->get_all();
        $fallback_email = sanitize_email( $opts['log_email'] ?? '' );
        if ( $fallback_email === '' ) {
            $fallback_email = sanitize_email( get_option( 'admin_email' ) );
        }

        $email = sanitize_email( $payload['bookaro_test_email'] ?? '' );
        if ( $email === '' ) {
            $email = $fallback_email;
        }

        if ( $email === '' || ! is_email( $email ) ) {
            $this->send_test_action_response(
                'bookaro_email_test_mode',
                'bookaro_email_test_notice',
                false,
                __( 'Please enter a valid test email address.', 'mybookaro' ),
                $this->get_settings_tab_url( 'notifications' )
            );
            return;
        }

        $subject = __( 'MyBookaro test email', 'mybookaro' );
        $message = __( 'This is a test email from MyBookaro. If you received it, your email settings are working.', 'mybookaro' );
        $mail_error = null;
        $mail_failed_handler = static function( $wp_error ) use ( &$mail_error ): void {
            $mail_error = $wp_error;
        };
        add_action( 'wp_mail_failed', $mail_failed_handler, 10, 1 );
        $sent = wp_mail( $email, $subject, $message );
        remove_action( 'wp_mail_failed', $mail_failed_handler, 10 );

        if ( $sent ) {
        $this->send_test_action_response(
            'bookaro_email_test_mode',
            'bookaro_email_test_notice',
            true,
            sprintf(
                    /* translators: %s: email address */
                    __( 'Test email sent to %s.', 'mybookaro' ),
                    $email
                ),
                $this->get_settings_tab_url( 'notifications' )
            );
            return;
        }

        $log_context = [
            'email' => $email,
            'subject' => $subject,
        ];
        if ( $mail_error instanceof \WP_Error ) {
            $log_context['error_message'] = $mail_error->get_error_message();
            $log_context['error_data'] = $mail_error->get_error_data();
        }

        Logger::log( 'Test email failed.', $log_context, false );

        $this->send_test_action_response(
            'bookaro_email_test_mode',
            'bookaro_email_test_notice',
            false,
            __( 'Test email failed. Check your site mail configuration.', 'mybookaro' ),
            $this->get_settings_tab_url( 'notifications' )
        );
    }

    public function handle_create_dashboard_pages(): void
    {
        if ( ! $this->can_manage_admin() ) {
            wp_die( esc_html__( 'You do not have permission to perform this action.', 'mybookaro' ) );
        }

        check_admin_referer( 'bookaro_create_dashboard_pages', 'bookaro_create_dashboard_pages_nonce' );

        $opts = $this->options->get_all();
        $ui_mode = $this->resolve_ui_mode_from_options( $opts );

        $redirect = $this->get_settings_tab_url( 'general' );

        if ( 'shortcodes' !== $ui_mode ) {
            set_transient( 'bookaro_settings_notice', [
                'type' => 'error',
                'message' => __( 'Switch UI mode to Shortcodes before creating pages.', 'mybookaro' ),
            ], 30 );
            wp_safe_redirect( $redirect );
            exit;
        }

        $updated = $opts;
        $created = 0;

        $booking_current = absint( $opts['booking_page_id'] ?? 0 );
        $booking_missing = ! ( $booking_current > 0 && get_post( $booking_current ) );
        $booking_id = $this->create_page_if_missing(
            $booking_current,
            'bookaro-booking',
            __( 'Book a Service', 'mybookaro' ),
            $this->build_booking_form_block()
        );
        if ( $booking_id > 0 ) {
            $updated['booking_page_id'] = $booking_id;
            if ( $booking_missing ) {
                $created++;
            }
        }

        $provider_current = absint( $opts['provider_dashboard_page_id'] ?? 0 );
        $provider_missing = ! ( $provider_current > 0 && get_post( $provider_current ) );
        $provider_id = $this->create_page_if_missing(
            $provider_current,
            'bookaro-provider-dashboard',
            __( 'MyBookaro Provider Dashboard', 'mybookaro' ),
            $this->build_provider_dashboard_block()
        );
        if ( $provider_id > 0 ) {
            $updated['provider_dashboard_page_id'] = $provider_id;
            if ( $provider_missing ) {
                $created++;
            }
        }

        $customer_current = absint( $opts['customer_dashboard_page_id'] ?? 0 );
        $customer_missing = ! ( $customer_current > 0 && get_post( $customer_current ) );
        $customer_id = $this->create_page_if_missing(
            $customer_current,
            'bookaro-customer-dashboard',
            __( 'MyBookaro Customer Dashboard', 'mybookaro' ),
            $this->build_customer_dashboard_block()
        );
        if ( $customer_id > 0 ) {
            $updated['customer_dashboard_page_id'] = $customer_id;
            if ( $customer_missing ) {
                $created++;
            }
        }

        if ( $updated !== $opts ) {
            $this->options->update( $updated );
            $this->maybe_set_generic_launch_validation_notice_for_settings_update( $opts, $updated );
        }

        set_transient( 'bookaro_settings_notice', [
            'type' => 'success',
            'message' => $created > 0
                ? __( 'Dashboard pages created and linked.', 'mybookaro' )
                : __( 'All dashboard pages are already set.', 'mybookaro' ),
        ], 30 );
        wp_safe_redirect( $redirect );
        exit;
    }

    private function create_page_if_missing( int $current_id, string $slug, string $title, string $content ): int
    {
        if ( $current_id > 0 && get_post( $current_id ) ) {
            return $current_id;
        }

        $existing = get_page_by_path( $slug );
        if ( $existing ) {
            return (int) $existing->ID;
        }

        $page_id = wp_insert_post( [
            'post_type' => 'page',
            'post_title' => $title,
            'post_name' => $slug,
            'post_content' => $content,
            'post_status' => 'publish',
            'post_author' => get_current_user_id(),
        ] );

        return is_wp_error( $page_id ) ? 0 : (int) $page_id;
    }

    private function build_booking_form_block(): string
    {
        $attrs = [ 'autoOpen' => true ];
        $json = wp_json_encode( $attrs );
        if ( ! is_string( $json ) ) {
            $json = '{}';
        }
        return sprintf( '<!-- wp:bookaro/booking-form %s /-->', $json );
    }

    private function build_provider_dashboard_block(): string
    {
        return '<!-- wp:bookaro/provider-dashboard /-->';
    }

    private function build_customer_dashboard_block(): string
    {
        return '<!-- wp:bookaro/customer-dashboard /-->';
    }

    private function send_test_action_response(
        string $mode_key,
        string $transient_key,
        bool $success,
        string $message,
        string $redirect_url
    ): void {
        // This is a private response-formatter. Every caller
        // (send_test_email_action, etc.) has already verified its own
        // admin nonce above. Re-verifying here would require
        // propagating the nonce key through every signature and would
        // be redundant; annotating the read is the WP.org-accepted
        // pattern for "trusted internal call site".
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        $mode = isset( $_POST[ $mode_key ] )
            // phpcs:ignore WordPress.Security.NonceVerification.Missing
            ? sanitize_key( wp_unslash( $_POST[ $mode_key ] ) )
            : '';
        if ( $mode === 'inline' ) {
            if ( $success ) {
                wp_send_json_success( [ 'message' => $message ] );
            }
            wp_send_json_error( [ 'message' => $message ] );
        }

        set_transient( $transient_key, [
            'type'    => $success ? 'success' : 'error',
            'message' => $message,
        ], 30 );

        wp_safe_redirect( $redirect_url );
        exit;
    }

    private function render_bookings_snapshot_card(): void
    {
        $cache_group = 'bookaro_admin_overview';
        $cache_key = 'bookings_snapshot';
        $data = wp_cache_get( $cache_key, $cache_group );

        if ( ! is_array( $data ) ) {
            $data = [
                'pending' => 0,
                'upcoming' => 0,
                'recent_total' => 0,
                'recent_completed' => 0,
                'has_table' => false,
            ];

            $db = new BookingRepository();
            $table = esc_sql( $db->get_table_name() );
            $wpdb = $GLOBALS['wpdb'] ?? null;
            if ( $wpdb instanceof \wpdb ) {
                $exists = $wpdb->get_var(
                    $wpdb->prepare( 'SHOW TABLES LIKE %s', $table )
                );
                $data['has_table'] = ! empty( $exists );
            }

            if ( ! empty( $data['has_table'] ) && $wpdb instanceof \wpdb ) {
                $data['pending'] = (int) $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT COUNT(*) FROM {$table} WHERE status = %s",
                        'pending'
                    )
                );

                // Bug 32: date_i18n() expects UTC and adds gmt_offset itself.
                // current_time('timestamp') was already WP-localized, so the
                // operational-summary "upcoming bookings (next 7 days)" stat
                // was bounding the SQL BETWEEN with a doubly-offset window.
                $now_ts = time();
                $upcoming_start = date_i18n( 'Y-m-d H:i:s', $now_ts );
                $upcoming_end = date_i18n( 'Y-m-d H:i:s', $now_ts + ( 7 * DAY_IN_SECONDS ) );
                $data['upcoming'] = (int) $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT COUNT(*) FROM {$table}
                         WHERE booking_date BETWEEN %s AND %s
                           AND status IN (%s, %s)",
                        $upcoming_start,
                        $upcoming_end,
                        'pending',
                        'approved'
                    )
                );

                $start_date = date_i18n( 'Y-m-d', $now_ts - ( 30 * DAY_IN_SECONDS ) );
                $end_date = date_i18n( 'Y-m-d', $now_ts );
                $counts = $db->get_booking_status_counts( 0, $start_date, $end_date );
                $data['recent_total'] = (int) array_sum( $counts );
                $data['recent_completed'] = (int) ( $counts['completed'] ?? 0 );
            }

            wp_cache_set( $cache_key, $data, $cache_group, 300 );
        }

        $pending_url = admin_url( 'admin.php?page=bookaro-bookings&status=pending' );
        $bookings_url = admin_url( 'admin.php?page=bookaro-bookings' );
        $has_activity = ( (int) $data['pending'] + (int) $data['upcoming'] + (int) $data['recent_total'] ) > 0;
        ?>
        <div class="bookaro-card bookaro-overview-card bookaro-overview-snapshot">
            <div class="bookaro-card__header">
                <h2><?php esc_html_e( 'Booking activity', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'Last 30 days performance and upcoming workload.', 'mybookaro' ); ?></p>
            </div>
            <div class="bookaro-overview-kpi-grid">
                <div class="bookaro-overview-kpi">
                    <div class="bookaro-overview-kpi__label"><?php esc_html_e( 'Pending approvals', 'mybookaro' ); ?></div>
                    <div class="bookaro-overview-kpi__value"><?php echo esc_html( (string) $data['pending'] ); ?></div>
                </div>
                <div class="bookaro-overview-kpi">
                    <div class="bookaro-overview-kpi__label"><?php esc_html_e( 'Upcoming 7 days', 'mybookaro' ); ?></div>
                    <div class="bookaro-overview-kpi__value"><?php echo esc_html( (string) $data['upcoming'] ); ?></div>
                </div>
                <div class="bookaro-overview-kpi">
                    <div class="bookaro-overview-kpi__label"><?php esc_html_e( 'Bookings (30 days)', 'mybookaro' ); ?></div>
                    <div class="bookaro-overview-kpi__value"><?php echo esc_html( (string) $data['recent_total'] ); ?></div>
                </div>
                <div class="bookaro-overview-kpi">
                    <div class="bookaro-overview-kpi__label"><?php esc_html_e( 'Completed (30 days)', 'mybookaro' ); ?></div>
                    <div class="bookaro-overview-kpi__value"><?php echo esc_html( (string) $data['recent_completed'] ); ?></div>
                </div>
            </div>
            <div class="bookaro-actions bookaro-overview-inline">
                <a class="button button-small" href="<?php echo esc_url( $pending_url ); ?>">
                    <?php esc_html_e( 'View pending', 'mybookaro' ); ?>
                </a>
                <a class="button button-small button-secondary" href="<?php echo esc_url( $bookings_url ); ?>">
                    <?php esc_html_e( 'View all bookings', 'mybookaro' ); ?>
                </a>
            </div>
            <?php if ( ! $has_activity ) : ?>
                <p class="description bookaro-overview-muted"><?php esc_html_e( 'No bookings yet.', 'mybookaro' ); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    private function render_overview_controls_card(): void
    {
        $general_url = $this->get_settings_tab_url( 'general' );
        $notifications_url = $this->get_settings_tab_url( 'notifications' );
        $options_form_action = admin_url( 'options.php', 'relative' );
        ?>
        <div class="bookaro-card bookaro-overview-card bookaro-overview-controls">
            <div class="bookaro-card__header">
                <h2><?php esc_html_e( 'Quick controls', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'Toggle core booking behavior without leaving this page.', 'mybookaro' ); ?></p>
            </div>
            <form method="post" action="<?php echo esc_url( $options_form_action ); ?>">
                <?php settings_fields( self::SETTINGS_GROUP ); ?>
                <div class="bookaro-overview-controls__grid">
                    <?php
                    $checkboxes = [
                        'frontend_enabled' => __( 'Frontend UI', 'mybookaro' ),
                        'auto_approve_bookings' => __( 'Auto-approve bookings', 'mybookaro' ),
                        'auto_complete_enabled' => __( 'Auto-complete bookings', 'mybookaro' ),
                        'auto_expire_enabled' => __( 'Auto-expire pending', 'mybookaro' ),
                        'notify_new_booking' => __( 'New booking emails', 'mybookaro' ),
                        'notify_reminder' => __( 'Reminder emails', 'mybookaro' ),
                        'notify_review_invite' => __( 'Review invite emails', 'mybookaro' ),
                    ];
                    foreach ( $checkboxes as $id => $label ) {
                        $this->render_checkbox_field( [
                            'id' => $id,
                            'label' => $label,
                            'wrapper_class' => 'bookaro-overview-controls__item',
                        ] );
                    }
                    ?>
                </div>
                <div class="bookaro-actions">
                    <?php submit_button( __( 'Save Quick Settings', 'mybookaro' ), 'primary', 'submit', false ); ?>
                    <a class="button button-secondary" href="<?php echo esc_url( $general_url ); ?>">
                        <?php esc_html_e( 'Open general settings', 'mybookaro' ); ?>
                    </a>
                    <a class="button button-secondary" href="<?php echo esc_url( $notifications_url ); ?>">
                        <?php esc_html_e( 'Open notifications', 'mybookaro' ); ?>
                    </a>
                </div>
            </form>
        </div>
        <?php
    }

    private function render_overview_counts_card(): void
    {
        $opts = $this->options->get_all();
        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : '';
        $is_generic = ( 'generic' === $integration_mode );
        $provider_role = sanitize_key( (string) ( $opts['provider_role'] ?? '' ) );
        $customer_role = sanitize_key( (string) ( $opts['owner_role'] ?? '' ) );
        $provider_role = $provider_role !== '' ? $provider_role : 'bookaro_provider';
        $customer_role = $customer_role !== '' ? $customer_role : 'bookaro_customer';

        $listing_types = $opts['allowed_listing_post_types'] ?? [];
        if ( ! is_array( $listing_types ) ) {
            $listing_types = [ $listing_types ];
        }
        if ( empty( $listing_types ) && ! empty( $opts['listing_post_type'] ) ) {
            $listing_types = [ $opts['listing_post_type'] ];
        }
        $listing_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $listing_types ) ) ) );
        if ( empty( $listing_types ) && function_exists( 'bookaro_get_allowed_listing_post_types' ) ) {
            $listing_types = bookaro_get_allowed_listing_post_types();
        }

        $cache_group = 'bookaro_admin_overview';
        $cache_key = 'overview_counts_' . md5( wp_json_encode( [
            'provider_role' => $provider_role,
            'customer_role' => $customer_role,
            'listing_types' => $listing_types,
        ] ) );
        $data = wp_cache_get( $cache_key, $cache_group );

        if ( ! is_array( $data ) ) {
            $data = [
                'providers' => 0,
                'customers' => 0,
                'listings' => 0,
                'services' => 0,
            ];
            $user_counts = count_users();
            if ( is_array( $user_counts ) && ! empty( $user_counts['avail_roles'] ) ) {
                $roles = $user_counts['avail_roles'];
                $data['providers'] = (int) ( $roles[ $provider_role ] ?? 0 );
                $data['customers'] = (int) ( $roles[ $customer_role ] ?? 0 );
            }

            if ( ! empty( $listing_types ) ) {
                foreach ( $listing_types as $type ) {
                    if ( ! post_type_exists( $type ) ) {
                        continue;
                    }
                    $counts = wp_count_posts( $type );
                    if ( $counts instanceof \stdClass ) {
                        $data['listings'] += (int) ( $counts->publish ?? 0 );
                    }
                }
            }

            if ( defined( 'BOOKARO_SERVICE_POST_TYPE' ) && post_type_exists( BOOKARO_SERVICE_POST_TYPE ) ) {
                $counts = wp_count_posts( BOOKARO_SERVICE_POST_TYPE );
                if ( $counts instanceof \stdClass ) {
                    $data['services'] = (int) ( $counts->publish ?? 0 );
                }
            }

            wp_cache_set( $cache_key, $data, $cache_group, 300 );
        }

        $provider_users_url = admin_url( 'users.php?role=' . rawurlencode( $provider_role ) );
        $customer_users_url = admin_url( 'users.php?role=' . rawurlencode( $customer_role ) );
        $listing_action_url = '';
        if ( ! empty( $listing_types ) ) {
            $listing_action_url = admin_url( 'edit.php?post_type=' . rawurlencode( (string) $listing_types[0] ) );
        }
        $services_url = defined( 'BOOKARO_SERVICE_POST_TYPE' )
            ? admin_url( 'edit.php?post_type=' . rawurlencode( BOOKARO_SERVICE_POST_TYPE ) )
            : '';
        $general_url = $this->get_settings_tab_url( 'general' );
        ?>
        <div class="bookaro-card bookaro-overview-card bookaro-overview-counts">
            <div class="bookaro-card__header">
                <h2><?php esc_html_e( 'Key totals', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'People and content totals that power bookings.', 'mybookaro' ); ?></p>
            </div>
            <div class="bookaro-status-list">
                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'Providers', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill bookaro-status-pill--info"><?php echo esc_html( (string) $data['providers'] ); ?></span>
                        <div class="bookaro-status-item__meta">
                            <?php
                            printf(
                                /* translators: %s: role key */
                                esc_html__( 'Role: %s', 'mybookaro' ),
                                esc_html( $provider_role )
                            );
                            ?>
                        </div>
                        <div class="bookaro-status-item__actions">
                            <a class="button button-small" href="<?php echo esc_url( $provider_users_url ); ?>">
                                <?php esc_html_e( 'View users', 'mybookaro' ); ?>
                            </a>
                        </div>
                    </div>
                </div>

                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'Customers', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill bookaro-status-pill--info"><?php echo esc_html( (string) $data['customers'] ); ?></span>
                        <div class="bookaro-status-item__meta">
                            <?php
                            printf(
                                /* translators: %s: role key */
                                esc_html__( 'Role: %s', 'mybookaro' ),
                                esc_html( $customer_role )
                            );
                            ?>
                        </div>
                        <div class="bookaro-status-item__actions">
                            <a class="button button-small" href="<?php echo esc_url( $customer_users_url ); ?>">
                                <?php esc_html_e( 'View users', 'mybookaro' ); ?>
                            </a>
                        </div>
                    </div>
                </div>

                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'Listings', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill bookaro-status-pill--info"><?php echo esc_html( (string) $data['listings'] ); ?></span>
                        <div class="bookaro-status-item__meta">
                            <?php if ( empty( $listing_types ) ) : ?>
                                <?php esc_html_e( 'No listing post types configured.', 'mybookaro' ); ?>
                            <?php else : ?>
                                <?php
                                printf(
                                    /* translators: %s: listing post types */
                                    esc_html__( 'Types: %s', 'mybookaro' ),
                                    esc_html( implode( ', ', $listing_types ) )
                                );
                                ?>
                            <?php endif; ?>
                        </div>
                        <div class="bookaro-status-item__actions">
                            <?php if ( $listing_action_url !== '' ) : ?>
                                <a class="button button-small" href="<?php echo esc_url( $listing_action_url ); ?>">
                                    <?php esc_html_e( 'View listings', 'mybookaro' ); ?>
                                </a>
                            <?php else : ?>
                                <a class="button button-small" href="<?php echo esc_url( $general_url ); ?>">
                                    <?php esc_html_e( 'Configure types', 'mybookaro' ); ?>
                                </a>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>

                <?php if ( $is_generic ) : ?>
                    <div class="bookaro-status-item">
                        <div class="bookaro-status-item__label"><?php esc_html_e( 'Services', 'mybookaro' ); ?></div>
                        <div class="bookaro-status-item__value">
                            <span class="bookaro-status-pill bookaro-status-pill--info"><?php echo esc_html( (string) $data['services'] ); ?></span>
                            <div class="bookaro-status-item__meta">
                                <?php if ( defined( 'BOOKARO_SERVICE_POST_TYPE' ) && post_type_exists( BOOKARO_SERVICE_POST_TYPE ) ) : ?>
                                    <?php
                                    printf(
                                        /* translators: %s: service post type */
                                        esc_html__( 'Post type: %s', 'mybookaro' ),
                                        esc_html( BOOKARO_SERVICE_POST_TYPE )
                                    );
                                    ?>
                                <?php else : ?>
                                    <?php esc_html_e( 'Services post type not registered yet.', 'mybookaro' ); ?>
                                <?php endif; ?>
                            </div>
                            <?php if ( $services_url !== '' ) : ?>
                                <div class="bookaro-status-item__actions">
                                    <a class="button button-small" href="<?php echo esc_url( $services_url ); ?>">
                                        <?php esc_html_e( 'View services', 'mybookaro' ); ?>
                                    </a>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>
                <?php endif; ?>
            </div>
        </div>
        <?php
    }

    private function render_setup_checklist_card(): void
    {
        $items = $this->get_setup_checklist_items();
        if ( empty( $items ) ) {
            return;
        }

        $completed = 0;
        foreach ( $items as $item ) {
            if ( ! empty( $item['completed'] ) ) {
                $completed++;
            }
        }
        $total = count( $items );
        $percent = $total > 0 ? (int) round( ( $completed / $total ) * 100 ) : 0;
        ?>
        <div class="bookaro-card bookaro-checklist">
            <div class="bookaro-checklist__header">
                <div>
                    <h2><?php esc_html_e( 'Setup Checklist', 'mybookaro' ); ?></h2>
                    <p class="description"><?php esc_html_e( 'Complete these steps to start taking bookings.', 'mybookaro' ); ?></p>
                </div>
                <div class="bookaro-checklist__progress">
                    <div class="bookaro-checklist__count">
                        <?php
                        echo esc_html(
                            sprintf(
                                /* translators: 1: completed steps, 2: total steps */
                                __( '%1$d of %2$d completed', 'mybookaro' ),
                                $completed,
                                $total
                            )
                        );
                        ?>
                    </div>
                    <div class="bookaro-progress">
                        <div class="bookaro-progress__bar" style="width:<?php echo esc_attr( (string) $percent ); ?>%;"></div>
                    </div>
                </div>
            </div>
            <ul class="bookaro-checklist__list">
                <?php foreach ( $items as $item ) : ?>
                    <?php
                    $is_done = ! empty( $item['completed'] );
                    $icon = $is_done ? 'dashicons-yes' : 'dashicons-minus';
                    ?>
                    <li class="bookaro-checklist__item <?php echo esc_attr( $is_done ? 'is-done' : '' ); ?>">
                        <span class="dashicons <?php echo esc_attr( $icon ); ?> bookaro-checklist__icon"></span>
                        <div class="bookaro-checklist__body">
                            <div class="bookaro-checklist__title"><?php echo esc_html( $item['label'] ?? '' ); ?></div>
                            <?php if ( ! empty( $item['description'] ) ) : ?>
                                <div class="description"><?php echo esc_html( $item['description'] ); ?></div>
                            <?php endif; ?>
                        </div>
                        <?php if ( ! empty( $item['url'] ) ) : ?>
                            <a class="button button-small" href="<?php echo esc_url( $item['url'] ); ?>">
                                <?php esc_html_e( 'Open', 'mybookaro' ); ?>
                            </a>
                        <?php endif; ?>
                    </li>
                <?php endforeach; ?>
            </ul>
        </div>
        <?php
    }

    private function get_next_setup_action(): array
    {
        $items = $this->get_setup_checklist_items();
        foreach ( $items as $item ) {
            if ( ! empty( $item['completed'] ) ) {
                continue;
            }
            $url = $item['url'] ?? '';
            $label = $item['label'] ?? '';
            if ( $url !== '' && $label !== '' ) {
                return [
                    'url' => $url,
                    'label' => $label,
                ];
            }
        }
        return [];
    }

    private function build_lane_summary_banner(
        array $opts,
        array $checklist_items,
        array $next_action,
        array $generic_validation_prompt
    ): array {
        $resolved_mode = $this->resolve_integration_mode_from_options( $opts );
        $site_type = $this->resolve_site_type_from_options( $opts );
        $lane_label = AdminLaneSummary::resolve_lane_label( '', $resolved_mode, $site_type );

        $pending_item = AdminLaneSummary::find_first_incomplete( $checklist_items, 'completed' );
        $top_blocker = '';
        if ( is_array( $pending_item ) ) {
            $top_blocker = trim( (string) ( $pending_item['description'] ?? '' ) );
            if ( '' === $top_blocker ) {
                $top_blocker = trim( (string) ( $pending_item['label'] ?? '' ) );
            }
        }

        $next_action_payload = [
            'label' => trim( (string) ( $next_action['label'] ?? '' ) ),
            'url' => trim( (string) ( $next_action['url'] ?? '' ) ),
            'detail' => '',
        ];

        if ( '' === $next_action_payload['url'] || '' === $next_action_payload['label'] ) {
            if ( is_array( $pending_item ) ) {
                $next_action_payload['label'] = trim( (string) ( $pending_item['label'] ?? __( 'Open next setup step', 'mybookaro' ) ) );
                $next_action_payload['url'] = trim( (string) ( $pending_item['url'] ?? '' ) );
            }
        }

        $show_generic_validation_prompt = ( 'generic' === $resolved_mode )
            && ! empty( $generic_validation_prompt['active'] )
            && ! empty( $generic_validation_prompt['needs_validation'] );
        if ( $show_generic_validation_prompt ) {
            $top_blocker = $this->get_generic_launch_readiness_notice_message();
            $next_action_payload = [
                'label' => $this->get_generic_launch_readiness_notice_action_label(),
                'url' => $this->get_generic_validation_status_url(),
                'detail' => __( 'Run System Status validation before continuing with launch checks.', 'mybookaro' ),
            ];
        } elseif ( is_array( $pending_item ) && '' === trim( (string) ( $next_action_payload['detail'] ?? '' ) ) ) {
            $next_action_payload['detail'] = sprintf(
                /* translators: %s: pending checklist step label */
                __( 'Next step: %s', 'mybookaro' ),
                (string) ( $pending_item['label'] ?? __( 'Setup step', 'mybookaro' ) )
            );
        }

        $state = 'ok';
        $state_label = __( 'Ready', 'mybookaro' );
        if ( $show_generic_validation_prompt ) {
            $state = 'warn';
            $state_label = __( 'Needs attention', 'mybookaro' );
        } elseif ( is_array( $pending_item ) ) {
            $state = 'warn';
            $state_label = __( 'Needs attention', 'mybookaro' );
        }

        if ( '' === $top_blocker ) {
            $top_blocker = __( 'No immediate setup blockers detected. Before launching, run the full readiness checklist on the System Status page to confirm services, repeater wiring, and email templates are wired up.', 'mybookaro' );
        }

        return AdminLaneSummary::normalize( [
            'title' => __( 'Lane summary', 'mybookaro' ),
            'summary' => __( 'Shared lane snapshot with the highest-priority blocker and next action.', 'mybookaro' ),
            'state' => $state,
            'state_label' => $state_label,
            'lane_label' => $lane_label,
            'top_blocker' => $top_blocker,
            'next_action' => $next_action_payload,
        ] );
    }

    private function render_integration_health_card(): void
    {
        $items = $this->get_integration_health_items();
        if ( empty( $items ) ) {
            return;
        }

        $status_map = [
            'ok' => [
                'label'  => __( 'Ready', 'mybookaro' ),
                'bg'     => '#dcfce7',
                'border' => '#bbf7d0',
                'color'  => '#166534',
            ],
            'warning' => [
                'label'  => __( 'Needs attention', 'mybookaro' ),
                'bg'     => '#fef3c7',
                'border' => '#fde68a',
                'color'  => '#92400e',
            ],
            'info' => [
                'label'  => __( 'Optional', 'mybookaro' ),
                'bg'     => '#e2e8f0',
                'border' => '#cbd5e1',
                'color'  => '#475569',
            ],
            'locked' => [
                'label'  => __( 'Pro', 'mybookaro' ),
                'bg'     => '#e9d5ff',
                'border' => '#d8b4fe',
                'color'  => '#6b21a8',
            ],
        ];
        ?>
        <div class="bookaro-card bookaro-health">
            <div class="bookaro-health__header">
                <h2><?php esc_html_e( 'Integration Health', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'Review integration readiness and fix missing configuration.', 'mybookaro' ); ?></p>
            </div>
            <div class="bookaro-health__list">
                <?php foreach ( $items as $item ) : ?>
                    <?php
                    $status = $item['status'] ?? 'info';
                    $styles = $status_map[ $status ] ?? $status_map['info'];
                    ?>
                    <div class="bookaro-health__item">
                        <div class="bookaro-health__label"><?php echo esc_html( $item['label'] ?? '' ); ?></div>
                        <span class="bookaro-health__status" style="border-color:<?php echo esc_attr( $styles['border'] ); ?>; background:<?php echo esc_attr( $styles['bg'] ); ?>; color:<?php echo esc_attr( $styles['color'] ); ?>;">
                            <?php echo esc_html( $styles['label'] ); ?>
                        </span>
                        <div class="description bookaro-health__message">
                            <?php echo esc_html( $item['message'] ?? '' ); ?>
                        </div>
                        <?php if ( ! empty( $item['url'] ) ) : ?>
                            <a class="button button-small" href="<?php echo esc_url( $item['url'] ); ?>"><?php esc_html_e( 'Open', 'mybookaro' ); ?></a>
                        <?php endif; ?>
                    </div>
                <?php endforeach; ?>
            </div>
        </div>
        <?php
    }

    private function render_dashboard_status_card(): void
    {
        $opts = $this->options->get_all();
        $resolved_mode = $this->resolve_integration_mode_from_options( $opts );
        $ui_mode = $this->resolve_ui_mode_from_options( $opts );
        $frontend_enabled = array_key_exists( 'frontend_enabled', $opts )
            ? ! empty( $opts['frontend_enabled'] )
            : true;
        $provider_page_id = absint( $opts['provider_dashboard_page_id'] ?? 0 );
        $customer_page_id = absint( $opts['customer_dashboard_page_id'] ?? 0 );

        $roles = function_exists( 'wp_roles' ) ? ( wp_roles() ? wp_roles()->get_names() : [] ) : [];
        $provider_role = sanitize_key( (string) ( $opts['provider_role'] ?? '' ) );
        $owner_role = sanitize_key( (string) ( $opts['owner_role'] ?? '' ) );
        $provider_role_label = ( $provider_role !== '' && isset( $roles[ $provider_role ] ) )
            ? $roles[ $provider_role ]
            : __( 'MyBookaro Provider', 'mybookaro' );
        $owner_role_label = ( $owner_role !== '' && isset( $roles[ $owner_role ] ) )
            ? $roles[ $owner_role ]
            : __( 'MyBookaro Customer', 'mybookaro' );
        $roles_custom = ( $provider_role !== '' || $owner_role !== '' );

        $settings_url = admin_url( 'admin.php?page=bookaro-booking-settings&tab=general#bookaro-settings-tabs' );
        $roles_url = admin_url( 'admin.php?page=bookaro-booking-settings&tab=roles#bookaro-settings-tabs' );
        $shortcodes_url = admin_url( 'admin.php?page=bookaro-booking-settings&tab=shortcodes#bookaro-settings-tabs' );

        $ui_label = ( 'shortcodes' === $ui_mode )
            ? __( 'Shortcodes', 'mybookaro' )
            : __( 'WooCommerce My Account', 'mybookaro' );
        if ( 'shortcodes' === $ui_mode ) {
            $ui_meta = ( 'generic' === $resolved_mode )
                ? __( 'Recommended Generic path: dashboards appear on pages you assign with explicit block/shortcode placement.', 'mybookaro' )
                : __( 'Dashboards appear on pages you choose.', 'mybookaro' );
        } else {
            $ui_meta = ( 'generic' === $resolved_mode )
                ? __( 'WooCommerce My Account dashboards are active. For Generic, Shortcodes is the primary flow.', 'mybookaro' )
                : __( 'Dashboards appear under WooCommerce My Account.', 'mybookaro' );
        }

        $frontend_status_label = $frontend_enabled ? __( 'Enabled', 'mybookaro' ) : __( 'Disabled', 'mybookaro' );
        $frontend_status_class = $frontend_enabled ? 'bookaro-status-pill--ok' : 'bookaro-status-pill--warn';

        $roles_status_label = $roles_custom ? __( 'Custom roles', 'mybookaro' ) : __( 'Default roles', 'mybookaro' );
        $roles_status_class = $roles_custom ? 'bookaro-status-pill--ok' : 'bookaro-status-pill--info';

        $provider_title = $provider_page_id > 0 ? get_the_title( $provider_page_id ) : '';
        if ( $provider_page_id > 0 && $provider_title === '' ) {
            /* translators: %d: numeric value */
            $provider_title = sprintf( __( 'Page #%d', 'mybookaro' ), $provider_page_id );
        }
        $customer_title = $customer_page_id > 0 ? get_the_title( $customer_page_id ) : '';
        if ( $customer_page_id > 0 && $customer_title === '' ) {
            /* translators: %d: numeric value */
            $customer_title = sprintf( __( 'Page #%d', 'mybookaro' ), $customer_page_id );
        }
        $provider_edit_link = $provider_page_id > 0 ? get_edit_post_link( $provider_page_id, 'url' ) : '';
        $customer_edit_link = $customer_page_id > 0 ? get_edit_post_link( $customer_page_id, 'url' ) : '';

        ?>
        <div class="bookaro-card bookaro-dashboard-status">
            <div class="bookaro-card__header">
                <h2><?php esc_html_e( 'Dashboard Configuration', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'Review where dashboards live and who can access them.', 'mybookaro' ); ?></p>
            </div>
            <div class="bookaro-status-list">
                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'UI mode', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill bookaro-status-pill--info"><?php echo esc_html( $ui_label ); ?></span>
                        <div class="bookaro-status-item__meta"><?php echo esc_html( $ui_meta ); ?></div>
                        <?php if ( 'shortcodes' === $ui_mode ) : ?>
                            <div class="bookaro-status-item__actions">
                                <a class="button button-small" href="<?php echo esc_url( $shortcodes_url ); ?>">
                                    <?php esc_html_e( 'Open Embed tab', 'mybookaro' ); ?>
                                </a>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>

                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'Frontend UI', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill <?php echo esc_attr( $frontend_status_class ); ?>">
                            <?php echo esc_html( $frontend_status_label ); ?>
                        </span>
                        <div class="bookaro-status-item__meta">
                            <?php
                            echo esc_html(
                                $frontend_enabled
                                    ? __( 'Booking form and dashboards are visible on the site.', 'mybookaro' )
                                    : __( 'Booking UI and dashboards are hidden until enabled.', 'mybookaro' )
                            );
                            ?>
                        </div>
                    </div>
                </div>

                <?php if ( 'shortcodes' === $ui_mode ) : ?>
                    <div class="bookaro-status-item">
                        <div class="bookaro-status-item__label"><?php esc_html_e( 'Provider dashboard page', 'mybookaro' ); ?></div>
                        <div class="bookaro-status-item__value">
                            <span class="bookaro-status-pill <?php echo esc_attr( $provider_page_id > 0 ? 'bookaro-status-pill--ok' : 'bookaro-status-pill--warn' ); ?>">
                                <?php echo esc_html( $provider_page_id > 0 ? __( 'Set', 'mybookaro' ) : __( 'Not set', 'mybookaro' ) ); ?>
                            </span>
                            <div class="bookaro-status-item__meta">
                                <?php
                                echo esc_html(
                                    $provider_page_id > 0
                                        /* translators: %s: dynamic string */
                                        ? sprintf( __( 'Page: %s', 'mybookaro' ), $provider_title )
                                        : __( 'Choose a provider dashboard page in Setup > Pages and placement.', 'mybookaro' )
                                );
                                ?>
                            </div>
                            <div class="bookaro-status-item__actions">
                                <?php if ( $provider_page_id > 0 && $provider_edit_link ) : ?>
                                    <a class="button button-small" href="<?php echo esc_url( $provider_edit_link ); ?>">
                                        <?php esc_html_e( 'Edit page', 'mybookaro' ); ?>
                                    </a>
                                <?php else : ?>
                                    <a class="button button-small" href="<?php echo esc_url( $settings_url ); ?>">
                                        <?php esc_html_e( 'Choose page', 'mybookaro' ); ?>
                                    </a>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>

                    <div class="bookaro-status-item">
                        <div class="bookaro-status-item__label"><?php esc_html_e( 'Customer dashboard page', 'mybookaro' ); ?></div>
                        <div class="bookaro-status-item__value">
                            <span class="bookaro-status-pill <?php echo esc_attr( $customer_page_id > 0 ? 'bookaro-status-pill--ok' : 'bookaro-status-pill--warn' ); ?>">
                                <?php echo esc_html( $customer_page_id > 0 ? __( 'Set', 'mybookaro' ) : __( 'Not set', 'mybookaro' ) ); ?>
                            </span>
                            <div class="bookaro-status-item__meta">
                                <?php
                                echo esc_html(
                                    $customer_page_id > 0
                                        /* translators: %s: dynamic string */
                                        ? sprintf( __( 'Page: %s', 'mybookaro' ), $customer_title )
                                        : __( 'Choose a customer dashboard page in Setup > Pages and placement.', 'mybookaro' )
                                );
                                ?>
                            </div>
                            <div class="bookaro-status-item__actions">
                                <?php if ( $customer_page_id > 0 && $customer_edit_link ) : ?>
                                    <a class="button button-small" href="<?php echo esc_url( $customer_edit_link ); ?>">
                                        <?php esc_html_e( 'Edit page', 'mybookaro' ); ?>
                                    </a>
                                <?php else : ?>
                                    <a class="button button-small" href="<?php echo esc_url( $settings_url ); ?>">
                                        <?php esc_html_e( 'Choose page', 'mybookaro' ); ?>
                                    </a>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>
                <?php endif; ?>

                <div class="bookaro-status-item">
                    <div class="bookaro-status-item__label"><?php esc_html_e( 'Access roles', 'mybookaro' ); ?></div>
                    <div class="bookaro-status-item__value">
                        <span class="bookaro-status-pill <?php echo esc_attr( $roles_status_class ); ?>">
                            <?php echo esc_html( $roles_status_label ); ?>
                        </span>
                        <div class="bookaro-status-item__meta">
                            <?php
                            echo esc_html(
                                sprintf(
                                    /* translators: 1: provider role, 2: customer role */
                                    __( 'Provider: %1$s · Customer: %2$s', 'mybookaro' ),
                                    $provider_role_label,
                                    $owner_role_label
                                )
                            );
                            ?>
                        </div>
                        <div class="bookaro-status-item__actions">
                            <a class="button button-small" href="<?php echo esc_url( $roles_url ); ?>">
                                <?php esc_html_e( 'Open Access tab', 'mybookaro' ); ?>
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <?php
    }

    private function render_support_resources_card(): void
    {
        $support_url = $this->build_support_url();
        $docs_url = $this->build_docs_url( 'general' );
        $status_url = admin_url( 'admin.php?page=bookaro-system-status#bookaro-support-report' );

        $items = [];
        if ( $support_url !== '' ) {
            $items[] = [
                'label' => __( 'Support portal', 'mybookaro' ),
                'message' => __( 'Browse help articles or open a support request.', 'mybookaro' ),
                'url' => $support_url,
                'action' => __( 'Contact support', 'mybookaro' ),
                'external' => true,
            ];
        }

        $items[] = [
            'label' => __( 'System status', 'mybookaro' ),
            'message' => __( 'Download diagnostics and review integration health.', 'mybookaro' ),
            'url' => $status_url,
            'action' => __( 'View status', 'mybookaro' ),
            'external' => false,
        ];

        if ( $docs_url !== '' ) {
            $items[] = [
                'label' => __( 'Docs & guides', 'mybookaro' ),
                'message' => __( 'Step-by-step setup guides for each settings tab.', 'mybookaro' ),
                'url' => $docs_url,
                'action' => __( 'View docs', 'mybookaro' ),
                'external' => true,
            ];
        }

        if ( empty( $items ) ) {
            return;
        }

        $reference_label = '';
        $reference_value = '';
        if ( function_exists( 'home_url' ) ) {
            $reference_label = __( 'Site fingerprint', 'mybookaro' );
            $reference_value = substr( hash( 'sha256', home_url( '/' ) ), 0, 12 );
        }
        ?>
        <div class="bookaro-card bookaro-support">
            <div class="bookaro-card__header">
                <h2><?php esc_html_e( 'Support & resources', 'mybookaro' ); ?></h2>
                <p class="description"><?php esc_html_e( 'Keep everything you need for troubleshooting and support in one place.', 'mybookaro' ); ?></p>
            </div>
            <div class="bookaro-support__list">
                <?php foreach ( $items as $item ) : ?>
                    <div class="bookaro-support__item">
                        <div class="bookaro-support__label"><?php echo esc_html( $item['label'] ?? '' ); ?></div>
                        <p class="description bookaro-support__message"><?php echo esc_html( $item['message'] ?? '' ); ?></p>
                        <?php if ( ! empty( $item['url'] ) ) : ?>
                            <a class="button button-small"
                                href="<?php echo esc_url( $item['url'] ); ?>"
                                <?php echo ! empty( $item['external'] ) ? 'target="_blank" rel="noopener"' : ''; ?>>
                                <?php echo esc_html( $item['action'] ?? __( 'Open', 'mybookaro' ) ); ?>
                            </a>
                        <?php endif; ?>
                    </div>
                <?php endforeach; ?>
            </div>
            <?php if ( $reference_value !== '' ) : ?>
                <div class="bookaro-support__meta">
                    <?php
                    printf(
                        /* translators: 1: reference label, 2: reference value */
                        wp_kses_post( __( '%1$s: <code>%2$s</code>', 'mybookaro' ) ),
                        esc_html( $reference_label ),
                        esc_html( $reference_value )
                    );
                    ?>
                </div>
            <?php endif; ?>
        </div>
        <?php
    }

    private function get_setup_checklist_items(): array
    {
        $opts = $this->options->get_all();
        $ui_mode = $this->resolve_ui_mode_from_options( $opts );

        $frontend_enabled = array_key_exists( 'frontend_enabled', $opts )
            ? ! empty( $opts['frontend_enabled'] )
            : true;
        $listing_types = $opts['allowed_listing_post_types'] ?? [];
        if ( ! is_array( $listing_types ) ) {
            $listing_types = [];
        }
        if ( empty( $listing_types ) && ! empty( $opts['listing_post_type'] ) ) {
            $listing_types = [ $opts['listing_post_type'] ];
        }
        $listing_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $listing_types ) ) ) );
        if ( empty( $listing_types ) && function_exists( 'bookaro_get_allowed_listing_post_types' ) ) {
            $listing_types = bookaro_get_allowed_listing_post_types();
        }
        $has_listing_types = ! empty( $listing_types );
        $shortcodes_ready = ( 'shortcodes' === $ui_mode ) ? $this->has_bookaro_shortcodes_in_posts() : true;

        $rule_keys = [
            'booking_min_notice_hours',
            'booking_max_window_days',
            'booking_buffer_before_minutes',
            'booking_buffer_after_minutes',
            'booking_cancel_window_hours',
            'booking_reschedule_window_hours',
        ];
        $rules_configured = false;
        foreach ( $rule_keys as $key ) {
            if ( absint( $opts[ $key ] ?? 0 ) > 0 ) {
                $rules_configured = true;
                break;
            }
        }
        $lifecycle_policy_hint = $this->get_lifecycle_policy_hint_text( $opts );

        $notifications_enabled = ! empty( $opts['notify_new_booking'] ?? 0 )
            || ! empty( $opts['notify_reminder'] ?? 0 )
            || ! empty( $opts['notify_review_invite'] ?? 0 );

        $roles_configured = ! empty( $opts['provider_role'] ?? '' ) || ! empty( $opts['owner_role'] ?? '' );

        $shortcodes_tab = ( 'shortcodes' === $ui_mode ) ? 'shortcodes' : 'general';
        $site_type = $this->resolve_site_type_from_options( $opts );
        $is_single_business = ( 'single_business' === $site_type );
        $default_provider_id = absint( $opts['default_provider_id'] ?? 0 );
        $default_placement_strategy = $is_single_business ? 'normal_page' : 'directory_template';
        $placement_strategy = $this->normalize_booking_placement_strategy_setting(
            $opts['booking_placement_strategy']
            ?? ( function_exists( 'bookaro_get_booking_placement_strategy' ) ? bookaro_get_booking_placement_strategy() : '' ),
            $default_placement_strategy
        );
        $auto_insert_selector = sanitize_text_field( (string) ( $opts['auto_insert_booking_button_selector'] ?? '' ) );

        $integration_mode = sanitize_key( (string) ( $opts['integration_mode'] ?? 'auto' ) );
        if ( ! in_array( $integration_mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $integration_mode = 'auto';
        }
        $resolved_mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : $integration_mode;
        if ( ! in_array( $resolved_mode, [ 'mylisting', 'generic' ], true ) ) {
            $resolved_mode = $integration_mode;
        }

        if ( 'generic' === $resolved_mode ) {
            $wizard_url = admin_url( 'admin.php?page=bookaro-setup-wizard' );
            $status_url = admin_url( 'admin.php?page=bookaro-system-status#bookaro-support-check-generic_validation_provider_resolution_sample' );
            $bookings_url = admin_url( 'admin.php?page=bookaro-bookings' );

            $generic_setup_preset = sanitize_key( (string) ( $opts['generic_setup_preset'] ?? '' ) );
            $generic_preset_labels = [
                'author_provider' => __( 'Provider-Owned Directory (site shape: providers author listings)', 'mybookaro' ),
                'meta_key_provider' => __( 'Editorial Directory + Provider Meta Key (site shape: editors author listings)', 'mybookaro' ),
                'author_admin_catalog' => __( 'Shared Catalog Directory (site shape: one centralized service menu)', 'mybookaro' ),
            ];
            $generic_preset_outcomes = [
                'author_provider' => __( 'Outcome: each provider receives bookings for their own listings and manages their own services.', 'mybookaro' ),
                'meta_key_provider' => __( 'Outcome: listings route bookings to providers from listing meta while providers manage their own services.', 'mybookaro' ),
                'author_admin_catalog' => __( 'Outcome: bookings still resolve to listing owners while admins control one shared service catalog.', 'mybookaro' ),
            ];
            $generic_preset_selected = array_key_exists( $generic_setup_preset, $generic_preset_labels );
            $generic_preset_description = $generic_preset_selected
                ? sprintf(
                    /* translators: 1: Generic guided path label, 2: expected path outcome */
                    __( 'Selected guided path: %1$s. %2$s Confirm this matches your site shape before launch.', 'mybookaro' ),
                    (string) $generic_preset_labels[ $generic_setup_preset ],
                    (string) ( $generic_preset_outcomes[ $generic_setup_preset ] ?? '' )
                )
                : __( 'No guided path selected yet. Start with Step 1 (choose a guided path) unless your data model requires another path.', 'mybookaro' );

            $primary_type = $listing_types[0] ?? '';
            if ( '' === $primary_type ) {
                $primary_type = sanitize_key( (string) ( $opts['listing_post_type'] ?? '' ) );
            }
            if ( '' === $primary_type ) {
                $primary_type = 'job_listing';
            }
            $booking_page_id = function_exists( 'bookaro_get_page_for_context' )
                ? absint( bookaro_get_page_for_context( 'generic', $primary_type, 'booking' ) )
                : absint( $opts['booking_page_id'] ?? 0 );
            $provider_page_id = function_exists( 'bookaro_get_page_for_context' )
                ? absint( bookaro_get_page_for_context( 'generic', $primary_type, 'provider_dashboard' ) )
                : absint( $opts['provider_dashboard_page_id'] ?? 0 );
            $customer_page_id = function_exists( 'bookaro_get_page_for_context' )
                ? absint( bookaro_get_page_for_context( 'generic', $primary_type, 'customer_dashboard' ) )
                : absint( $opts['customer_dashboard_page_id'] ?? 0 );

            $missing_pages = [];
            if ( $booking_page_id < 1 ) {
                $missing_pages[] = __( 'Booking page', 'mybookaro' );
            }
            if ( 'shortcodes' === $ui_mode ) {
                if ( $provider_page_id < 1 ) {
                    $missing_pages[] = __( 'Provider dashboard page', 'mybookaro' );
                }
                if ( $customer_page_id < 1 ) {
                    $missing_pages[] = __( 'Customer dashboard page', 'mybookaro' );
                }
            }
            $pages_assigned = empty( $missing_pages );
            $pages_description = $pages_assigned
                ? (
                    'shortcodes' === $ui_mode
                        ? __( 'Booking, provider, and customer dashboard pages are assigned.', 'mybookaro' )
                        : __( 'Booking page is assigned. WooCommerce My Account dashboards are active; switch to Shortcodes mode for the recommended Generic block/shortcode path.', 'mybookaro' )
                )
                : sprintf(
                    /* translators: %s: missing page names */
                    __( 'Missing: %s.', 'mybookaro' ),
                    implode( ', ', $missing_pages )
                );

            $shortcodes_ready_generic = ( 'shortcodes' === $ui_mode ) ? $this->has_bookaro_shortcodes_in_posts() : false;
            $auto_insert_mode = sanitize_key( (string) ( $opts['auto_insert_booking_button'] ?? '' ) );
            $auto_insert_enabled = array_key_exists( 'auto_insert_booking_button_enabled', $opts )
                ? ! empty( $opts['auto_insert_booking_button_enabled'] )
                : ( '' !== $auto_insert_mode );
            if ( 'shortcode_only' === $placement_strategy ) {
                $auto_insert_enabled = false;
                $auto_insert_mode = '';
            } elseif ( 'css_selector' === $placement_strategy ) {
                $auto_insert_enabled = true;
                $auto_insert_mode = 'selector';
            }
            $selector_required = ( 'selector' === $auto_insert_mode );
            $selector_valid = ! $selector_required || '' !== trim( $auto_insert_selector );
            $auto_insert_ready = $auto_insert_enabled && '' !== $auto_insert_mode && $selector_valid;

            if ( 'shortcode_only' === $placement_strategy ) {
                $entry_placement_ready = $shortcodes_ready_generic;
                $entry_placement_description = $shortcodes_ready_generic
                    ? __( 'Shortcode/block placement detected. Strategy is shortcode-only, so auto-insert is intentionally disabled.', 'mybookaro' )
                    : __( 'Placement strategy is shortcode/block only. Add Booking Button and Services Cards blocks/shortcodes to your pages/templates.', 'mybookaro' );
            } elseif ( 'css_selector' === $placement_strategy ) {
                $entry_placement_ready = $shortcodes_ready_generic || $auto_insert_ready;
                if ( ! $selector_valid ) {
                    $entry_placement_description = __( 'Placement strategy is CSS selector insertion, but the selector is empty. Add a valid selector or switch strategy.', 'mybookaro' );
                } elseif ( $auto_insert_ready ) {
                    $entry_placement_description = __( 'CSS selector insertion is configured. Keep blocks/shortcodes as explicit fallback where needed.', 'mybookaro' );
                } else {
                    $entry_placement_description = __( 'Set a valid CSS selector or place blocks/shortcodes manually to expose booking entry points.', 'mybookaro' );
                }
            } elseif ( 'normal_page' === $placement_strategy ) {
                $normal_page_context_ready = ! $is_single_business || $default_provider_id > 0;
                $entry_placement_ready = $shortcodes_ready_generic || ( $auto_insert_ready && $normal_page_context_ready );
                if ( $entry_placement_ready ) {
                    $entry_placement_description = __( 'Normal page placement is ready. Booking UI can run without listing template restrictions.', 'mybookaro' );
                } elseif ( $is_single_business && ! $normal_page_context_ready ) {
                    $entry_placement_description = __( 'Normal page placement needs a Default Provider in Single Business mode.', 'mybookaro' );
                } else {
                    $entry_placement_description = __( 'Set up normal-page placement with blocks/shortcodes or enable valid auto-insert.', 'mybookaro' );
                }
            } else {
                $entry_placement_ready = $shortcodes_ready_generic || $auto_insert_ready;
                $entry_placement_description = $entry_placement_ready
                    ? __( 'Directory listing template placement is configured.', 'mybookaro' )
                    : __( 'Configure listing-template placement with blocks/shortcodes or valid auto-insert.', 'mybookaro' );
            }

            $mapping_label = $is_single_business
                ? __( 'Confirm default provider', 'mybookaro' )
                : __( 'Confirm listing post types', 'mybookaro' );
            $mapping_description = $is_single_business
                ? __( 'Single Business placement uses the Default Provider for provider-based booking UI on normal pages.', 'mybookaro' )
                : __( 'MyBookaro only runs on the selected listing post types.', 'mybookaro' );
            $mapping_completed = $is_single_business
                ? ( $default_provider_id > 0 )
                : $has_listing_types;

            $generic_validation_prompt = $this->get_generic_validation_prompt( $opts );
            $validation_ready = ! empty( $generic_validation_prompt['active'] ) && empty( $generic_validation_prompt['needs_validation'] );
            $validation_description = $validation_ready
                ? __( 'System Status review is up to date for current Generic mapping/pages.', 'mybookaro' )
                : __( 'Run System Status validation and clear Warn/Fail checks before launch.', 'mybookaro' );

            $completed_bookings = $this->get_recent_completed_booking_count();
            $test_booking_ready = $completed_bookings > 0;
            $test_booking_description = $test_booking_ready
                ? sprintf(
                    /* translators: %d: completed booking count */
                    __( 'Completed bookings in recent activity: %d.', 'mybookaro' ),
                    $completed_bookings
                )
                : __( 'No completed booking found yet. Run one full booking request/approval flow before launch.', 'mybookaro' );

            return [
                [
                    'label' => __( 'Step 1: Choose guided path', 'mybookaro' ),
                    'description' => $generic_preset_description,
                    'completed' => $generic_preset_selected,
                    'url' => $wizard_url . '&step=modes',
                ],
                [
                    'label' => __( 'Step 2: Assign booking/dashboard pages', 'mybookaro' ),
                    'description' => $pages_description,
                    'completed' => $pages_assigned,
                    'url' => $wizard_url . '&step=pages',
                ],
                [
                    'label' => __( 'Step 3: Confirm booking placement strategy', 'mybookaro' ),
                    'description' => $entry_placement_description,
                    'completed' => $entry_placement_ready,
                    'url' => $this->get_settings_tab_url( 'shortcodes' ),
                ],
                [
                    'label' => __( 'Step 4: Run System Status validation', 'mybookaro' ),
                    'description' => $validation_description,
                    'completed' => $validation_ready,
                    'url' => $status_url,
                ],
                [
                    'label' => __( 'Step 5: Test one booking', 'mybookaro' ),
                    'description' => $test_booking_description,
                    'completed' => $test_booking_ready,
                    'url' => $bookings_url,
                ],
                [
                    'label'       => $mapping_label,
                    'description' => $mapping_description,
                    'completed'   => $mapping_completed,
                    'url'         => $this->get_settings_tab_url( 'general' ),
                ],
                [
                    'label'       => __( 'Enable email notifications', 'mybookaro' ),
                    'description' => __( 'Turn on new booking, reminders, or review invites.', 'mybookaro' ),
                    'completed'   => $notifications_enabled,
                    'url'         => $this->get_settings_tab_url( 'notifications' ),
                ],
                [
                    'label'       => __( 'Assign provider/customer roles', 'mybookaro' ),
                    'description' => __( 'Choose which WordPress roles can access MyBookaro dashboards.', 'mybookaro' ),
                    'completed'   => $roles_configured,
                    'url'         => $this->get_settings_tab_url( 'roles' ),
                ],
            ];
        }

        return [
            [
                'label'       => __( 'Enable frontend booking UI', 'mybookaro' ),
                'description' => __( 'Show the booking form and dashboards on the site.', 'mybookaro' ),
                'completed'   => $frontend_enabled,
                'url'         => $this->get_settings_tab_url( 'general' ),
            ],
            [
                'label'       => __( 'Confirm listing post types', 'mybookaro' ),
                'description' => __( 'MyBookaro only runs on the selected listing post types.', 'mybookaro' ),
                'completed'   => $has_listing_types,
                'url'         => $this->get_settings_tab_url( 'general' ),
            ],
            [
                'label'       => __( 'Place dashboards (UI mode)', 'mybookaro' ),
                'description' => ( 'shortcodes' === $ui_mode )
                    ? __( 'Add provider/customer shortcodes to pages when using Shortcodes mode.', 'mybookaro' )
                    : __( 'Using WooCommerce My Account pages for dashboards.', 'mybookaro' ),
                'completed'   => $shortcodes_ready,
                'url'         => $this->get_settings_tab_url( $shortcodes_tab ),
            ],
            [
                'label'       => __( 'Review booking rules', 'mybookaro' ),
                'description' => $lifecycle_policy_hint !== ''
                    ? $lifecycle_policy_hint
                    : __( 'Set notice windows and buffers to fit your booking policies.', 'mybookaro' ),
                'completed'   => $rules_configured,
                'url'         => $this->get_settings_tab_url( 'general' ),
            ],
            [
                'label'       => __( 'Enable email notifications', 'mybookaro' ),
                'description' => __( 'Turn on new booking, reminders, or review invites.', 'mybookaro' ),
                'completed'   => $notifications_enabled,
                'url'         => $this->get_settings_tab_url( 'notifications' ),
            ],
            [
                'label'       => __( 'Assign provider/customer roles', 'mybookaro' ),
                'description' => __( 'Choose which WordPress roles can access MyBookaro dashboards.', 'mybookaro' ),
                'completed'   => $roles_configured,
                'url'         => $this->get_settings_tab_url( 'roles' ),
            ],
        ];
    }

    private function get_lifecycle_policy_hint_text( array $opts ): string
    {
        $payment_policy_text = function_exists( 'bookaro_pro_get_payment_policy_behavior_text' )
            ? (string) bookaro_pro_get_payment_policy_behavior_text()
            : '';
        $deposit_text = function_exists( 'bookaro_pro_get_deposit_behavior_text' )
            ? (string) bookaro_pro_get_deposit_behavior_text()
            : '';
        $cancel_fee_text = function_exists( 'bookaro_pro_get_cancel_fee_behavior_text' )
            ? (string) bookaro_pro_get_cancel_fee_behavior_text()
            : '';
        $no_show_fee_text = function_exists( 'bookaro_pro_get_no_show_fee_behavior_text' )
            ? (string) bookaro_pro_get_no_show_fee_behavior_text()
            : '';

        $bundle = BookingPolicyPresenter::build_policy_bundle(
            [
                'cancel_window_hours' => absint( $opts['booking_cancel_window_hours'] ?? 0 ),
                'reschedule_window_hours' => absint( $opts['booking_reschedule_window_hours'] ?? 0 ),
                'cancel_template' => sanitize_text_field( (string) ( $opts['booking_cancel_policy_text'] ?? '' ) ),
                'reschedule_template' => sanitize_text_field( (string) ( $opts['booking_reschedule_policy_text'] ?? '' ) ),
                'payment_policy_text' => $payment_policy_text,
                'deposit_text' => $deposit_text,
                'cancel_fee_text' => $cancel_fee_text,
                'no_show_fee_text' => $no_show_fee_text,
            ]
        );

        return (string) ( $bundle['combined'] ?? '' );
    }

    private function get_recent_completed_booking_count( int $days = 60 ): int
    {
        $days = max( 1, absint( $days ) );
        $wpdb = $GLOBALS['wpdb'] ?? null;
        if ( ! ( $wpdb instanceof \wpdb ) ) {
            return 0;
        }

        $db = new BookingRepository();
        $table = esc_sql( $db->get_table_name() );
        $exists = $wpdb->get_var(
            $wpdb->prepare( 'SHOW TABLES LIKE %s', $table )
        );
        if ( empty( $exists ) ) {
            return 0;
        }

        // Bug 32: same double-offset pattern as the upcoming-bookings stat.
        // Use time() so date_i18n receives a true UTC timestamp.
        $now_ts = time();
        $start_date = date_i18n( 'Y-m-d', $now_ts - ( $days * DAY_IN_SECONDS ) );
        $end_date = date_i18n( 'Y-m-d', $now_ts );
        $counts = $db->get_booking_status_counts( 0, $start_date, $end_date );
        if ( ! is_array( $counts ) ) {
            return 0;
        }

        return max( 0, (int) ( $counts['completed'] ?? 0 ) );
    }

    private function get_settings_tab_url( string $tab ): string
    {
        $tab = sanitize_key( $tab );
        return admin_url( 'admin.php?page=bookaro-booking-settings&tab=' . $tab ) . '#bookaro-settings-tabs';
    }

    private function get_generic_preset_profile_summary( array $opts ): array
    {
        $preset = sanitize_key( (string) ( $opts['generic_setup_preset'] ?? '' ) );
        if ( ! in_array( $preset, [ 'author_provider', 'meta_key_provider', 'author_admin_catalog' ], true ) ) {
            $provider_mode = sanitize_key( (string) ( $opts['provider_resolution_mode'] ?? '' ) );
            $services_model = sanitize_key( (string) ( $opts['services_model'] ?? $opts['generic_services_mode'] ?? '' ) );
            if ( 'admin_catalog' === $services_model ) {
                $services_model = 'admin_only';
            }
            if ( 'meta_key' === $provider_mode ) {
                $preset = 'meta_key_provider';
            } elseif ( 'admin_only' === $services_model ) {
                $preset = 'author_admin_catalog';
            } else {
                $preset = 'author_provider';
            }
        }

        $provider_meta_key = sanitize_key( (string) ( $opts['provider_meta_key'] ?? $opts['listing_provider_meta_key'] ?? 'provider_user_id' ) );
        if ( '' === $provider_meta_key ) {
            $provider_meta_key = 'provider_user_id';
        }

        $profiles = [
            'author_provider' => [
                'label' => __( 'Provider-Owned Directory (site shape: providers author listings)', 'mybookaro' ),
                'site_shape' => __( 'Site shape: listing posts are authored by provider accounts.', 'mybookaro' ),
                'mapping_example' => __( 'Mapping example: listing #123 author user #44 resolves provider user #44.', 'mybookaro' ),
                'services_example' => __( 'Services example: each provider manages their own services.', 'mybookaro' ),
            ],
            'meta_key_provider' => [
                'label' => __( 'Editorial Directory + Provider Meta Key (site shape: editors author listings)', 'mybookaro' ),
                'site_shape' => __( 'Site shape: listings can be authored by editors/admins while provider IDs live in listing meta.', 'mybookaro' ),
                'mapping_example' => sprintf(
                    /* translators: %s: provider meta key */
                    __( 'Mapping example: listing #123 has %s=44, so provider resolves to user #44.', 'mybookaro' ),
                    $provider_meta_key
                ),
                'services_example' => __( 'Services example: provider-owned services still work after provider resolution from listing meta.', 'mybookaro' ),
            ],
            'author_admin_catalog' => [
                'label' => __( 'Shared Catalog Directory (site shape: one centralized service menu)', 'mybookaro' ),
                'site_shape' => __( 'Site shape: providers receive bookings, but admins maintain one shared service catalog.', 'mybookaro' ),
                'mapping_example' => __( 'Mapping example: listing #123 author user #44 resolves provider user #44.', 'mybookaro' ),
                'services_example' => __( 'Services example: customers choose from admin-managed services; providers do not create custom services.', 'mybookaro' ),
            ],
        ];

        return $profiles[ $preset ] ?? $profiles['author_provider'];
    }

    private function get_integration_health_items(): array
    {
        $items = [];
        $opts = $this->options->get_all();

        $settings_url = $this->get_settings_tab_url( 'general' );
        $roles_url = $this->get_settings_tab_url( 'roles' );
        $shortcodes_url = $this->get_settings_tab_url( 'shortcodes' );
        $payments_url = $this->get_settings_tab_url( 'payments' );

        $frontend_enabled = array_key_exists( 'frontend_enabled', $opts )
            ? ! empty( $opts['frontend_enabled'] )
            : true;
        $items[] = [
            'label' => __( 'Frontend UI', 'mybookaro' ),
            'status' => $frontend_enabled ? 'ok' : 'warning',
            'message' => $frontend_enabled
                ? __( 'Booking form and dashboards are visible.', 'mybookaro' )
                : __( 'Frontend UI is disabled; dashboards are hidden.', 'mybookaro' ),
            'url' => $settings_url,
        ];

        $listing_types = $opts['allowed_listing_post_types'] ?? [];
        if ( ! is_array( $listing_types ) ) {
            $listing_types = [ $listing_types ];
        }
        if ( empty( $listing_types ) && ! empty( $opts['listing_post_type'] ) ) {
            $listing_types = [ $opts['listing_post_type'] ];
        }
        $listing_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $listing_types ) ) ) );
        if ( empty( $listing_types ) && function_exists( 'bookaro_get_allowed_listing_post_types' ) ) {
            $listing_types = bookaro_get_allowed_listing_post_types();
        }
        $items[] = [
            'label' => __( 'Listing post types', 'mybookaro' ),
            'status' => ! empty( $listing_types ) ? 'ok' : 'warning',
            'message' => ! empty( $listing_types )
                ? sprintf(
                    /* translators: %d: count of post types */
                    __( '%d selected', 'mybookaro' ),
                    count( $listing_types )
                )
                : __( 'Select at least one listing post type.', 'mybookaro' ),
            'url' => $settings_url,
        ];

        $ui_mode = $this->resolve_ui_mode_from_options( $opts );
        if ( 'shortcodes' === $ui_mode ) {
            $provider_page_id = absint( $opts['provider_dashboard_page_id'] ?? 0 );
            $customer_page_id = absint( $opts['customer_dashboard_page_id'] ?? 0 );
            $missing = [];
            if ( $provider_page_id <= 0 ) {
                $missing[] = __( 'provider', 'mybookaro' );
            }
            if ( $customer_page_id <= 0 ) {
                $missing[] = __( 'customer', 'mybookaro' );
            }
            $items[] = [
                'label' => __( 'Dashboard pages', 'mybookaro' ),
                'status' => empty( $missing ) ? 'ok' : 'warning',
                'message' => empty( $missing )
                    ? __( 'Provider and customer pages are set.', 'mybookaro' )
                    : sprintf(
                        /* translators: %s: list of missing pages */
                        __( 'Missing %s page(s).', 'mybookaro' ),
                        implode( ', ', $missing )
                    ),
                'url' => $settings_url,
            ];
        } else {
            $items[] = [
                'label' => __( 'Dashboard pages', 'mybookaro' ),
                'status' => 'info',
                'message' => __( 'Dashboards live in WooCommerce My Account.', 'mybookaro' ),
                'url' => $settings_url,
            ];
        }

        $integration_mode = sanitize_key( (string) ( $opts['integration_mode'] ?? 'auto' ) );
        if ( ! in_array( $integration_mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $integration_mode = 'auto';
        }
        $resolved_mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : $integration_mode;
        $mylisting_detected = defined( 'MYLISTING_VERSION' )
            || defined( 'MYLISTING_DIR' )
            || class_exists( '\\MyListing\\Plugin' )
            || function_exists( 'mylisting' );
        $hook_ok = function_exists( 'has_action' )
            ? (bool) has_action( 'mylisting/single/sidebar/actions', 'bookaro_render_mylisting_booking_button' )
            : false;
        $mylisting_ready = $mylisting_detected && $hook_ok;
        if ( 'mylisting' === $resolved_mode ) {
            $items[] = [
                'label' => __( 'MyListing hook', 'mybookaro' ),
                'status' => $mylisting_ready ? 'ok' : 'warning',
                'message' => $mylisting_ready
                    ? __( 'Booking button hook registered.', 'mybookaro' )
                    : __( 'MyListing not detected or hook missing.', 'mybookaro' ),
                'url' => $settings_url,
            ];
        } else {
            $items[] = [
                'label' => __( 'MyListing hook', 'mybookaro' ),
                'status' => $mylisting_detected ? 'info' : 'info',
                'message' => $mylisting_detected
                    ? __( 'MyListing detected; auto mode will use it.', 'mybookaro' )
                    : __( 'MyListing not detected in auto mode.', 'mybookaro' ),
                'url' => $settings_url,
            ];
        }
        if ( 'generic' === $resolved_mode ) {
            $preset_profile = $this->get_generic_preset_profile_summary( $opts );
            $items[] = [
                'label' => __( 'Generic guided path mapping', 'mybookaro' ),
                'status' => 'info',
                'message' => sprintf(
                    /* translators: 1: guided path label, 2: site shape summary, 3: provider mapping example, 4: services mapping example */
                    __( 'Guided path: %1$s. %2$s %3$s %4$s', 'mybookaro' ),
                    (string) ( $preset_profile['label'] ?? '' ),
                    (string) ( $preset_profile['site_shape'] ?? '' ),
                    (string) ( $preset_profile['mapping_example'] ?? '' ),
                    (string) ( $preset_profile['services_example'] ?? '' )
                ),
                'url' => $settings_url,
            ];
        }

        $payments_enabled = function_exists( 'bookaro_get_option' )
            ? (bool) bookaro_get_option( 'payments_enabled', 0 )
            : ! empty( $opts['payments_enabled'] ?? 0 );
        $woocommerce_present = class_exists( 'WooCommerce' ) || defined( 'WC_VERSION' ) || function_exists( 'WC' );
        if ( $payments_enabled ) {
            $items[] = [
                'label' => __( 'WooCommerce', 'mybookaro' ),
                'status' => $woocommerce_present ? 'ok' : 'warning',
                'message' => $woocommerce_present
                    ? __( 'WooCommerce detected for payments.', 'mybookaro' )
                    : __( 'Payments enabled but WooCommerce is missing.', 'mybookaro' ),
                'url' => $payments_url,
            ];
        } else {
            $items[] = [
                'label' => __( 'WooCommerce', 'mybookaro' ),
                'status' => 'info',
                'message' => __( 'Payments disabled; WooCommerce not required.', 'mybookaro' ),
                'url' => $payments_url,
            ];
        }

        // Prefixed override filter for the REST probe; defaults to whether the
        // REST API is present.
        $rest_enabled = (bool) apply_filters( 'bookaro_rest_enabled', function_exists( 'rest_url' ) );
        $rest_ok = $rest_enabled && function_exists( 'rest_url' ) && rest_url() !== '';
        $items[] = [
            'label' => __( 'REST API', 'mybookaro' ),
            'status' => $rest_ok ? 'ok' : 'warning',
            'message' => $rest_ok
                ? __( 'REST API available for booking actions.', 'mybookaro' )
                : __( 'REST API appears disabled; bookings may not load.', 'mybookaro' ),
            'url' => admin_url( 'site-health.php' ),
        ];

        $auto_complete_enabled = array_key_exists( 'auto_complete_enabled', $opts )
            ? ! empty( $opts['auto_complete_enabled'] )
            : true;
        $next_auto_complete = function_exists( 'wp_next_scheduled' ) ? wp_next_scheduled( 'bookaro_auto_complete' ) : false;
        if ( ! $auto_complete_enabled ) {
            $items[] = [
                'label' => __( 'Auto-complete', 'mybookaro' ),
                'status' => 'info',
                'message' => __( 'Auto-complete is disabled.', 'mybookaro' ),
                'url' => $settings_url,
            ];
        } else {
            $items[] = [
                'label' => __( 'Auto-complete', 'mybookaro' ),
                'status' => $next_auto_complete ? 'ok' : 'warning',
                'message' => $next_auto_complete
                    ? __( 'Scheduled task is active.', 'mybookaro' )
                    : __( 'Scheduled task missing; run plugin upgrade or re-save settings.', 'mybookaro' ),
                'url' => admin_url( 'admin.php?page=bookaro-system-status' ),
            ];
        }

        $provider_role = sanitize_key( (string) ( $opts['provider_role'] ?? '' ) );
        $customer_role = sanitize_key( (string) ( $opts['owner_role'] ?? '' ) );
        $provider_role_key = $provider_role !== '' ? $provider_role : 'bookaro_provider';
        $customer_role_key = $customer_role !== '' ? $customer_role : 'bookaro_customer';
        $role_ready = true;
        if ( function_exists( 'wp_roles' ) && wp_roles() ) {
            $roles = wp_roles();
            $provider_role_obj = $roles ? $roles->get_role( $provider_role_key ) : null;
            $customer_role_obj = $roles ? $roles->get_role( $customer_role_key ) : null;
            $cap_provider = defined( 'BOOKARO_CAP_PROVIDER' ) ? BOOKARO_CAP_PROVIDER : 'bookaro_provider';
            $cap_customer = defined( 'BOOKARO_CAP_CUSTOMER' ) ? BOOKARO_CAP_CUSTOMER : 'bookaro_customer';
            if ( ! $provider_role_obj || ! $provider_role_obj->has_cap( $cap_provider ) ) {
                $role_ready = false;
            }
            if ( ! $customer_role_obj || ! $customer_role_obj->has_cap( $cap_customer ) ) {
                $role_ready = false;
            }
        }
        $items[] = [
            'label' => __( 'Dashboard roles', 'mybookaro' ),
            'status' => $role_ready ? 'ok' : 'warning',
            'message' => $role_ready
                ? __( 'Roles are configured for dashboard access.', 'mybookaro' )
                : __( 'Roles missing required capabilities.', 'mybookaro' ),
            'url' => $roles_url,
        ];

        return $items;
    }

    private function has_bookaro_shortcodes_in_posts(): bool
    {
        global $wpdb;
        if ( ! $wpdb ) {
            return false;
        }

        $shortcodes = [
            'bookaro_provider_dashboard',
            'bookaro_customer_dashboard',
            'bookaro_booking_button',
            'bookaro_booking_form',
        ];
        $likes = [];
        $params = [];
        foreach ( $shortcodes as $shortcode ) {
            $likes[] = 'post_content LIKE %s';
            $params[] = '%' . $wpdb->esc_like( $shortcode ) . '%';
        }

        // $sql template + $params are passed straight to $wpdb->prepare() with
        // explicit %s placeholders for every dynamic value (the LIKE patterns
        // were assembled from $shortcodes which is a hard-coded list at the top
        // of this method). The phpcs:ignore comments below are because the
        // static analyzer can't trace through the dynamic implode() into
        // prepare() — the runtime call IS parameterized.
        $sql = "SELECT ID FROM {$wpdb->posts} WHERE post_status IN ('publish','private','draft') AND post_type NOT IN ('revision','nav_menu_item') AND (" . implode( ' OR ', $likes ) . ') LIMIT 1';
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $sql is a hard-coded template with %s placeholders + $wpdb->posts; prepare() handles the values.
        $prepared = $wpdb->prepare( $sql, $params );
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $prepared is the result of $wpdb->prepare() above.
        $found = $wpdb->get_var( $prepared );
        return ! empty( $found );
    }

    /**
     * Render a color-picker field.
     *
     * @param array $args ['id'=>string, 'default'=>string]
     */
    public function render_color_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, 'bookaro-color-field ' . $this->input_base_class( 'w-40' ) );
        $palette_attr = '';
        if ( ! empty( $args['palette'] ) && is_array( $args['palette'] ) ) {
            $palette = array_values(
                array_filter(
                    array_map(
                        static function ( $color ): string {
                            $sanitized = sanitize_hex_color( (string) $color );
                            return $sanitized ?: '';
                        },
                        $args['palette']
                    )
                )
            );
            if ( ! empty( $palette ) ) {
                $palette_attr = ' data-bookaro-palette="' . esc_attr( wp_json_encode( $palette ) ) . '"';
            }
        }
        $this->render_field_wrapper_open( $args );
        // $palette_attr is either an empty string or a pre-escaped data-bookaro-palette attribute
        // built via esc_attr( wp_json_encode( $palette ) ) above. Plugin Check can't trace this.
        printf(
            '<input name="%1$s[%2$s]" class="%4$s" value="%3$s" data-bookaro-setting="%2$s"%5$s%6$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_attr($val),
            esc_attr( $class ),
            disabled( $disabled, true, false ),
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $palette_attr is empty or a pre-escaped ` data-bookaro-palette="<json>"` fragment built with esc_attr( wp_json_encode( ... ) ) above.
            $palette_attr
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Provide a shared curated color palette for all settings color pickers.
     *
     * @return string[]
     */
    private function get_default_color_palette(): array
    {
        $opts = $this->options->get_all();
        $primary = sanitize_hex_color( (string) ( $opts['appearance_primary_color'] ?? '#007cba' ) );
        $secondary = sanitize_hex_color( (string) ( $opts['appearance_secondary_color'] ?? '#6c757d' ) );
        $palette = [
            $primary ?: '#007cba',
            $secondary ?: '#6c757d',
            '#4a90e2',
            '#1f77b4',
            '#28a745',
            '#16a34a',
            '#7ed321',
            '#a35dd6',
            '#d62728',
            '#f59e0b',
            '#0f172a',
            '#ffffff',
        ];

        $palette = array_values(
            array_unique(
                array_filter(
                    array_map(
                        static function ( $color ): string {
                            $sanitized = sanitize_hex_color( (string) $color );
                            return $sanitized ?: '';
                        },
                        $palette
                    )
                )
            )
        );

        return ! empty( $palette ) ? $palette : [ '#007cba', '#6c757d', '#4a90e2', '#28a745', '#ffffff' ];
    }

    /**
     * Render a text input field.
     *
     * @param array $args ['id'=>string, 'default'=>string]
     */
    public function render_text_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="text" name="%1$s[%2$s]" class="%4$s" value="%3$s" data-bookaro-setting="%2$s"%5$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_attr($val),
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render the email test action field.
     */
    public function render_test_email_field(array $args): void
    {
        $opts = $this->options->get_all();
        $default_email = sanitize_email( $opts['log_email'] ?? '' );
        if ( $default_email === '' ) {
            $default_email = sanitize_email( get_option( 'admin_email' ) );
        }

        $this->render_field_wrapper_open( $args );
        $class = $this->input_base_class();
        $action_url = admin_url( 'admin-post.php' );
        $nonce = wp_create_nonce( 'bookaro_email_test' );
        $wrapper_attrs = sprintf(
            'class="bookaro-email-test" data-url="%1$s" data-action="%2$s" data-nonce="%3$s" data-msg-empty="%4$s" data-msg-sending="%5$s" data-msg-success="%6$s" data-msg-failed="%7$s"',
            esc_url( $action_url ),
            esc_attr( 'bookaro_send_test_email' ),
            esc_attr( $nonce ),
            esc_attr__( 'Please enter a test email address.', 'mybookaro' ),
            esc_attr__( 'Sending test email...', 'mybookaro' ),
            esc_attr__( 'Test email sent.', 'mybookaro' ),
            esc_attr__( 'Test email failed. Check your site mail configuration.', 'mybookaro' )
        );
        printf(
            '<div %5$s><input type="email" id="bookaro_test_email" name="bookaro_test_email" class="%1$s" value="%2$s" placeholder="%3$s"> <button type="button" class="button button-secondary bookaro-email-test-button">%4$s</button></div>',
            esc_attr( $class ),
            esc_attr( $default_email ),
            esc_attr__( 'name@example.com', 'mybookaro' ),
            esc_html__( 'Send test email', 'mybookaro' ),
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $wrapper_attrs is an HTML attribute string assembled in the sprintf() above from esc_url() / esc_attr() / esc_attr__() pieces.
            $wrapper_attrs
        );

        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }

        echo '<p class="description bookaro-email-test-result" role="status" aria-live="polite"></p>';
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render a password input field.
     *
     * @param array $args ['id'=>string, 'default'=>string]
     */
    public function render_password_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="password" name="%1$s[%2$s]" class="%4$s" value="%3$s" data-bookaro-setting="%2$s"%5$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_attr($val),
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render a textarea field.
     */
    public function render_textarea_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class( 'min-h-[110px]' ) );
        $this->render_field_wrapper_open( $args );
        printf(
            '<textarea name="%1$s[%2$s]" rows="4" class="%4$s" data-bookaro-setting="%2$s"%5$s>%3$s</textarea>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_textarea($val),
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        $this->render_pro_badge( $args );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_field_wrapper_close();
    }

    /**
     * Render a numeric input field.
     *
     * @param array $args ['id'=>string, 'default'=>string, 'min'=>int|float, 'max'=>int|float, 'step'=>string]
     */
    public function render_number_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class( 'w-40' ) );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="number" name="%1$s[%2$s]" value="%3$s"%4$s%5$s%6$s class="%7$s" data-bookaro-setting="%2$s"%8$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_attr($val),
            isset($args['min']) ? ' min="' . esc_attr($args['min']) . '"' : '',
            isset($args['max']) ? ' max="' . esc_attr($args['max']) . '"' : '',
            isset($args['step']) ? ' step="' . esc_attr($args['step']) . '"' : '',
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    /**
     * Render an email input field.
     */
    public function render_email_field(array $args): void
    {
        $opts = $this->options->get_all();
        $id   = $args['id'];
        $val  = $opts[$id] ?? $args['default'];
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args, $this->input_base_class() );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="email" name="%1$s[%2$s]" class="%4$s" value="%3$s" data-bookaro-setting="%2$s"%5$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            esc_attr($val),
            esc_attr( $class ),
            disabled( $disabled, true, false )
        );
        $this->render_pro_badge( $args );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_field_wrapper_close();
    }

    private function input_base_class( string $extra = '' ): string
    {
        $base = 'w-full max-w-xl rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-sky-500';
        return trim( $base . ' ' . $extra );
    }

    /**
     * Render a checkbox field.
     *
     * @param array $args ['id'=>string, 'label'=>string]
     */
    public function render_checkbox_field(array $args): void
    {
        $opts    = $this->options->get_all();
        $id      = $args['id'];
        $has_value = array_key_exists( $id, $opts );
        $is_checked = $has_value ? ! empty( $opts[ $id ] ) : ! empty( $args['default'] );
        $disabled = $this->get_pro_disabled_attr( $args );
        $class = $this->get_pro_field_class( $args );
        $this->render_field_wrapper_open( $args );
        printf(
            '<input type="hidden" name="%1$s[%2$s]" value="0"%3$s>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            disabled( $disabled, true, false )
        );
        printf(
            '<label class="inline-flex items-center gap-3 text-sm"><input type="checkbox" name="%1$s[%2$s]" value="1" %3$s class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-500 %4$s" data-bookaro-setting="%2$s"%5$s> <span>%6$s</span></label>',
            esc_attr(OptionRepository::OPTION_KEY),
            esc_attr($id),
            checked( $is_checked, true, false ),
            esc_attr( $class ),
            disabled( $disabled, true, false ),
            esc_html($args['label'] ?? '')
        );
        if ( ! empty( $args['description'] ) ) {
            $description = $this->get_locked_description( $args, (string) $args['description'] );
            echo '<p class="description">' . esc_html( $description ) . '</p>';
        }
        $this->render_disabled_reason( $args );
        $this->render_pro_badge( $args );
        $this->render_field_wrapper_close();
    }

    private function get_pro_disabled_attr( array $args ): bool
    {
        return $this->is_disabled_by_integration_mode( $args );
    }

    private function get_pro_field_class( array $args, string $base = '' ): string
    {
        return trim( $base );
    }

    private function resolve_integration_mode_for_settings(): string
    {
        if ( function_exists( 'bookaro_get_integration_mode' ) ) {
            return sanitize_key( (string) bookaro_get_integration_mode() );
        }
        $site_type = $this->normalize_site_type_setting( $this->options->get( 'site_type', 'directory' ) );
        if ( in_array( $site_type, [ 'single_business', 'advanced_mapping' ], true ) ) {
            return 'generic';
        }
        $mode = (string) $this->options->get( 'integration_mode', 'auto' );
        $mode = sanitize_key( $mode );
        if ( ! in_array( $mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $mode = 'auto';
        }
        if ( 'auto' === $mode && function_exists( 'bookaro_is_mylisting_active' ) ) {
            $mode = bookaro_is_mylisting_active() ? 'mylisting' : 'generic';
        }
        return $mode;
    }

    private function get_default_ui_mode_for_resolved_mode( string $resolved_mode ): string
    {
        return ( 'generic' === sanitize_key( $resolved_mode ) ) ? 'shortcodes' : 'woocommerce';
    }

    private function resolve_ui_mode_from_options( array $opts ): string
    {
        $resolved_mode = $this->resolve_integration_mode_from_options( $opts );
        $default_ui_mode = $this->get_default_ui_mode_for_resolved_mode( $resolved_mode );
        $ui_mode = sanitize_key( (string) ( $opts['ui_mode'] ?? $default_ui_mode ) );
        if ( ! in_array( $ui_mode, [ 'woocommerce', 'shortcodes' ], true ) ) {
            $ui_mode = $default_ui_mode;
        }
        if ( 'woocommerce' === $ui_mode ) {
            $woocommerce_active = function_exists( 'bookaro_is_woocommerce_active' )
                ? (bool) bookaro_is_woocommerce_active()
                : ( class_exists( 'WooCommerce' ) || defined( 'WC_VERSION' ) || function_exists( 'WC' ) );
            if ( ! $woocommerce_active ) {
                $ui_mode = 'shortcodes';
            }
        }
        return $ui_mode;
    }

    private function maybe_set_generic_launch_validation_notice_for_settings_update( array $before, array $after ): void
    {
        if ( 'generic' !== $this->resolve_integration_mode_from_options( $after ) ) {
            return;
        }

        $before_hash = $this->build_generic_validation_config_hash( $before );
        $after_hash = $this->build_generic_validation_config_hash( $after );
        if ( '' === $after_hash || $before_hash === $after_hash ) {
            return;
        }

        $this->set_generic_launch_validation_notice();
    }

    private function set_generic_launch_validation_notice(): void
    {
        set_transient(
            self::GENERIC_LAUNCH_NOTICE_KEY,
            [
                'type' => 'warning',
                'message' => $this->get_generic_launch_readiness_notice_message(),
                'action_url' => $this->get_generic_validation_status_url(),
                'action_label' => $this->get_generic_launch_readiness_notice_action_label(),
            ],
            10 * MINUTE_IN_SECONDS
        );
    }

    private function get_generic_validation_prompt( array $opts = [] ): array
    {
        if ( empty( $opts ) ) {
            $opts = $this->options->get_all();
        }

        $resolved_mode = $this->resolve_integration_mode_from_options( $opts );
        $active = ( 'generic' === $resolved_mode );

        $current_hash = $this->build_generic_validation_config_hash( $opts );
        $snapshot = function_exists( 'bookaro_get_generic_validation_snapshot' )
            ? bookaro_get_generic_validation_snapshot()
            : [ 'hash' => (string) ( $opts['generic_validation_review_hash'] ?? '' ), 'at' => absint( $opts['generic_validation_reviewed_at'] ?? 0 ) ];
        $reviewed_hash_raw = strtolower( (string) ( $snapshot['hash'] ?? '' ) );
        $reviewed_hash = preg_replace( '/[^a-f0-9]/', '', $reviewed_hash_raw );
        if ( ! is_string( $reviewed_hash ) ) {
            $reviewed_hash = '';
        }

        return [
            'active' => $active,
            'needs_validation' => $active && '' !== $current_hash && $reviewed_hash !== $current_hash,
            'current_hash' => $current_hash,
            'reviewed_hash' => $reviewed_hash,
            'reviewed_at' => absint( $snapshot['at'] ?? 0 ),
        ];
    }

    private function resolve_integration_mode_from_options( array $opts ): string
    {
        $site_type = $this->resolve_site_type_from_options( $opts );
        if ( in_array( $site_type, [ 'single_business', 'advanced_mapping' ], true ) ) {
            return 'generic';
        }
        $mode = sanitize_key( (string) ( $opts['integration_mode'] ?? 'auto' ) );
        if ( ! in_array( $mode, [ 'auto', 'mylisting', 'generic' ], true ) ) {
            $mode = 'auto';
        }
        if ( 'auto' === $mode ) {
            $mode = ( function_exists( 'bookaro_is_mylisting_active' ) && bookaro_is_mylisting_active() )
                ? 'mylisting'
                : 'generic';
        }
        return $mode;
    }

    private function build_generic_validation_config_hash( array $opts ): string
    {
        if ( function_exists( 'bookaro_build_generic_validation_config_hash' ) ) {
            return bookaro_build_generic_validation_config_hash( $opts );
        }
        $allowed_types = $opts['allowed_listing_post_types'] ?? [];
        if ( ! is_array( $allowed_types ) ) {
            $allowed_types = [ $allowed_types ];
        }
        $allowed_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $allowed_types ) ) ) );
        sort( $allowed_types );

        $allowed_pages = $opts['allowed_listing_pages'] ?? [];
        if ( ! is_array( $allowed_pages ) ) {
            $allowed_pages = [ $allowed_pages ];
        }
        $allowed_pages = array_values( array_unique( array_filter( array_map( 'absint', $allowed_pages ) ) ) );
        sort( $allowed_pages );

        $listing_page_overrides = [];
        $raw_overrides = $opts['listing_post_type_pages'] ?? [];
        if ( is_array( $raw_overrides ) ) {
            foreach ( $raw_overrides as $post_type => $contexts ) {
                $post_type = sanitize_key( (string) $post_type );
                if ( '' === $post_type || ! is_array( $contexts ) ) {
                    continue;
                }
                $row = [];
                foreach ( [ 'booking', 'provider_dashboard', 'customer_dashboard', 'services' ] as $context ) {
                    $page_id = absint( $contexts[ $context ] ?? 0 );
                    if ( $page_id > 0 ) {
                        $row[ $context ] = $page_id;
                    }
                }
                if ( ! empty( $row ) ) {
                    $listing_page_overrides[ $post_type ] = $row;
                }
            }
        }
        ksort( $listing_page_overrides );

        $payload = [
            'site_type' => $this->resolve_site_type_from_options( $opts ),
            'integration_mode_setting' => sanitize_key( (string) ( $opts['integration_mode'] ?? 'auto' ) ),
            'integration_mode_resolved' => $this->resolve_integration_mode_from_options( $opts ),
            'ui_mode' => sanitize_key( (string) ( $opts['ui_mode'] ?? '' ) ),
            'generic_advanced_setup_enabled' => ! empty( $opts['generic_advanced_setup_enabled'] ) ? 1 : 0,
            'generic_setup_preset' => sanitize_key( (string) ( $opts['generic_setup_preset'] ?? '' ) ),
            'allowed_listing_post_types' => $allowed_types,
            'allowed_listing_pages' => $allowed_pages,
            'listing_post_type' => sanitize_key( (string) ( $opts['listing_post_type'] ?? '' ) ),
            'provider_resolution_mode' => sanitize_key( (string) ( $opts['provider_resolution_mode'] ?? '' ) ),
            'provider_meta_key' => sanitize_key( (string) ( $opts['provider_meta_key'] ?? $opts['listing_provider_meta_key'] ?? '' ) ),
            'services_model' => sanitize_key( (string) ( $opts['services_model'] ?? '' ) ),
            'generic_services_mode' => sanitize_key( (string) ( $opts['generic_services_mode'] ?? '' ) ),
            'provider_services_enabled' => ! empty( $opts['provider_services_enabled'] ) ? 1 : 0,
            'allow_provider_custom_services' => ! empty( $opts['allow_provider_custom_services'] ) ? 1 : 0,
            'allow_provider_override_catalog' => ! empty( $opts['allow_provider_override_catalog'] ) ? 1 : 0,
            'booking_placement_strategy' => $this->normalize_booking_placement_strategy_setting(
                $opts['booking_placement_strategy'] ?? '',
                ( 'single_business' === $this->resolve_site_type_from_options( $opts ) ) ? 'normal_page' : 'directory_template'
            ),
            'auto_insert_booking_button_enabled' => ! empty( $opts['auto_insert_booking_button_enabled'] ) ? 1 : 0,
            'auto_insert_booking_button' => sanitize_key( (string) ( $opts['auto_insert_booking_button'] ?? '' ) ),
            'auto_insert_booking_button_selector' => sanitize_text_field( (string) ( $opts['auto_insert_booking_button_selector'] ?? '' ) ),
            'generic_setup_pages' => [
                'booking' => absint( $opts['generic_setup_booking_page_id'] ?? 0 ),
                'provider_dashboard' => absint( $opts['generic_setup_provider_dashboard_page_id'] ?? 0 ),
                'customer_dashboard' => absint( $opts['generic_setup_customer_dashboard_page_id'] ?? 0 ),
                'services' => absint( $opts['generic_setup_services_page_id'] ?? 0 ),
            ],
            'global_pages' => [
                'booking' => absint( $opts['booking_page_id'] ?? 0 ),
                'provider_dashboard' => absint( $opts['provider_dashboard_page_id'] ?? 0 ),
                'customer_dashboard' => absint( $opts['customer_dashboard_page_id'] ?? 0 ),
            ],
            'generic_setup_context' => [
                'listing_id' => absint( $opts['generic_setup_listing_id'] ?? 0 ),
                'provider_user_id' => absint( $opts['generic_setup_provider_user_id'] ?? 0 ),
                'service_id' => absint( $opts['generic_setup_service_id'] ?? 0 ),
                'default_provider_id' => absint( $opts['default_provider_id'] ?? 0 ),
            ],
            'listing_post_type_pages' => $listing_page_overrides,
        ];

        $json = wp_json_encode( $payload );
        if ( ! is_string( $json ) || '' === $json ) {
            return '';
        }

        return hash( 'sha256', $json );
    }

    private function normalize_site_type_setting( $site_type ): string
    {
        $site_type = sanitize_key( (string) $site_type );
        if ( ! in_array( $site_type, [ 'directory', 'single_business', 'advanced_mapping' ], true ) ) {
            return 'directory';
        }
        return $site_type;
    }

    private function resolve_site_type_from_options( array $opts ): string
    {
        return $this->normalize_site_type_setting( $opts['site_type'] ?? 'directory' );
    }

    private function normalize_booking_placement_strategy_setting( $strategy, string $fallback = 'directory_template' ): string
    {
        $strategy = sanitize_key( (string) $strategy );
        if ( ! in_array( $strategy, [ 'directory_template', 'normal_page', 'shortcode_only', 'css_selector' ], true ) ) {
            $strategy = sanitize_key( $fallback );
        }
        if ( ! in_array( $strategy, [ 'directory_template', 'normal_page', 'shortcode_only', 'css_selector' ], true ) ) {
            return 'directory_template';
        }
        return $strategy;
    }

    private function get_generic_launch_readiness_notice_message(): string
    {
        return __( 'Generic mode had mapping, service model, or page assignment changes since the last System Status review. Re-run System Status validation and clear every Warn/Fail check before launch.', 'mybookaro' );
    }

    private function get_generic_launch_readiness_notice_action_label(): string
    {
        return __( 'Run System Status validation', 'mybookaro' );
    }

    private function get_generic_validation_status_url(): string
    {
        return admin_url( 'admin.php?page=bookaro-system-status#bookaro-support-check-generic_validation_provider_resolution_sample' );
    }

    private function get_admin_host_mismatch_notice(): array
    {
        $request_host_raw = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
        $request_host_raw = trim( $request_host_raw );
        if ( $request_host_raw === '' ) {
            return [
                'active' => false,
                'message' => '',
            ];
        }

        $request_host = strtolower( preg_replace( '/:\d+$/', '', $request_host_raw ) );
        $admin_host_value = wp_parse_url( admin_url( '/' ), PHP_URL_HOST );
        $admin_host = is_string( $admin_host_value ) ? strtolower( trim( $admin_host_value ) ) : '';

        if ( $request_host === '' || $admin_host === '' || $request_host === $admin_host ) {
            return [
                'active' => false,
                'message' => '',
            ];
        }

        return [
            'active' => true,
            'message' => sprintf(
                /* translators: 1: current request host, 2: WordPress admin host */
                __( 'Host mismatch detected: you are on "%1$s" but WordPress admin URLs resolve to "%2$s". This can send settings saves to another domain. Verify WordPress Address and Site Address in Settings > General.', 'mybookaro' ),
                $request_host,
                $admin_host
            ),
        ];
    }

    private function is_disabled_by_integration_mode( array $args ): bool
    {
        $modes = $args['disabled_integration_modes'] ?? [];
        if ( is_string( $modes ) ) {
            $modes = [ $modes ];
        }
        if ( ! is_array( $modes ) || empty( $modes ) ) {
            return false;
        }
        $modes = array_values( array_filter( array_map( 'sanitize_key', $modes ) ) );
        if ( empty( $modes ) ) {
            return false;
        }
        $current = $this->resolve_integration_mode_for_settings();
        return in_array( $current, $modes, true );
    }

    private function render_disabled_reason( array $args ): void
    {
        if ( ! $this->is_disabled_by_integration_mode( $args ) ) {
            return;
        }
        $reason = isset( $args['disabled_reason'] ) ? (string) $args['disabled_reason'] : '';
        if ( $reason === '' ) {
            return;
        }
        echo '<p class="description bookaro-disabled-reason">' . esc_html( $reason ) . '</p>';
    }

    private function preserve_existing_settings( array &$out, array $existing, array $keys ): void
    {
        foreach ( $keys as $key ) {
            if ( array_key_exists( $key, $existing ) ) {
                $out[ $key ] = $existing[ $key ];
            }
        }
    }

    /**
     * Pro feature badges and locked-field decorations were removed for the
     * Lite build. This is kept as an intentional no-op so the shared field
     * renderers (used by the real General/Appearance/Notifications/Roles/
     * Shortcodes tabs) can call it without branching.
     */
    private function render_pro_badge( array $args ): void
    {
    }

    /**
     * Locked-field description prefixing was removed for the Lite build.
     * Real tabs call this for their normal descriptions, so it now simply
     * returns the description unchanged.
     */
    private function get_locked_description( array $args, string $description ): string
    {
        return trim( $description );
    }

    private function sanitize_css_var( string $value, string $fallback ): string
    {
        $value = trim( (string) $value );
        if ( $value === '' ) {
            return $fallback;
        }
        $value = preg_replace( '/[<>"\'\\\\]/', '', $value );
        return $value !== '' ? $value : $fallback;
    }

    private function plugin_asset_path( string $relative ): string
    {
        $relative = ltrim( $relative, '/\\' );
        if ( defined( 'BOOKARO_PLUGIN_FILE' ) ) {
            return plugin_dir_path( BOOKARO_PLUGIN_FILE ) . $relative;
        }
        if ( defined( 'BOOKARO_PLUGIN_DIR' ) ) {
            return rtrim( BOOKARO_PLUGIN_DIR, '/\\' ) . '/' . $relative;
        }
        return $relative;
    }

    private function plugin_asset_url( string $relative ): string
    {
        $relative = ltrim( $relative, '/\\' );
        if ( defined( 'BOOKARO_PLUGIN_FILE' ) ) {
            return plugins_url( $relative, BOOKARO_PLUGIN_FILE );
        }
        if ( defined( 'BOOKARO_PLUGIN_URL' ) ) {
            return rtrim( BOOKARO_PLUGIN_URL, '/' ) . '/' . $relative;
        }
        return $relative;
    }
}
