<?php
namespace Bookaro\Controller\Frontend;

defined( 'ABSPATH' ) || exit;

use Bookaro\Frontend\I18n;

/**
 * Registers Bookaro shortcodes for dashboards and booking UI.
 */
class ShortcodesController
{
    private static bool $needs_booking_assets = false;
    private static bool $needs_provider_dashboard_assets = false;
    private static bool $needs_customer_dashboard_assets = false;
    private static bool $booking_container_rendered = false;
    private static array $auto_inserted_posts = [];

    public function register_hooks(): void
    {
        add_shortcode( 'bookaro_provider_dashboard', [ $this, 'render_provider_dashboard' ] );
        add_shortcode( 'bookaro_customer_dashboard', [ $this, 'render_customer_dashboard' ] );
        add_shortcode( 'bookaro_booking_button', [ $this, 'render_booking_button' ] );
        add_shortcode( 'bookaro_booking_form', [ $this, 'render_booking_form' ] );
        add_shortcode( 'bookaro_services_cards', [ $this, 'render_services_cards' ] );
        // Defensive alias: some early demo pages / docs / migrated content
        // use the bare `[bookaro_booking]` tag (no `_form`/`_button` suffix).
        // Without this WordPress prints the literal bracket text on the
        // front-end, which is what visitors see on any page that still has
        // it. Map it to the booking-form behavior.
        add_shortcode( 'bookaro_booking', [ $this, 'render_booking_form' ] );
        add_filter( 'the_posts', [ $this, 'detect_shortcodes' ], 10, 2 );
        add_filter( 'the_content', [ $this, 'maybe_auto_insert_booking_button' ], 12 );
    }

    public function detect_shortcodes( array $posts, $query = null ): array
    {
        // Block themes (and many classic themes) render nav menus / sidebars /
        // related-posts widgets through sub-queries that fire `the_posts`
        // with OTHER pages of the site. Without this guard, visiting the
        // provider dashboard page would mark "needs_customer_dashboard_assets"
        // true because the nav menu loads the customer-dashboard page too —
        // and we'd enqueue owner-dashboard.js on the wrong screen, leaving
        // a "mount target not found" warning + a wasted ~100KB download.
        if ( $query instanceof \WP_Query && ! $query->is_main_query() ) {
            return $posts;
        }

        $shortcodes_enabled = $this->shortcodes_enabled();

        $block_checks = [];
        if ( function_exists( 'has_block' ) ) {
            $block_checks = [
                'booking_button' => 'bookaro/booking-button',
                'booking_form'   => 'bookaro/booking-form',
                'provider_dash'  => 'bookaro/provider-dashboard',
                'customer_dash'  => 'bookaro/customer-dashboard',
            ];
        }

        foreach ( $posts as $post ) {
            if ( ! isset( $post->post_content ) ) {
                continue;
            }
            if ( $shortcodes_enabled ) {
                if ( has_shortcode( $post->post_content, 'bookaro_provider_dashboard' ) ) {
                    self::$needs_provider_dashboard_assets = true;
                }
                if ( has_shortcode( $post->post_content, 'bookaro_customer_dashboard' ) ) {
                    self::$needs_customer_dashboard_assets = true;
                }
                if ( $block_checks ) {
                    if ( has_block( $block_checks['provider_dash'], $post ) ) {
                        self::$needs_provider_dashboard_assets = true;
                    }
                    if ( has_block( $block_checks['customer_dash'], $post ) ) {
                        self::$needs_customer_dashboard_assets = true;
                    }
                }
            }
            if (
                has_shortcode( $post->post_content, 'bookaro_booking_button' ) ||
                has_shortcode( $post->post_content, 'bookaro_booking_form' ) ||
                has_shortcode( $post->post_content, 'bookaro_services_cards' )
            ) {
                self::$needs_booking_assets = true;
            }
            if ( $block_checks ) {
                if (
                    has_block( $block_checks['booking_button'], $post ) ||
                    has_block( $block_checks['booking_form'], $post ) ||
                    has_block( 'bookaro/services-cards', $post )
                ) {
                    self::$needs_booking_assets = true;
                }
            }
        }
        return $posts;
    }

    public static function needs_booking_assets(): bool
    {
        return self::$needs_booking_assets;
    }

    public static function needs_provider_dashboard_assets(): bool
    {
        return self::$needs_provider_dashboard_assets;
    }

    public static function needs_customer_dashboard_assets(): bool
    {
        return self::$needs_customer_dashboard_assets;
    }

    public static function render_generic_setup_notice( string $issue, array $args = [] ): string
    {
        $details = self::get_generic_setup_issue_details( $issue, $args );
        if ( empty( $details ) ) {
            return '';
        }

        $is_admin_user = current_user_can( 'manage_options' );
        // Non-admins should not see admin-only setup hints; render nothing for them.
        if ( ! $is_admin_user ) {
            return '';
        }

        $title       = (string) ( $details['title'] ?? __( 'Finish Bookaro setup', 'mybookaro' ) );
        $description = (string) ( $details['admin_description'] ?? '' );
        $action_url  = (string) ( $details['action_url'] ?? '' );
        $action_label = (string) ( $details['action_label'] ?? '' );

        $action_html = '';
        if ( '' !== $action_url && '' !== $action_label ) {
            $action_html = sprintf(
                '<div class="bookaro-setup-notice-actions"><a href="%1$s" class="bookaro-btn bookaro-btn--outline bookaro-btn--sm mt-2">%2$s</a></div>',
                esc_url( $action_url ),
                esc_html( $action_label )
            );
        }

        return self::render_frontend_setup_notice_card( $title, $description, $action_html );
    }

    public static function get_generic_setup_issue_details( string $issue, array $args = [] ): array
    {
        $issue = sanitize_key( $issue );
        $wizard_listings_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=listings' );
        $wizard_services_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=services' );

        switch ( $issue ) {
            case 'generic_missing_listing_context':
                return [
                    'title'             => __( 'Booking button needs context', 'mybookaro' ),
                    'admin_description' => __( 'This page is not an allowed listing type, so the booking button cannot resolve a listing or provider. Add listing_id, provider_id or service_id to the shortcode, or place this shortcode on an allowed listing page.', 'mybookaro' ),
                    'action_url'        => $wizard_listings_url,
                    'action_label'      => __( 'Open Listings step', 'mybookaro' ),
                ];

            case 'generic_missing_provider_context':
                return [
                    'title'             => __( 'Set a Default Provider', 'mybookaro' ),
                    'admin_description' => __( 'No listing context was resolved and no provider mapping is available. Either pass listing_id/provider_id explicitly on the shortcode, or set Default Provider in Setup Wizard > Listings.', 'mybookaro' ),
                    'action_url'        => $wizard_listings_url,
                    'action_label'      => __( 'Set Default Provider', 'mybookaro' ),
                ];
        }

        return [];
    }

    public static function render_single_business_setup_notice( string $issue, array $args = [] ): string
    {
        $details = self::get_single_business_setup_issue_details( $issue, $args );
        if ( empty( $details ) ) {
            return '';
        }

        $is_admin_user = current_user_can( 'manage_options' );
        $title = (string) ( $details['title'] ?? __( 'Finish Single Business setup', 'mybookaro' ) );
        $description = $is_admin_user
            ? (string) ( $details['admin_description'] ?? '' )
            : (string) ( $details['user_description'] ?? __( 'Booking is temporarily unavailable while the site admin finishes setup.', 'mybookaro' ) );
        $action_url = $is_admin_user
            ? (string) ( $details['action_url'] ?? '' )
            : '';
        $action_label = $is_admin_user
            ? (string) ( $details['action_label'] ?? '' )
            : '';

        $action_links = [];
        if ( '' !== $action_url && '' !== $action_label ) {
            $action_links[] = sprintf(
                '<a href="%1$s" class="bookaro-btn bookaro-btn--outline bookaro-btn--sm mt-2">%2$s</a>',
                esc_url( $action_url ),
                esc_html( $action_label )
            );
        }
        if ( empty( $action_links ) && ! $is_admin_user ) {
            $admin_email = sanitize_email( (string) get_option( 'admin_email', '' ) );
            if ( '' !== $admin_email ) {
                $action_links[] = sprintf(
                    '<a href="%1$s" class="bookaro-btn bookaro-btn--outline bookaro-btn--sm mt-2">%2$s</a>',
                    esc_url( 'mailto:' . antispambot( $admin_email ) ),
                    esc_html__( 'Contact site admin', 'mybookaro' )
                );
            }
        }

        $action_html = '';
        if ( ! empty( $action_links ) ) {
            $action_html = '<div class="bookaro-setup-notice-actions">' . implode( ' ', $action_links ) . '</div>';
        }

        return self::render_frontend_setup_notice_card( $title, $description, $action_html );
    }

    public static function get_single_business_setup_issue_details( string $issue, array $args = [] ): array
    {
        $issue = sanitize_key( $issue );
        $service_type = defined( 'BOOKARO_SERVICE_POST_TYPE' ) ? BOOKARO_SERVICE_POST_TYPE : 'bookaro_service';
        $wizard_listings_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=listings' );
        $wizard_pages_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=pages' );
        $wizard_services_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=services' );
        $create_service_url = admin_url( 'post-new.php?post_type=' . $service_type );
        $fast_path_url = self::get_single_business_fast_path_url();
        $service_count = max( 0, absint( $args['service_count'] ?? 0 ) );

        switch ( $issue ) {
            case 'missing_default_provider':
                return [
                    'title' => __( 'Finish Single Business setup', 'mybookaro' ),
                    'admin_description' => __( 'Default Provider is missing, so Single Business booking cannot resolve provider context. Next action: open Setup Wizard > Listings, set Default Provider, save, then reload this page.', 'mybookaro' ),
                    'user_description' => __( 'Booking is temporarily unavailable while the site admin finishes setup.', 'mybookaro' ),
                    'action_url' => $wizard_listings_url,
                    'action_label' => __( 'Set Default Provider', 'mybookaro' ),
                ];

            case 'missing_booking_page':
                return [
                    'title' => __( 'Assign a booking page', 'mybookaro' ),
                    'admin_description' => __( 'Booking page is missing, so customers do not have a confirmed booking entry in Single Business lane. Next action: open Setup Wizard > Pages, assign Booking page, save, then reload this page.', 'mybookaro' ),
                    'user_description' => __( 'Booking is temporarily unavailable while the site admin finishes setup.', 'mybookaro' ),
                    'action_url' => $wizard_pages_url,
                    'action_label' => __( 'Assign Booking page', 'mybookaro' ),
                ];

            case 'missing_dashboard_pages':
                return [
                    'title' => __( 'Assign dashboard pages', 'mybookaro' ),
                    'admin_description' => __( 'Provider/Customer dashboard pages are missing for Single Business Shortcodes mode. Next action: open Setup Wizard > Pages, assign the missing dashboard pages, save, then reload this page.', 'mybookaro' ),
                    'user_description' => __( 'Dashboard is temporarily unavailable while the site admin finishes setup.', 'mybookaro' ),
                    'action_url' => $wizard_pages_url,
                    'action_label' => __( 'Assign dashboard pages', 'mybookaro' ),
                ];

            case 'missing_placement_strategy':
                return [
                    'title' => __( 'Fix booking placement', 'mybookaro' ),
                    'admin_description' => __( 'Single Business booking placement is not configured correctly. Next action: open Setup Wizard > Services, choose Normal page, Shortcode/block only, or CSS selector placement, save, then reload this page.', 'mybookaro' ),
                    'user_description' => __( 'Booking is temporarily unavailable while the site admin finishes setup.', 'mybookaro' ),
                    'action_url' => $wizard_services_url,
                    'action_label' => __( 'Fix placement strategy', 'mybookaro' ),
                ];

            case 'missing_services':
                $admin_description = __( 'Booking UI cannot show service choices because no services are available for the Default Provider. Next action: create at least one service for that provider, then reload this page and preview booking.', 'mybookaro' );
                $action_url = $create_service_url;
                $action_label = __( 'Create first service', 'mybookaro' );
                if ( $service_count > 0 ) {
                    $admin_description = sprintf(
                        /* translators: %d: available services count */
                        __( 'Available services for the Default Provider: %d. If booking still looks incomplete, next action: open Setup Wizard > Services, confirm Services Source and placement, then reload this page and retry preview booking.', 'mybookaro' ),
                        $service_count
                    );
                    $action_url = $wizard_services_url;
                    $action_label = __( 'Open services step', 'mybookaro' );
                }

                return [
                    'title' => __( 'Add at least one service', 'mybookaro' ),
                    'admin_description' => $admin_description,
                    'user_description' => __( 'Booking is temporarily unavailable while the site admin adds services.', 'mybookaro' ),
                    'action_url' => $action_url,
                    'action_label' => $action_label,
                ];
        }

        return [];
    }

    private static function get_single_business_fast_path_url(): string
    {
        $url = admin_url( 'admin-post.php?action=bookaro_getting_started_fast_path_booking&listing_id=0' );
        if ( function_exists( 'add_query_arg' ) ) {
            $url = add_query_arg(
                [
                    'action' => 'bookaro_getting_started_fast_path_booking',
                    'listing_id' => 0,
                ],
                admin_url( 'admin-post.php' )
            );
        }
        if ( function_exists( 'wp_nonce_url' ) ) {
            return wp_nonce_url( $url, 'bookaro_getting_started_fast_path_booking' );
        }

        return $url;
    }

    private static function render_frontend_setup_notice_card( string $title, string $description, string $action_html = '' ): string
    {
        // Issue #27: ship inline styles so the amber notice renders even on themes/pages
        // that don't load the admin Tailwind utility CSS. Existing Tailwind class names are
        // kept for back-compat with anyone targeting them in custom CSS; new BEM-style
        // classes carry the actual styling.
        $styles = self::render_setup_notice_styles_once();

        return $styles . sprintf(
            '<div class="bookaro-setup-notice bookaro-card bookaro-card__body mb-4"><div class="bookaro-setup-notice__card rounded-lg border border-amber-200 bg-amber-50 p-3"><h3 class="bookaro-setup-notice__title text-sm font-semibold text-amber-900">%1$s</h3><p class="bookaro-setup-notice__desc mt-1 text-sm text-amber-800">%2$s</p>%3$s</div></div>',
            esc_html( sanitize_text_field( $title ) ),
            esc_html( sanitize_text_field( $description ) ),
            $action_html
        );
    }

    /**
     * Enqueue the small CSS bundle for setup notices once per page. The CSS is
     * attached as inline data on a dedicated, src-less style handle via
     * wp_add_inline_style() instead of printing an inline style element, and so
     * always returns '' for the notice markup.
     */
    private static function render_setup_notice_styles_once(): string
    {
        static $printed = false;
        if ( $printed ) {
            return '';
        }
        $printed = true;

        $css = '.bookaro-setup-notice{margin:0 0 16px;}'
             . '.bookaro-setup-notice__card{border:1px solid #fcd34d;background:#fef3c7;border-radius:8px;padding:12px;}'
             . '.bookaro-setup-notice__title{margin:0;font-size:14px;font-weight:600;color:#78350f;line-height:1.4;}'
             . '.bookaro-setup-notice__desc{margin:4px 0 0;font-size:14px;color:#92400e;line-height:1.5;}'
             . '.bookaro-setup-notice-actions{margin-top:8px;}'
             . '.bookaro-setup-notice-actions a{display:inline-block;background:transparent;border:1px solid #92400e;color:#78350f;padding:4px 10px;border-radius:6px;text-decoration:none;font-size:13px;line-height:1.4;}'
             . '.bookaro-setup-notice-actions a:hover{background:#fde68a;color:#78350f;}';

        if ( function_exists( 'wp_register_style' ) && function_exists( 'wp_add_inline_style' ) ) {
            $handle = 'bookaro-setup-notice';
            if ( ! wp_style_is( $handle, 'registered' ) ) {
                wp_register_style( $handle, false, [], BOOKARO_VERSION );
            }
            wp_enqueue_style( $handle );
            wp_add_inline_style( $handle, $css );
        }

        return '';
    }

    /**
     * Build admin diagnostics for generic booking button visibility rules.
     *
     * This mirrors the core conditions used by auto-insert booking button logic
     * so setup/status validation can report the same pass/fail outcome.
     *
     * @return array{state:string,value:string,detail:string,fix:string}
     */
    public static function get_admin_booking_button_visibility_diagnostics( int $listing_id ): array
    {
        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? (string) bookaro_get_integration_mode()
            : 'auto';
        $frontend_enabled = function_exists( 'bookaro_is_frontend_enabled' )
            ? (bool) bookaro_is_frontend_enabled()
            : true;
        $site_type = function_exists( 'bookaro_get_site_type' )
            ? sanitize_key( (string) bookaro_get_site_type() )
            : 'directory';
        if ( ! in_array( $site_type, [ 'directory', 'single_business', 'advanced_mapping' ], true ) ) {
            $site_type = 'directory';
        }
        $single_business = ( 'single_business' === $site_type );
        $placement_strategy = function_exists( 'bookaro_get_booking_placement_strategy' )
            ? sanitize_key( (string) bookaro_get_booking_placement_strategy() )
            : '';
        if ( ! in_array( $placement_strategy, [ 'directory_template', 'normal_page', 'shortcode_only', 'css_selector' ], true ) ) {
            $placement_strategy = $single_business ? 'normal_page' : 'directory_template';
        }
        $requires_listing_context = ( 'directory_template' === $placement_strategy )
            || ( 'css_selector' === $placement_strategy && ! $single_business );
        $default_provider_id = function_exists( 'bookaro_get_option' )
            ? absint( bookaro_get_option( 'default_provider_id', 0 ) )
            : 0;
        $allowed_listing = $listing_id > 0
            && function_exists( 'bookaro_is_allowed_listing' )
            && bookaro_is_allowed_listing( $listing_id );

        $placement = function_exists( 'bookaro_get_auto_insert_booking_button_mode' )
            ? (string) bookaro_get_auto_insert_booking_button_mode()
            : '';
        $placement = sanitize_key( $placement );
        if ( 'css_selector' === $placement_strategy ) {
            $placement = 'selector';
        } elseif ( 'shortcode_only' === $placement_strategy ) {
            $placement = '';
        } elseif ( '' === $placement ) {
            $placement = 'after_content';
        }
        $selector = function_exists( 'bookaro_get_auto_insert_booking_button_selector' )
            ? sanitize_text_field( (string) bookaro_get_auto_insert_booking_button_selector() )
            : '';
        $selector = trim( $selector );

        $placement_valid = in_array( $placement, [ 'before_content', 'after_content', 'selector' ], true );
        $selector_required = ( 'selector' === $placement );
        $selector_valid = ! $selector_required || '' !== $selector;
        $auto_insert_valid = ( 'shortcode_only' === $placement_strategy )
            ? true
            : ( $placement_valid && $selector_valid );

        $has_booking_page = false;
        if ( function_exists( 'bookaro_get_booking_page_url' ) ) {
            if ( $requires_listing_context ) {
                if ( $listing_id > 0 ) {
                    $has_booking_page = '' !== (string) bookaro_get_booking_page_url( [ 'listing_id' => $listing_id ] );
                }
            } else {
                $has_booking_page = '' !== (string) bookaro_get_booking_page_url( [ 'listing_id' => 0 ] );
            }
        }
        // Single Business uses the global Booking page (booking_page_id) instead of a
        // listing-context page, so recognise it here — otherwise normal_page / shortcode
        // placement is wrongly flagged as "no booking page assigned" when one is set.
        if ( ! $has_booking_page && $single_business && function_exists( 'bookaro_get_option' ) ) {
            $has_booking_page = absint( bookaro_get_option( 'booking_page_id', 0 ) ) > 0;
        }

        $state = 'ok';
        $value = __( 'Pass', 'mybookaro' );
        $detail = '';
        $fix = '';
        $next_step = '';

        if ( 'generic' !== $integration_mode ) {
            $state = 'info';
            $value = __( 'Not required', 'mybookaro' );
            $detail = __( 'Booking button visibility diagnostics apply only in Generic mode.', 'mybookaro' );
            return [
                'state' => $state,
                'value' => $value,
                'detail' => $detail,
                'fix' => $fix,
                'next_step' => $next_step,
            ];
        }

        if ( ! $frontend_enabled ) {
            $state = 'error';
            $value = __( 'Fail', 'mybookaro' );
            $detail = __( 'Frontend booking UI is disabled, so customers will not see a booking entry anywhere.', 'mybookaro' );
            $fix = __( 'Enable frontend booking UI in Settings > General.', 'mybookaro' );
            $next_step = __( 'Next step: Open Settings > General, enable frontend booking UI, save, then re-run this check.', 'mybookaro' );
            return [
                'state' => $state,
                'value' => $value,
                'detail' => $detail,
                'fix' => $fix,
                'next_step' => $next_step,
            ];
        }

        if ( $requires_listing_context && ! $allowed_listing ) {
            $state = 'error';
            $value = __( 'Fail', 'mybookaro' );
            if ( $single_business ) {
                $detail = __( 'This placement strategy needs listing context, but no allowed listing was resolved. Without a real listing page, Bookaro cannot confirm where the booking entry should render.', 'mybookaro' );
                $fix = __( 'Single Business listings are optional. Open Setup Wizard > Services and switch placement to Normal page, Shortcode/block only, or CSS selector. Only use listing-context placement if your template intentionally requires listings.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Services, switch to Normal page, Shortcode/block only, or CSS selector placement, save, then re-run this check.', 'mybookaro' );
            } else {
                $detail = __( 'This placement strategy needs an allowed listing page, but Bookaro could not resolve one. Until there is listing context, it cannot confirm where customers will find the booking entry.', 'mybookaro' );
                $fix = __( 'Open Setup Wizard > Listings, confirm allowed listing post types/pages and provider mapping, then create or select a sample listing and retry.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Listings, confirm the allowed listing setup, save, then re-run this check with a valid sample listing.', 'mybookaro' );
            }
            return [
                'state' => $state,
                'value' => $value,
                'detail' => $detail,
                'fix' => $fix,
                'next_step' => $next_step,
            ];
        }

        if ( 'shortcode_only' === $placement_strategy ) {
            if ( $has_booking_page ) {
                $detail = __( 'Shortcode/block-only placement is selected and a Booking page is assigned. Bookaro expects you to place the booking block or shortcode manually on that page or template.', 'mybookaro' );
            } else {
                $state = 'warn';
                $value = __( 'Warning', 'mybookaro' );
                $detail = __( 'Shortcode/block-only placement is selected, but no booking page assignment was found. Customers have no confirmed page where the booking UI should load.', 'mybookaro' );
                $fix = __( 'Next action: open Setup Wizard > Pages, assign a Booking page, save, then place Booking Button/Services Cards manually on that page/template.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Pages, assign the Booking page, save, then add the booking block or shortcode to that page or template.', 'mybookaro' );
            }
            return [
                'state' => $state,
                'value' => $value,
                'detail' => $detail,
                'fix' => $fix,
                'next_step' => $next_step,
            ];
        }

        if ( ! $requires_listing_context && $default_provider_id < 1 ) {
            $state = 'error';
            $value = __( 'Fail', 'mybookaro' );
            $detail = __( 'Provider-first placement is active, but Default Provider is missing. Without a provider context, the booking page cannot render the correct provider-based flow.', 'mybookaro' );
            $fix = __( 'Next action: open Setup Wizard > Listings, set the Default Provider field, save, then retry.', 'mybookaro' );
            $next_step = __( 'Next step: Open Setup Wizard > Listings, set Default Provider, save, then re-run this check.', 'mybookaro' );
            return [
                'state' => $state,
                'value' => $value,
                'detail' => $detail,
                'fix' => $fix,
                'next_step' => $next_step,
            ];
        }

        if ( $auto_insert_valid ) {
            if ( 'selector' === $placement ) {
                $detail = sprintf(
                    /* translators: %s: CSS selector */
                    __( 'Auto-insert is ready. Bookaro can place the booking entry with the CSS selector `%s`.', 'mybookaro' ),
                    $selector
                );
            } else {
                $detail = sprintf(
                    /* translators: %s: placement value */
                    __( 'Auto-insert is ready. Bookaro can place the booking entry %s.', 'mybookaro' ),
                    $placement
                );
            }
        } elseif ( $has_booking_page ) {
            $state = 'warn';
            $value = __( 'Warning', 'mybookaro' );
            if ( $requires_listing_context ) {
                $detail = __( 'Auto-insert is not configured, but a Booking page is assigned. That still works if you place the booking block or shortcode manually where customers start bookings.', 'mybookaro' );
                $fix = __( 'Open Setup Wizard > Services to configure valid Auto-Insert, or keep manual placement and confirm booking blocks/shortcodes in your listing template.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Services to finish Auto-Insert, or keep manual placement and confirm the booking block or shortcode in the listing template.', 'mybookaro' );
            } else {
                $detail = __( 'A Booking page is assigned, but the provider-first placement rule is not configured yet. That still works if you place the booking block or shortcode manually on the assigned page.', 'mybookaro' );
                $fix = __( 'Open Setup Wizard > Services, set a valid Normal page/Shortcode/CSS selector strategy, or keep booking blocks/shortcodes on the assigned booking page.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Services to finish the placement rule, or keep manual placement on the assigned Booking page and confirm the block or shortcode is there.', 'mybookaro' );
            }
        } else {
            $state = 'error';
            $value = __( 'Fail', 'mybookaro' );
            if ( $requires_listing_context ) {
                $detail = __( 'Bookaro does not have a confirmed booking entry yet. There is no valid auto-insert rule and no Booking page assignment, so customers will not see how to start a booking.', 'mybookaro' );
                $fix = __( 'Open Setup Wizard > Services to set valid Auto-Insert placement/selector, or open Setup Wizard > Pages to assign Booking page and place booking UI manually.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Services or Setup Wizard > Pages, create one booking entry path, save, then re-run this check.', 'mybookaro' );
            } else {
                $detail = __( 'Bookaro does not have a confirmed provider-first booking entry yet. There is no valid placement rule and no Booking page assignment, so customers will not see how to start a booking.', 'mybookaro' );
                $fix = __( 'Open Setup Wizard > Services and configure a valid Normal page/Shortcode/CSS selector strategy, or open Setup Wizard > Pages to assign Booking page and place booking UI manually.', 'mybookaro' );
                $next_step = __( 'Next step: Open Setup Wizard > Services or Setup Wizard > Pages, create one provider-first booking entry path, save, then re-run this check.', 'mybookaro' );
            }
        }

        return [
            'state' => $state,
            'value' => $value,
            'detail' => $detail,
            'fix' => $fix,
            'next_step' => $next_step,
        ];
    }

    public function render_provider_dashboard(): string
    {
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return '';
        }
        if ( ! $this->shortcodes_enabled() ) {
            return '';
        }

        self::$needs_provider_dashboard_assets = true;
        $this->ensure_frontend_assets();

        if ( $this->requires_login() && ! is_user_logged_in() ) {
            return $this->render_login_prompt();
        }

        $user = wp_get_current_user();
        if ( ! bookaro_user_has_provider_access( $user->ID ) ) {
            return $this->render_permission_denied();
        }

        return $this->render_dashboard_page_assignment_notice( 'provider_dashboard' )
            . '<div id="bookaro-provider-dashboard" class="bookaro-scope bookaro-provider-dashboard"></div>';
    }

    public function render_customer_dashboard(): string
    {
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return '';
        }
        if ( ! $this->shortcodes_enabled() ) {
            return '';
        }

        self::$needs_customer_dashboard_assets = true;
        $this->ensure_frontend_assets();

        if ( $this->requires_login() && ! is_user_logged_in() ) {
            return $this->render_login_prompt();
        }

        $user = wp_get_current_user();
        if ( ! bookaro_user_has_customer_access( $user->ID ) ) {
            return $this->render_permission_denied();
        }

        return $this->render_dashboard_page_assignment_notice( 'customer_dashboard' )
            . '<div id="bookaro-owner-dashboard" class="bookaro-scope bookaro-owner-dashboard"></div>';
    }

    public function render_booking_button( array $atts = [] ): string
    {
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return '';
        }
        self::$needs_booking_assets = true;
        $this->ensure_frontend_assets();

        $context = $this->build_booking_context( $atts );
        if ( isset( $context['setup_issue'] ) ) {
            $setup_issue = (string) $context['setup_issue'];
            if ( 'missing_default_provider' === $setup_issue ) {
                return self::render_single_business_setup_notice( 'missing_default_provider' );
            }
            if ( in_array( $setup_issue, [ 'generic_missing_listing_context', 'generic_missing_provider_context' ], true ) ) {
                $notice = self::render_generic_setup_notice( $setup_issue );
                if ( '' !== $notice ) {
                    return $notice;
                }
                // Non-admin fallthrough: return silent HTML comment as before.
                return $context['error'] ?? '';
            }
        }
        if ( isset( $context['error'] ) ) {
            return $context['error'];
        }

        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : 'auto';
        if ( $this->should_render_single_business_missing_booking_page_notice( $context, $integration_mode ) ) {
            return self::render_single_business_setup_notice( 'missing_booking_page' );
        }
        $context['services'] = $this->resolve_booking_services_for_context( $context, $integration_mode );
        if ( $this->should_render_single_business_missing_services_notice( $context, $integration_mode ) ) {
            return self::render_single_business_setup_notice( 'missing_services' );
        }

        $this->localize_booking_data( $context );

        $button_label = $context['button_label'] ?: esc_html__( 'Book Now', 'mybookaro' );
        $button_class = $context['button_class'] ? ' ' . $context['button_class'] : '';

        return $this->render_booking_container() .
            sprintf(
                '<button type="button" class="bookaro-booking-btn bookaro-booking-launch bookaro-scope%1$s" data-listing-id="%2$d" data-provider-id="%3$d" data-service-label="%4$s" data-listing-price="%5$s" data-service-id="%6$d" data-service-duration="%7$d">%8$s</button>',
                esc_attr( $button_class ),
                (int) $context['listing_id'],
                (int) $context['provider_id'],
                esc_attr( $context['service_label'] ),
                esc_attr( $context['listing_price'] ),
                (int) $context['service_id'],
                (int) ( $context['service_duration'] ?? 0 ),
                esc_html( $button_label )
            );
    }

    public function render_booking_form( array $atts = [] ): string
    {
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return '';
        }
        self::$needs_booking_assets = true;
        $this->ensure_frontend_assets();

        $context = $this->build_booking_context( $atts );
        if ( isset( $context['setup_issue'] ) ) {
            $setup_issue = (string) $context['setup_issue'];
            if ( 'missing_default_provider' === $setup_issue ) {
                return self::render_single_business_setup_notice( 'missing_default_provider' );
            }
            if ( in_array( $setup_issue, [ 'generic_missing_listing_context', 'generic_missing_provider_context' ], true ) ) {
                $notice = self::render_generic_setup_notice( $setup_issue );
                if ( '' !== $notice ) {
                    return $notice;
                }
                return $context['error'] ?? '';
            }
        }
        if ( isset( $context['error'] ) ) {
            return $context['error'];
        }

        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : 'auto';
        if ( $this->should_render_single_business_missing_booking_page_notice( $context, $integration_mode ) ) {
            return self::render_single_business_setup_notice( 'missing_booking_page' );
        }
        $context['services'] = $this->resolve_booking_services_for_context( $context, $integration_mode );
        if ( $this->should_render_single_business_missing_services_notice( $context, $integration_mode ) ) {
            return self::render_single_business_setup_notice( 'missing_services' );
        }

        $this->localize_booking_data( $context );

        $auto_open = true;
        if ( isset( $atts['auto_open'] ) ) {
            $auto_open = filter_var( $atts['auto_open'], FILTER_VALIDATE_BOOLEAN );
        }

        $button_id = 'bookaro-booking-form-' . wp_generate_password( 8, false, false );
        $button_label = $context['button_label'] ?: esc_html__( 'Book Now', 'mybookaro' );
        $button_style = $auto_open ? ' style="display:none;"' : '';

        $output = $this->render_booking_container() .
            sprintf(
                '<button type="button" id="%1$s" class="bookaro-booking-btn bookaro-booking-launch bookaro-scope" data-listing-id="%2$d" data-provider-id="%3$d" data-service-label="%4$s" data-listing-price="%5$s" data-service-id="%6$d" data-service-duration="%7$d"%8$s>%9$s</button>',
                esc_attr( $button_id ),
                (int) $context['listing_id'],
                (int) $context['provider_id'],
                esc_attr( $context['service_label'] ),
                esc_attr( $context['listing_price'] ),
                (int) $context['service_id'],
                (int) ( $context['service_duration'] ?? 0 ),
                $button_style,
                esc_html( $button_label )
            );

        if ( $auto_open ) {
            // Attach the auto-open behavior as inline JS on the booking script
            // handle (enqueued above via ensure_frontend_assets) instead of
            // printing an inline script element in the shortcode output.
            $auto_open_js = sprintf(
                'window.addEventListener("load",function(){var btn=document.getElementById("%s");if(btn){btn.click();}});',
                esc_js( $button_id )
            );
            if ( function_exists( 'wp_add_inline_script' ) && wp_script_is( 'bookaro-booking-script', 'enqueued' ) ) {
                wp_add_inline_script( 'bookaro-booking-script', $auto_open_js );
            }
        }

        return $output;
    }

    public function render_services_cards( array $atts = [] ): string
    {
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return '';
        }
        self::$needs_booking_assets = true;
        $this->ensure_frontend_assets();
        $controller = new ServicesCardsController();
        return $controller->render( $atts, [ $this, 'render_booking_button' ] );
    }

    public function maybe_auto_insert_booking_button( string $content ): string
    {
        if ( is_admin() || wp_doing_ajax() || is_feed() ) {
            return $content;
        }
        if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
            return $content;
        }
        if ( function_exists( 'bookaro_is_frontend_enabled' ) && ! bookaro_is_frontend_enabled() ) {
            return $content;
        }
        if ( ! in_the_loop() || ! is_main_query() ) {
            return $content;
        }

        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : 'auto';
        if ( 'generic' !== $integration_mode ) {
            return $content;
        }

        $single_business = $this->is_single_business_site();
        $placement_strategy = function_exists( 'bookaro_get_booking_placement_strategy' )
            ? sanitize_key( (string) bookaro_get_booking_placement_strategy() )
            : '';
        if ( ! in_array( $placement_strategy, [ 'directory_template', 'normal_page', 'shortcode_only', 'css_selector' ], true ) ) {
            $placement_strategy = $single_business ? 'normal_page' : 'directory_template';
        }
        if ( 'shortcode_only' === $placement_strategy ) {
            return $content;
        }

        $placement = function_exists( 'bookaro_get_auto_insert_booking_button_mode' )
            ? bookaro_get_auto_insert_booking_button_mode()
            : sanitize_key( (string) bookaro_get_option( 'auto_insert_booking_button', '' ) );
        $placement = sanitize_key( (string) $placement );
        if ( 'css_selector' === $placement_strategy ) {
            $placement = 'selector';
        } elseif ( ! in_array( $placement, [ 'before_content', 'after_content' ], true ) ) {
            $placement = 'after_content';
        }

        $listing_singular = false;
        if ( function_exists( 'bookaro_is_listing_singular' ) ) {
            $listing_singular = (bool) bookaro_is_listing_singular();
        } elseif ( function_exists( 'is_singular' ) ) {
            $listing_singular = (bool) is_singular();
        }
        if ( function_exists( 'is_singular' ) && ! is_singular() ) {
            return $content;
        }

        $post = get_post();
        if ( ! $post instanceof \WP_Post ) {
            return $content;
        }

        $requires_listing_context = ( 'directory_template' === $placement_strategy )
            || ( 'css_selector' === $placement_strategy && ! $single_business );
        if ( $requires_listing_context ) {
            if ( ! $listing_singular ) {
                return $content;
            }
            if ( function_exists( 'bookaro_is_allowed_listing' ) && ! bookaro_is_allowed_listing( $post->ID ) ) {
                return $content;
            }
        } else {
            if ( 'normal_page' === $placement_strategy && $listing_singular ) {
                return $content;
            }
        }

        if ( $this->content_has_booking_ui( (string) $post->post_content ) ) {
            return $content;
        }
        if ( isset( self::$auto_inserted_posts[ $post->ID ] ) ) {
            return $content;
        }

        $button_atts = [
            'class' => 'bookaro-auto-insert',
        ];
        if ( $requires_listing_context ) {
            $button_atts['listing_id'] = (int) $post->ID;
        } else {
            $provider_id = $this->get_default_provider_id();
            if ( $provider_id < 1 ) {
                return $content;
            }
            $button_atts['provider_id'] = $provider_id;
        }

        $button_html = $this->render_booking_button( $button_atts );
        if ( '' === $button_html ) {
            return $content;
        }

        self::$auto_inserted_posts[ $post->ID ] = true;

        if ( 'before_content' === $placement ) {
            return $button_html . $content;
        }
        if ( 'after_content' === $placement ) {
            return $content . $button_html;
        }
        if ( 'selector' === $placement ) {
            $selector = function_exists( 'bookaro_get_auto_insert_booking_button_selector' )
                ? bookaro_get_auto_insert_booking_button_selector()
                : sanitize_text_field( (string) bookaro_get_option( 'auto_insert_booking_button_selector', '' ) );
            $selector = is_string( $selector ) ? trim( $selector ) : '';
            if ( '' === $selector ) {
                return $content;
            }
            $payload = wp_json_encode( $button_html, JSON_UNESCAPED_UNICODE );
            $selector_json = wp_json_encode( $selector, JSON_UNESCAPED_UNICODE );
            if ( ! is_string( $payload ) || ! is_string( $selector_json ) ) {
                return $content;
            }
            // Inject the booking button next to the configured CSS selector via
            // inline JS attached to the booking script handle (enqueued by
            // render_booking_button above) rather than an inline script element.
            $selector_js = sprintf(
                'document.addEventListener("DOMContentLoaded",function(){var s=%1$s;var t=null;try{t=document.querySelector(s);}catch(e){return;}if(!t){return;}t.insertAdjacentHTML("afterend",%2$s);});',
                $selector_json,
                $payload
            );
            if ( function_exists( 'wp_add_inline_script' ) && wp_script_is( 'bookaro-booking-script', 'enqueued' ) ) {
                wp_add_inline_script( 'bookaro-booking-script', $selector_js );
            }
            return $content;
        }

        return $content;
    }

    private function content_has_booking_ui( string $content ): bool
    {
        if ( '' === $content ) {
            return false;
        }
        if ( function_exists( 'has_shortcode' ) ) {
            $shortcodes = [ 'bookaro_booking_button', 'bookaro_booking_form', 'bookaro_services_cards' ];
            foreach ( $shortcodes as $shortcode ) {
                if ( has_shortcode( $content, $shortcode ) ) {
                    return true;
                }
            }
        }
        if ( function_exists( 'has_block' ) ) {
            if (
                has_block( 'bookaro/booking-button', $content )
                || has_block( 'bookaro/booking-form', $content )
                || has_block( 'bookaro/services-cards', $content )
            ) {
                return true;
            }
        }
        return false;
    }

    private function build_booking_context( array $atts ): array
    {
        $atts = shortcode_atts(
            [
                'listing_id'   => '',
                'provider_id'  => '',
                'service_id'   => '',
                'service_label'=> '',
                'service_duration' => '',
                'duration'     => '',
                'label'        => '',
                'price'        => '',
                'button_label' => '',
                'button_text'  => '',
                'class'        => '',
            ],
            $atts,
            'bookaro_booking_button'
        );

        $explicit_listing_id = absint( $atts['listing_id'] );
        $explicit_provider_id = absint( $atts['provider_id'] );
        $explicit_service_id = absint( $atts['service_id'] );
        $has_explicit_context = ( $explicit_listing_id > 0 || $explicit_provider_id > 0 || $explicit_service_id > 0 );
        $single_business = $this->is_single_business_site();

        $current_id = 0;
        if ( function_exists( 'get_the_ID' ) ) {
            $current_id = (int) get_the_ID();
        }
        if ( $current_id < 1 && function_exists( 'get_queried_object_id' ) ) {
            $current_id = (int) get_queried_object_id();
        }
        if (
            $current_id > 0
            && function_exists( 'bookaro_is_allowed_listing' )
            && ! $has_explicit_context
            && ! bookaro_is_allowed_listing( $current_id )
            && ! $single_business
        ) {
            $integration_mode_for_notice = function_exists( 'bookaro_get_integration_mode' )
                ? sanitize_key( (string) bookaro_get_integration_mode() )
                : 'auto';
            if ( 'generic' === $integration_mode_for_notice ) {
                return [
                    'setup_issue' => 'generic_missing_listing_context',
                    'error' => '<!-- Bookaro: shortcode disabled for this content type. Use an allowed listing type/page or pass listing_id/provider_id explicitly. -->',
                ];
            }
            return [ 'error' => '<!-- Bookaro: shortcode disabled for this content type. Use an allowed listing type/page or pass listing_id/provider_id explicitly. -->' ];
        }

        $default_provider_id = $this->get_default_provider_id();
        $provider_id = $explicit_provider_id;
        if ( ! $provider_id && $single_business && $default_provider_id > 0 ) {
            $provider_id = $default_provider_id;
        }

        $listing_id = $explicit_listing_id;
        $listing = null;
        if ( $listing_id ) {
            $listing = get_post( $listing_id );
            if ( ! $listing ) {
                return [ 'error' => '<!-- Bookaro: listing not found. -->' ];
            }
            if ( ! bookaro_is_allowed_listing( $listing_id ) ) {
                return [ 'error' => '<!-- Bookaro: invalid listing type. Open Setup Wizard > Listings and confirm allowed listing post types/pages. -->' ];
            }
        } elseif ( function_exists( 'get_the_ID' ) ) {
            $candidate_id = (int) get_the_ID();
            if (
                $candidate_id > 0
                && bookaro_is_allowed_listing( $candidate_id )
                && ( ! $single_business || $provider_id < 1 )
            ) {
                $candidate = get_post( $candidate_id );
                if ( $candidate ) {
                    $listing_id = $candidate_id;
                    $listing = $candidate;
                }
            }
        }

        if ( ! $provider_id && $listing_id ) {
            $provider_id = function_exists( 'bookaro_get_listing_owner_id' )
                ? (int) bookaro_get_listing_owner_id( $listing_id )
                : 0;
        }
        if ( ! $provider_id && $default_provider_id > 0 ) {
            $provider_id = $default_provider_id;
        }
        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : 'auto';
        if (
            ! $listing_id
            && $provider_id > 0
            && 'generic' === $integration_mode
            && function_exists( 'bookaro_get_listing_id_for_provider' )
        ) {
            $resolved_listing_id = bookaro_get_listing_id_for_provider( $provider_id );
            if ( $resolved_listing_id > 0 && bookaro_is_allowed_listing( $resolved_listing_id ) ) {
                $listing_id = $resolved_listing_id;
                $listing = get_post( $listing_id );
            }
        }
        if ( ! $listing_id && $provider_id < 1 ) {
            if ( $single_business ) {
                return [
                    'setup_issue' => 'missing_default_provider',
                    'error' => '<!-- Bookaro: provider_id is required when listing_id is missing. Single Business listings are optional, but you must set Default Provider in Setup Wizard > Listings (or pass provider_id). -->',
                ];
            }

            $integration_mode_for_notice = function_exists( 'bookaro_get_integration_mode' )
                ? sanitize_key( (string) bookaro_get_integration_mode() )
                : 'auto';
            if ( 'generic' === $integration_mode_for_notice ) {
                return [
                    'setup_issue' => 'generic_missing_provider_context',
                    'error' => '<!-- Bookaro: provider_id is required when listing_id is missing. Pass listing_id/provider_id or complete provider mapping in Setup Wizard > Listings. -->',
                ];
            }
            return [
                'error' => '<!-- Bookaro: provider_id is required when listing_id is missing. Pass listing_id/provider_id or complete provider mapping in Setup Wizard > Listings. -->',
            ];
        }

        $service_id = $explicit_service_id;
        $service_label = sanitize_text_field( $atts['service_label'] ?: $atts['label'] );
        $price = $atts['price'] !== '' ? (float) $atts['price'] : 0.0;
        $duration_raw = $atts['duration'] !== '' ? $atts['duration'] : $atts['service_duration'];
        $service_duration = $duration_raw !== '' ? absint( $duration_raw ) : 0;
        $services = [];
        $price_formatted = function_exists( 'bookaro_format_price' )
            ? bookaro_format_price( $price )
            : '';

        if ( $service_id ) {
            $service_post = get_post( $service_id );
            if (
                $service_post &&
                $service_post->post_type === BOOKARO_SERVICE_POST_TYPE &&
                'publish' === $service_post->post_status
            ) {
                $service_listing_id = (int) get_post_meta( $service_id, BOOKARO_SERVICE_LISTING_META, true );
                $service_provider_id = (int) get_post_meta( $service_id, BOOKARO_SERVICE_PROVIDER_META, true );
                $service_matches_context = false;
                if ( $listing_id > 0 ) {
                    $service_matches_context = ( $service_listing_id === $listing_id || 0 === $service_listing_id );
                } elseif ( $provider_id > 0 ) {
                    if ( $service_provider_id > 0 ) {
                        $service_matches_context = ( $service_provider_id === $provider_id );
                    } elseif ( $service_listing_id > 0 ) {
                        $service_listing_provider_id = function_exists( 'bookaro_get_listing_owner_id' )
                            ? (int) bookaro_get_listing_owner_id( $service_listing_id )
                            : 0;
                        if ( $service_listing_provider_id < 1 ) {
                            $service_listing_post = get_post( $service_listing_id );
                            if ( $service_listing_post ) {
                                $service_listing_provider_id = (int) $service_listing_post->post_author;
                            }
                        }
                        $service_matches_context = ( $service_listing_provider_id > 0 && $service_listing_provider_id === $provider_id );
                    } else {
                        $service_matches_context = true;
                    }
                }

                if ( $service_matches_context ) {
                    $service_label = sanitize_text_field(
                        function_exists( 'bookaro_get_service_label' )
                            ? bookaro_get_service_label( $service_post )
                            : $service_post->post_title
                    );
                    $price = (float) get_post_meta( $service_id, BOOKARO_SERVICE_PRICE_META, true );
                    $price_formatted = function_exists( 'bookaro_format_price' )
                        ? bookaro_format_price( $price )
                        : '';
                    $service_duration = function_exists( 'bookaro_get_service_duration_minutes' )
                        ? bookaro_get_service_duration_minutes( $service_id )
                        : (int) get_post_meta( $service_id, BOOKARO_SERVICE_DURATION_META, true );
                    $services[] = [
                        'id'          => $service_id,
                        'title'       => function_exists( 'bookaro_get_service_label' )
                            ? bookaro_get_service_label( $service_post )
                            : $service_post->post_title,
                        'description' => function_exists( 'bookaro_get_service_description' )
                            ? bookaro_get_service_description( $service_post )
                            : $service_post->post_content,
                        'price'       => $price,
                        'price_formatted' => $price_formatted,
                        'duration'    => function_exists( 'bookaro_get_service_duration_minutes' )
                            ? bookaro_get_service_duration_minutes( $service_id )
                            : (int) get_post_meta( $service_id, BOOKARO_SERVICE_DURATION_META, true ),
                        'listing_id'  => $listing_id,
                    ];
                } else {
                    $service_id = 0;
                }
            } else {
                $service_id = 0;
            }
        }

        return [
            'listing_id'    => $listing_id,
            'provider_id'   => $provider_id,
            'service_id'    => $service_id,
            'has_explicit_context' => $has_explicit_context,
            'service_label' => $service_label,
            'listing_price' => $price,
            'listing_price_formatted' => $price_formatted,
            'listing_title' => $listing_id ? get_the_title( $listing_id ) : '',
            'services'      => $services,
            'button_label'  => sanitize_text_field( $atts['button_label'] ?: $atts['button_text'] ),
            'button_class'  => sanitize_text_field( $atts['class'] ),
            'service_duration' => $service_duration,
        ];
    }

    private function get_default_provider_id(): int
    {
        if ( ! function_exists( 'bookaro_get_option' ) ) {
            return 0;
        }

        $default_provider_id = absint( bookaro_get_option( 'default_provider_id', 0 ) );
        if ( $default_provider_id < 1 ) {
            return 0;
        }

        return get_user_by( 'ID', $default_provider_id ) ? $default_provider_id : 0;
    }

    private function is_single_business_site(): bool
    {
        return function_exists( 'bookaro_is_single_business_site' ) && bookaro_is_single_business_site();
    }

    private function resolve_generic_provider_context_services( int $provider_id ): array
    {
        if ( function_exists( 'bookaro_get_generic_provider_context_services' ) ) {
            $services = bookaro_get_generic_provider_context_services( $provider_id );
            return is_array( $services ) ? $services : [];
        }

        return [];
    }

    private function resolve_booking_services_for_context( array $context, string $integration_mode = '' ): array
    {
        $integration_mode = sanitize_key( $integration_mode );
        if ( '' === $integration_mode && function_exists( 'bookaro_get_integration_mode' ) ) {
            $integration_mode = sanitize_key( (string) bookaro_get_integration_mode() );
        }
        if ( '' === $integration_mode ) {
            $integration_mode = 'auto';
        }

        $services = $context['services'] ?? [];
        if (
            empty( $services )
            && ! empty( $context['listing_id'] )
            && 'generic' === $integration_mode
            && function_exists( 'bookaro_get_services_catalog_for_listing' )
        ) {
            $services = bookaro_get_services_catalog_for_listing( (int) $context['listing_id'] );
        } elseif (
            empty( $services )
            && 'generic' === $integration_mode
            && $this->is_single_business_site()
            && ! empty( $context['provider_id'] )
        ) {
            $services = $this->resolve_generic_provider_context_services( (int) $context['provider_id'] );
        }

        return is_array( $services ) ? $services : [];
    }

    private function should_render_single_business_missing_booking_page_notice( array $context, string $integration_mode ): bool
    {
        if ( ! $this->is_single_business_site() || 'generic' !== sanitize_key( $integration_mode ) ) {
            return false;
        }

        if ( absint( $context['provider_id'] ?? 0 ) < 1 ) {
            return false;
        }

        if ( ! empty( $context['has_explicit_context'] ) ) {
            return false;
        }

        return ! $this->single_business_booking_page_assigned();
    }

    private function should_render_single_business_missing_services_notice( array $context, string $integration_mode ): bool
    {
        if ( ! $this->is_single_business_site() || 'generic' !== sanitize_key( $integration_mode ) ) {
            return false;
        }

        if ( absint( $context['provider_id'] ?? 0 ) < 1 ) {
            return false;
        }

        if ( ! empty( $context['services'] ) ) {
            return false;
        }

        $has_explicit_service_context = absint( $context['service_id'] ?? 0 ) > 0
            || '' !== trim( (string) ( $context['service_label'] ?? '' ) )
            || absint( $context['service_duration'] ?? 0 ) > 0
            || 0.0 !== (float) ( $context['listing_price'] ?? 0 );

        return ! $has_explicit_service_context;
    }

    private function single_business_booking_page_assigned(): bool
    {
        if ( ! function_exists( 'bookaro_get_option' ) ) {
            return false;
        }

        return (
            absint( bookaro_get_option( 'generic_setup_booking_page_id', 0 ) ) > 0
            || absint( bookaro_get_option( 'booking_page_id', 0 ) ) > 0
            || absint( bookaro_get_option( 'generic_setup_services_page_id', 0 ) ) > 0
        );
    }

    private function localize_booking_data( array $context ): void
    {
        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : 'auto';
        $mylisting_active = (bool) apply_filters( 'bookaro_feature_enabled', false, 'mylisting' );
        if (
            $mylisting_active
            && 'mylisting' === $integration_mode
            && function_exists( 'bookaro_is_listing_singular' )
            && bookaro_is_listing_singular()
        ) {
            return;
        }
        if ( ! wp_script_is( 'bookaro-booking-script', 'enqueued' ) ) {
            return;
        }

        $is_pro = function_exists( 'bookaro_is_pro_active' ) && bookaro_is_pro_active();
        $features = [
            'mylisting' => (bool) apply_filters( 'bookaro_feature_enabled', false, 'mylisting' ),
        ];
        $features = apply_filters( 'bookaro_frontend_features', $features, $is_pro );

        $user = wp_get_current_user();
        $dashboard_url = '';
        if ( is_user_logged_in() && bookaro_user_has_dashboard_access( $user->ID ) ) {
            $dashboard_url = function_exists( 'bookaro_get_dashboard_url' )
                ? bookaro_get_dashboard_url( [ 'listing_id' => (int) $context['listing_id'] ] )
                : site_url( '/my-account/bookings-and-management/' );
        }

        $services = $this->resolve_booking_services_for_context( $context, (string) $integration_mode );
        $services_source = ! empty( $services ) ? 'bookaro' : '';

        $data = [
                'ajax_url'     => admin_url( 'admin-ajax.php' ),
                'ajax_nonce'   => wp_create_nonce( 'bookaro_nonce' ),
                'rest_url'     => esc_url_raw( rest_url() ),
                'rest_nonce'   => wp_create_nonce( 'wp_rest' ),
                'listing_id'   => (int) $context['listing_id'],
                'provider_id'  => (int) $context['provider_id'],
                'current_user_id' => get_current_user_id(),
                'is_logged_in' => is_user_logged_in(),
                'login_url'    => function_exists( 'bookaro_get_login_url' )
                    ? bookaro_get_login_url( get_permalink() )
                    : wp_login_url( get_permalink() ),
                'user'         => is_user_logged_in()
                    ? [
                        'name'  => trim( $user->first_name . ' ' . $user->last_name ) ?: $user->display_name,
                        'email' => $user->user_email,
                        'phone' => get_user_meta( $user->ID, 'billing_phone', true ),
                    ]
                    : null,
                'listing_title' => $context['listing_title'],
                'listing_price' => (float) $context['listing_price'],
                'listing_price_formatted' => $context['listing_price_formatted'] ?? '',
                'service_label' => $context['service_label'],
                'service_duration' => (int) ( $context['service_duration'] ?? 0 ),
                'services'      => $services,
                'services_source' => $services_source,
                'services_endpoint' => '',
                'dashboard_url' => $dashboard_url,
                'has_timeslots' => true,
                'service_durations' => [],
                'currency'    => function_exists( 'bookaro_get_currency_settings' )
                    ? bookaro_get_currency_settings()
                    : [],
                'is_pro'      => $is_pro,
                'features'    => $features,
                'i18n'        => I18n::all(),
        ];

        $data = apply_filters( 'bookaro_booking_data', $data, $context, $integration_mode );

        wp_localize_script(
            'bookaro-booking-script',
            'BookaroData',
            $data
        );
    }

    private function render_booking_container(): string
    {
        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? bookaro_get_integration_mode()
            : 'auto';
        $mylisting_active = (bool) apply_filters( 'bookaro_feature_enabled', false, 'mylisting' );
        if (
            $mylisting_active
            && 'mylisting' === $integration_mode
            && function_exists( 'bookaro_is_listing_singular' )
            && bookaro_is_listing_singular()
        ) {
            return '<!-- Bookaro: booking shortcodes are disabled on listing pages when MyListing mode is active. -->';
        }
        if ( self::$booking_container_rendered || ! empty( $GLOBALS['bookaro_booking_app_rendered'] ) ) {
            return '';
        }
        self::$booking_container_rendered = true;
        $GLOBALS['bookaro_booking_app_rendered'] = true;
        return '<div id="bookaro-booking-app" class="bookaro-scope"></div>';
    }

    private function shortcodes_enabled(): bool
    {
        return ( 'shortcodes' === bookaro_get_ui_mode() );
    }

    private function ensure_frontend_assets(): void
    {
        $assets = AssetsController::instance();
        if ( ! $assets ) {
            return;
        }
        $assets->ensure_frontend_assets();
    }

    private function requires_login(): bool
    {
        return (bool) bookaro_get_option( 'require_login', 1 );
    }

    private function render_login_prompt(): string
    {
        $login_url = function_exists( 'bookaro_get_login_url' )
            ? bookaro_get_login_url( get_permalink() )
            : wp_login_url( get_permalink() );

        return sprintf(
            '<div class="bookaro-login-required">%s</div>',
            sprintf(
                '<a href="%s">%s</a>',
                esc_url( $login_url ),
                esc_html__( 'Please log in to continue.', 'mybookaro' )
            )
        );
    }

    private function render_permission_denied(): string
    {
        return sprintf(
            '<div class="bookaro-permission-denied">%s</div>',
            esc_html__( 'You do not have permission to access this dashboard.', 'mybookaro' )
        );
    }

    private function render_dashboard_page_assignment_notice( string $context ): string
    {
        if ( ! $this->should_show_dashboard_page_assignment_notice( $context ) ) {
            return '';
        }

        $i18n = I18n::all();
        $is_admin_user = current_user_can( 'manage_options' );
        $title = sanitize_text_field(
            (string) ( $i18n['empty_no_dashboard_pages_title'] ?? __( 'Dashboard pages are not assigned', 'mybookaro' ) )
        );
        $dashboard_label = ( 'customer_dashboard' === $context )
            ? __( 'Customer dashboard page', 'mybookaro' )
            : __( 'Provider dashboard page', 'mybookaro' );
        $description = $is_admin_user
            ? sanitize_text_field(
                /* translators: %s: dynamic string */
                (string) ( $i18n['empty_no_dashboard_pages_desc'] ?? sprintf( __( '%s is not assigned yet. Assign dashboard pages in Setup Wizard so dashboard links can route users correctly.', 'mybookaro' ), $dashboard_label ) )
            )
            : sanitize_text_field(
                (string) ( $i18n['empty_no_dashboard_pages_desc_non_admin'] ?? __( 'Dashboard pages are not assigned yet. Ask the site admin to finish setup.', 'mybookaro' ) )
            );
        if ( $is_admin_user ) {
            $description = trim(
                $description . ' ' . __( 'Next step: Open Setup Wizard > Pages, assign Provider and Customer dashboard pages, then save.', 'mybookaro' )
            );
        }

        $action_html = '';
        if ( $is_admin_user ) {
            $setup_url = admin_url( 'admin.php?page=bookaro-setup-wizard&step=pages' );
            $action_html = sprintf(
                '<a href="%1$s" class="bookaro-btn bookaro-btn--outline bookaro-btn--sm mt-2">%2$s</a>',
                esc_url( $setup_url ),
                esc_html(
                    sanitize_text_field(
                        (string) ( $i18n['empty_action_assign_dashboard_pages'] ?? __( 'Open pages step', 'mybookaro' ) )
                    )
                )
            );
        } else {
            $admin_email = sanitize_email( (string) get_option( 'admin_email', '' ) );
            if ( '' !== $admin_email ) {
                $action_html = sprintf(
                    '<a href="%1$s" class="bookaro-btn bookaro-btn--outline bookaro-btn--sm mt-2">%2$s</a>',
                    esc_url( 'mailto:' . antispambot( $admin_email ) ),
                    esc_html(
                        sanitize_text_field(
                            (string) ( $i18n['empty_action_contact_admin'] ?? __( 'Contact site admin', 'mybookaro' ) )
                        )
                    )
                );
            }
        }

        return sprintf(
            '<div class="bookaro-card bookaro-card__body mb-4"><div class="rounded-lg border border-amber-200 bg-amber-50 p-3"><h3 class="text-sm font-semibold text-amber-900">%1$s</h3><p class="mt-1 text-sm text-amber-800">%2$s</p>%3$s</div></div>',
            esc_html( $title ),
            esc_html( $description ),
            $action_html
        );
    }

    private function should_show_dashboard_page_assignment_notice( string $context ): bool
    {
        $context = sanitize_key( $context );
        if ( ! in_array( $context, [ 'provider_dashboard', 'customer_dashboard' ], true ) ) {
            return false;
        }

        if ( ! $this->shortcodes_enabled() ) {
            return false;
        }

        $integration_mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : 'auto';
        if ( 'generic' !== $integration_mode ) {
            return false;
        }

        return ! $this->has_dashboard_page_assignment( $context );
    }

    private function has_dashboard_page_assignment( string $context ): bool
    {
        $context = sanitize_key( $context );
        if ( ! in_array( $context, [ 'provider_dashboard', 'customer_dashboard' ], true ) ) {
            return false;
        }

        $mode = function_exists( 'bookaro_get_integration_mode' )
            ? sanitize_key( (string) bookaro_get_integration_mode() )
            : 'auto';
        if ( function_exists( 'bookaro_get_allowed_listing_post_types' ) && function_exists( 'bookaro_get_page_for_context' ) ) {
            $post_types = bookaro_get_allowed_listing_post_types();
            if ( is_array( $post_types ) ) {
                foreach ( $post_types as $post_type ) {
                    $post_type = sanitize_key( (string) $post_type );
                    if ( '' === $post_type ) {
                        continue;
                    }
                    if ( absint( bookaro_get_page_for_context( $mode, $post_type, $context ) ) > 0 ) {
                        return true;
                    }
                }
            }
        }

        $settings = get_option( 'bookaro_settings', [] );
        if ( ! is_array( $settings ) ) {
            return false;
        }

        $keys_by_context = [
            'provider_dashboard' => [ 'provider_dashboard_page_id', 'generic_setup_provider_dashboard_page_id' ],
            'customer_dashboard' => [ 'customer_dashboard_page_id', 'generic_setup_customer_dashboard_page_id' ],
        ];
        foreach ( $keys_by_context[ $context ] ?? [] as $settings_key ) {
            if ( absint( $settings[ $settings_key ] ?? 0 ) > 0 ) {
                return true;
            }
        }

        $overrides = $settings['listing_post_type_pages'] ?? [];
        if ( ! is_array( $overrides ) ) {
            return false;
        }
        foreach ( $overrides as $row ) {
            if ( ! is_array( $row ) ) {
                continue;
            }
            if ( absint( $row[ $context ] ?? 0 ) > 0 ) {
                return true;
            }
        }

        return false;
    }

}
