# thinkrank/1.0.2/includes/frontend/class-seo-manager.php

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.0.2. 1,766 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.0.2/code/includes/frontend/class-seo-manager.php
- Raw: https://pluginprobe.com/plugins/thinkrank/1.0.2/raw/includes/frontend/class-seo-manager.php
- Modified: 2025-09-25T11:58:06+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.0.2/code/includes/frontend/class-seo-manager.php#L10-L20`.

```php
<?php
/**
 * Frontend SEO Manager Class
 * 
 * Handles frontend SEO meta tag output and WordPress integration
 * 
 * @package ThinkRank\Frontend
 * @since 1.0.0
 */

declare(strict_types=1);

namespace ThinkRank\Frontend;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Frontend SEO Manager Class
 * 
 * Single Responsibility: Output SEO meta tags and integrate with WordPress SEO
 * 
 * @since 1.0.0
 */
class SEO_Manager {

    /**
     * Current post ID
     *
     * @var int|null
     */
    private ?int $current_post_id = null;

    /**
     * Current post metadata
     *
     * @var array
     */
    private array $current_metadata = [];

    /**
     * Site Identity Manager instance
     *
     * @var \ThinkRank\SEO\Site_Identity_Manager|null
     */
    private ?\ThinkRank\SEO\Site_Identity_Manager $site_identity_manager = null;

    /**
     * Social Meta Manager instance
     *
     * @var \ThinkRank\SEO\Social_Meta_Manager|null
     */
    private ?\ThinkRank\SEO\Social_Meta_Manager $social_manager = null;

    /**
     * Schema Management System instance
     *
     * @var \ThinkRank\SEO\Schema_Management_System|null
     */
    private ?\ThinkRank\SEO\Schema_Management_System $schema_manager = null;

    /**
     * Site identity data cache
     *
     * @var array|null
     */
    private ?array $site_identity_data = null;

    /**
     * Current page context
     *
     * @var string
     */
    private string $current_context = 'site';
    
    /**
     * Initialize SEO manager
     *
     * @return void
     */
    public function init(): void {
        // Initialize Site Identity Manager
        $this->initialize_site_identity_manager();

        // Initialize Social Meta Manager
        $this->initialize_social_meta_manager();

        // Initialize Schema Manager for enhanced schema output
        $this->initialize_schema_manager();

        // Initialize Google Analytics Tracking Manager
        $this->initialize_google_analytics_tracking();

        // Initialize current post and context data first
        add_action('wp', [$this, 'initialize_current_context']);

        // Use HIGH PRIORITY hooks to override other SEO plugins
        // Priority 1-5 ensures ThinkRank runs before other SEO plugins

        // Override WordPress title with HIGH priority
        add_filter('pre_get_document_title', [$this, 'override_document_title'], 1);
        add_filter('wp_title', [$this, 'override_wp_title'], 1, 2);

        // Output meta tags with HIGH priority
        add_action('wp_head', [$this, 'output_meta_description'], 1);
        add_action('wp_head', [$this, 'output_seo_meta_tags'], 2);
        add_action('wp_head', [$this, 'output_open_graph_tags'], 3);
        add_action('wp_head', [$this, 'output_twitter_card_tags'], 4);
        add_action('wp_head', [$this, 'output_platform_meta_tags'], 5);
        add_action('wp_head', [$this, 'output_canonical_url'], 6);

        // Add Site Identity specific outputs
        add_action('wp_head', [$this, 'output_site_schema_markup'], 7);
        add_action('wp_head', [$this, 'output_breadcrumb_schema'], 8);

        // Add closing comment (runs last)
        add_action('wp_head', [$this, 'output_closing_comment'], 99);

        // Add breadcrumb display hook
        add_action('thinkrank_breadcrumbs', [$this, 'display_breadcrumbs']);

        // Add robots.txt filter hook
        add_filter('robots_txt', [$this, 'filter_robots_txt'], 10, 2);
    }

    /**
     * Initialize Site Identity Manager
     *
     * @return void
     */
    private function initialize_site_identity_manager(): void {
        if (!class_exists('ThinkRank\\SEO\\Site_Identity_Manager')) {
            require_once THINKRANK_PLUGIN_DIR . 'includes/seo/class-site-identity-manager.php';
        }

        $this->site_identity_manager = new \ThinkRank\SEO\Site_Identity_Manager();
    }

    /**
     * Initialize Social Meta Manager
     *
     * @return void
     */
    private function initialize_social_meta_manager(): void {
        if (!class_exists('ThinkRank\\SEO\\Social_Meta_Manager')) {
            require_once THINKRANK_PLUGIN_DIR . 'includes/seo/class-social-meta-manager.php';
        }

        $this->social_manager = new \ThinkRank\SEO\Social_Meta_Manager();
    }

    /**
     * Initialize Schema Manager for enhanced schema output
     *
     * @return void
     */
    private function initialize_schema_manager(): void {
        if (!class_exists('ThinkRank\\SEO\\Schema_Management_System')) {
            require_once THINKRANK_PLUGIN_DIR . 'includes/seo/class-schema-management-system.php';
        }

        // Initialize Schema Manager and store reference for integration
        $this->schema_manager = new \ThinkRank\SEO\Schema_Management_System();
    }

    /**
     * Initialize Google Analytics Tracking Manager
     *
     * @return void
     */
    private function initialize_google_analytics_tracking(): void {
        if (!class_exists('ThinkRank\\Frontend\\Google_Analytics_Tracking_Manager')) {
            require_once THINKRANK_PLUGIN_DIR . 'includes/frontend/class-google-analytics-tracking-manager.php';
        }

        // Initialize Google Analytics Tracking Manager
        new \ThinkRank\Frontend\Google_Analytics_Tracking_Manager();
    }

    /**
     * Initialize current context and post data
     *
     * @return void
     */
    public function initialize_current_context(): void {
        // Determine current context
        $this->current_context = $this->detect_current_context();

        // Initialize post data if singular
        if (is_singular()) {
            $post_id = get_the_ID();
            if ($post_id) {
                $this->current_post_id = $post_id;
                $this->current_metadata = $this->get_post_seo_metadata($post_id);
            }
        }

        // Load site identity data
        $this->load_site_identity_data();
    }

    /**
     * Detect current page context
     *
     * @return string Current context type
     */
    private function detect_current_context(): string {
        if (is_home() || is_front_page()) {
            return 'homepage';
        } elseif (is_single()) {
            return 'post';
        } elseif (is_page()) {
            return 'page';
        } elseif (is_category()) {
            return 'category';
        } elseif (is_tag()) {
            return 'tag';
        } elseif (is_author()) {
            return 'author';
        } elseif (is_search()) {
            return 'search';
        } elseif (is_archive()) {
            return 'archive';
        }

        return 'site';
    }

    /**
     * Load site identity data
     *
     * @return void
     */
    private function load_site_identity_data(): void {
        if ($this->site_identity_manager && $this->site_identity_data === null) {
            $this->site_identity_data = $this->site_identity_manager->get_output_data('site', null);
        }
    }
    
    /**
     * Get SEO metadata for a post
     * 
     * @param int $post_id Post ID
     * @return array SEO metadata
     */
    private function get_post_seo_metadata(int $post_id): array {
        return [
            'title' => get_post_meta($post_id, '_thinkrank_seo_title', true),
            'description' => get_post_meta($post_id, '_thinkrank_meta_description', true),
            'focus_keyword' => get_post_meta($post_id, '_thinkrank_focus_keyword', true),
            'seo_score' => get_post_meta($post_id, '_thinkrank_seo_score', true),
        ];
    }
    
    /**
     * Override WordPress document title (HIGH PRIORITY)
     * Post-specific metadata takes priority over Site Identity templates
     *
     * @param string $title Original title
     * @return string Modified title
     */
    public function override_document_title($title): string {
        // First priority: Post-specific ThinkRank metadata
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['title'])) {
            return $this->current_metadata['title'];
        }

        // Second priority: Site Identity templates
        $generated_title = $this->generate_context_title();
        if ($generated_title) {
            return $generated_title;
        }

        return $title;
    }
    
    /**
     * Override WordPress wp_title (HIGH PRIORITY)
     * Post-specific metadata takes priority over Site Identity templates
     *
     * @param string $title Original title
     * @param string $sep Title separator
     * @return string Modified title
     */
    public function override_wp_title(string $title, string $sep = ''): string {
        // First priority: Post-specific ThinkRank metadata
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['title'])) {
            $site_name = get_bloginfo('name');
            return $this->current_metadata['title'] . ($sep ? " $sep " : ' | ') . $site_name;
        }

        // Second priority: Site Identity templates
        $generated_title = $this->generate_context_title();
        if ($generated_title) {
            return $generated_title;
        }

        return $title;
    }
    
    /**
     * Output meta description (HIGH PRIORITY)
     * Uses post-specific metadata or Site Identity default fallback
     *
     * @return void
     */
    public function output_meta_description(): void {
        $description = $this->get_meta_description();

        if ($description) {
            // Output main ThinkRank SEO header comment (only once)
            static $header_output = false;
            if (!$header_output) {
                echo "<!-- Search Engine Optimization by ThinkRank - https://thinkrank.ai/ -->\n";
                $header_output = true;
            }

            // Ensure description is within optimal length (150-160 characters)
            if (strlen($description) > 160) {
                $description = wp_trim_words($description, 25, '...');
            }

            echo "<!-- ThinkRank SEO Meta Description -->\n";
            echo '<meta name="description" content="' . esc_attr($description) . '" />' . "\n";
            echo "<!-- /ThinkRank SEO Meta Description -->\n";
        }
    }
    
    /**
     * Output SEO meta tags
     *
     * @return void
     */
    public function output_seo_meta_tags(): void {
        echo "<!-- ThinkRank SEO Meta Tags -->\n";

        // Output robots meta tag with proper directives
        $robots_content = $this->get_robots_meta_content();
        echo '<meta name="robots" content="' . esc_attr($robots_content) . '" />' . "\n";

        // Output focus keyword as meta keywords (if available and not empty)
        if (!empty($this->current_metadata['focus_keyword'])) {
            $keywords = trim($this->current_metadata['focus_keyword']);
            if (!empty($keywords)) {
                echo '<meta name="keywords" content="' . esc_attr($keywords) . '" />' . "\n";
            }
        }

        // Output local SEO meta tags if business info is available
        $this->output_local_seo_meta_tags();

        // Output generator meta tag
        echo '<meta name="generator" content="ThinkRank ' . esc_attr(THINKRANK_VERSION) . '" />' . "\n";

        // Output viewport meta tag if not already present
        if (!has_action('wp_head', 'wp_site_icon') || !wp_is_mobile()) {
            echo '<meta name="viewport" content="width=device-width, initial-scale=1.0" />' . "\n";
        }
        echo "<!-- /ThinkRank SEO Meta Tags -->\n";
    }

    /**
     * Get robots meta content based on context and settings
     *
     * @return string Robots meta content
     */
    private function get_robots_meta_content(): string {
        $robots = [];

        // Default directives
        $robots[] = 'index';
        $robots[] = 'follow';

        // Add advanced directives for better SEO
        $robots[] = 'max-snippet:-1';
        $robots[] = 'max-image-preview:large';
        $robots[] = 'max-video-preview:-1';

        // Check for specific page settings
        if (is_singular()) {
            // Check if post has noindex setting
            $noindex = get_post_meta(get_the_ID(), '_thinkrank_noindex', true);
            if ($noindex) {
                $robots = ['noindex', 'nofollow'];
            }
        }

        // Check for archive pages
        if (is_archive() || is_search()) {
            // Allow indexing of category/tag archives but be more conservative
            if (is_paged()) {
                $robots = ['noindex', 'follow'];
            }
        }

        // Apply filters for customization
        $robots = apply_filters('thinkrank_robots_meta', $robots);

        return implode(', ', $robots);
    }

    /**
     * Output local SEO meta tags for business information
     *
     * @return void
     */
    private function output_local_seo_meta_tags(): void {
        if (!$this->site_identity_manager) {
            return;
        }

        $settings = $this->site_identity_manager->get_settings('site');

        // Only output if local SEO is enabled and business info is available
        if (empty($settings['local_seo_enabled']) || empty($settings['business_name'])) {
            return;
        }

        echo "<!-- ThinkRank Local SEO Meta Tags -->\n";

        // NAP (Name, Address, Phone) Consistency Meta Tags
        if (!empty($settings['business_name'])) {
            echo '<meta name="business:name" content="' . esc_attr($settings['business_name']) . '" />' . "\n";
        }

        // Business address components
        if (!empty($settings['business_address'])) {
            echo '<meta name="business:contact_data:street_address" content="' . esc_attr($settings['business_address']) . '" />' . "\n";
        }

        if (!empty($settings['business_city'])) {
            echo '<meta name="business:contact_data:locality" content="' . esc_attr($settings['business_city']) . '" />' . "\n";
            echo '<meta name="geo.placename" content="' . esc_attr($settings['business_city']) . '" />' . "\n";
        }

        if (!empty($settings['business_state'])) {
            echo '<meta name="business:contact_data:region" content="' . esc_attr($settings['business_state']) . '" />' . "\n";
        }

        if (!empty($settings['business_postal_code'])) {
            echo '<meta name="business:contact_data:postal_code" content="' . esc_attr($settings['business_postal_code']) . '" />' . "\n";
        }

        if (!empty($settings['business_country'])) {
            echo '<meta name="business:contact_data:country_name" content="' . esc_attr($settings['business_country']) . '" />' . "\n";
        }

        // Phone number
        if (!empty($settings['business_phone'])) {
            echo '<meta name="business:contact_data:phone_number" content="' . esc_attr($settings['business_phone']) . '" />' . "\n";
        }

        // Email address
        if (!empty($settings['business_email'])) {
            echo '<meta name="business:contact_data:email" content="' . esc_attr($settings['business_email']) . '" />' . "\n";
        }

        // Geo-location meta tags (if coordinates are available)
        if (!empty($settings['business_latitude']) && !empty($settings['business_longitude'])) {
            $coordinates = $settings['business_latitude'] . ';' . $settings['business_longitude'];
            echo '<meta name="geo.position" content="' . esc_attr($coordinates) . '" />' . "\n";
            echo '<meta name="ICBM" content="' . esc_attr($settings['business_latitude'] . ', ' . $settings['business_longitude']) . '" />' . "\n";
        }

        // Regional meta tag (state/country combination)
        if (!empty($settings['business_state']) && !empty($settings['business_country'])) {
            $region = strtoupper($settings['business_country']) . '-' . strtoupper($settings['business_state']);
            echo '<meta name="geo.region" content="' . esc_attr($region) . '" />' . "\n";
        }

        // Business hours in structured format
        if (!empty($settings['business_hours']) && is_array($settings['business_hours'])) {
            $formatted_hours = $this->format_business_hours_for_meta($settings['business_hours']);
            if (!empty($formatted_hours)) {
                echo '<meta name="business:hours" content="' . esc_attr($formatted_hours) . '" />' . "\n";
            }
        }

        // Business type
        if (!empty($settings['business_type'])) {
            echo '<meta name="business:type" content="' . esc_attr($settings['business_type']) . '" />' . "\n";
        }

        echo "<!-- /ThinkRank Local SEO Meta Tags -->\n";
    }

    /**
     * Format business hours for meta tag output
     *
     * @param array $business_hours Business hours array
     * @return string Formatted hours string
     */
    private function format_business_hours_for_meta(array $business_hours): string {
        $formatted_days = [];

        $day_abbreviations = [
            'monday' => 'Mo',
            'tuesday' => 'Tu',
            'wednesday' => 'We',
            'thursday' => 'Th',
            'friday' => 'Fr',
            'saturday' => 'Sa',
            'sunday' => 'Su'
        ];

        foreach ($day_abbreviations as $day => $abbrev) {
            if (isset($business_hours[$day]) && !empty($business_hours[$day])) {
                $day_data = $business_hours[$day];

                if (!empty($day_data['closed']) || empty($day_data['open']) || empty($day_data['close'])) {
                    continue; // Skip closed days
                }

                $formatted_days[] = $abbrev . ' ' . $day_data['open'] . '-' . $day_data['close'];
            }
        }

        return implode(', ', $formatted_days);
    }

    /**
     * Output social media Open Graph tags from Social Meta Manager
     *
     * @param array $og_tags Open Graph tags array
     * @return void
     */
    private function output_social_og_tags(array $og_tags): void {
        echo "<!-- ThinkRank SEO Open Graph Tags (Enhanced) -->\n";

        // Define optimal order for Open Graph tags
        $og_order = [
            'og:title', 'og:description', 'og:type', 'og:url', 'og:site_name', 'og:locale',
            'og:image', 'og:image:width', 'og:image:height', 'og:image:type', 'og:image:alt',
            'article:published_time', 'article:modified_time', 'article:author', 'article:section'
        ];

        // Output tags in optimal order
        foreach ($og_order as $property) {
            if (!empty($og_tags[$property])) {
                echo '<meta property="' . esc_attr($property) . '" content="' . esc_attr($og_tags[$property]) . '" />' . "\n";
            }
        }

        // Output any remaining tags not in the order list
        foreach ($og_tags as $property => $content) {
            if (!empty($content) && !in_array($property, $og_order, true)) {
                echo '<meta property="' . esc_attr($property) . '" content="' . esc_attr($content) . '" />' . "\n";
            }
        }

        echo "<!-- /ThinkRank SEO Open Graph Tags -->\n";
    }

    /**
     * Output social media Twitter Card tags from Social Meta Manager
     *
     * @param array $twitter_tags Twitter Card tags array
     * @return void
     */
    private function output_social_twitter_tags(array $twitter_tags): void {
        echo "<!-- ThinkRank SEO Twitter Card Tags (Enhanced) -->\n";

        // Define optimal order for Twitter Card tags
        $twitter_order = [
            'twitter:card', 'twitter:title', 'twitter:description', 'twitter:site', 'twitter:creator',
            'twitter:image', 'twitter:image:alt', 'twitter:player', 'twitter:player:width', 'twitter:player:height'
        ];

        // Output tags in optimal order
        foreach ($twitter_order as $name) {
            if (!empty($twitter_tags[$name])) {
                echo '<meta name="' . esc_attr($name) . '" content="' . esc_attr($twitter_tags[$name]) . '" />' . "\n";
            }
        }

        // Output any remaining tags not in the order list
        foreach ($twitter_tags as $name => $content) {
            if (!empty($content) && !in_array($name, $twitter_order, true)) {
                echo '<meta name="' . esc_attr($name) . '" content="' . esc_attr($content) . '" />' . "\n";
            }
        }

        echo "<!-- /ThinkRank SEO Twitter Card Tags -->\n";
    }

    /**
     * Output platform-specific meta tags
     *
     * @return void
     */
    public function output_platform_meta_tags(): void {
        // Try Social Meta Manager for platform tags
        if ($this->social_manager) {
            // Map context for Social Meta Manager (homepage -> site for site-wide settings)
            $social_context = $this->current_context === 'homepage' ? 'site' : $this->current_context;

            $social_data = $this->social_manager->get_output_data(
                $social_context,
                $this->current_post_id
            );

            if ($social_data['enabled'] && !empty($social_data['platform_tags'])) {
                $this->output_social_platform_tags($social_data['platform_tags']);
            }
        }
    }

    /**
     * Output social media platform tags from Social Meta Manager
     *
     * @param array $platform_tags Platform tags array
     * @return void
     */
    private function output_social_platform_tags(array $platform_tags): void {
        echo "<!-- ThinkRank SEO Platform Meta Tags -->\n";

        foreach ($platform_tags as $name => $content) {
            if (!empty($content)) {
                // Determine if it should be property or name attribute
                if (strpos($name, 'fb:') === 0) {
                    // Facebook tags use property attribute
                    echo '<meta property="' . esc_attr($name) . '" content="' . esc_attr($content) . '" />' . "\n";
                } else {
                    // Other platform tags use name attribute
                    echo '<meta name="' . esc_attr($name) . '" content="' . esc_attr($content) . '" />' . "\n";
                }
            }
        }

        echo "<!-- /ThinkRank SEO Platform Meta Tags -->\n";
    }

    /**
     * Output Open Graph meta tags (HIGH PRIORITY)
     * Uses Social Meta Manager with fallback to Site Identity templates
     *
     * @return void
     */
    public function output_open_graph_tags(): void {
        // Priority 1: Try Social Meta Manager (Social Media tab settings)
        if ($this->social_manager) {
            // Map context for Social Meta Manager (homepage -> site for site-wide settings)
            $social_context = $this->current_context === 'homepage' ? 'site' : $this->current_context;

            $social_data = $this->social_manager->get_output_data(
                $social_context,
                $this->current_post_id
            );

            if ($social_data['enabled'] && !empty($social_data['og_tags'])) {
                $this->output_social_og_tags($social_data['og_tags']);
                return;
            }
        }

        // Priority 2: Fallback to current implementation
        $this->output_basic_og_tags();
    }

    /**
     * Output basic Open Graph tags (fallback implementation)
     *
     * @return void
     */
    private function output_basic_og_tags(): void {
        // Get title using priority system: post-specific > Site Identity > default
        $title = '';
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['title'])) {
            $title = $this->current_metadata['title'];
        } else {
            $title = $this->generate_context_title();
        }
        if (!$title) {
            $title = is_singular() ? get_the_title() : get_bloginfo('name');
        }

        $description = $this->get_meta_description();
        if (!$description) {
            $description = is_singular() ? wp_trim_words(get_the_excerpt(), 30) : get_bloginfo('description');
        }

        $url = is_singular() ? get_permalink() : home_url();
        $site_name = $this->site_identity_data && !empty($this->site_identity_data['identity']['site_name'])
            ? $this->site_identity_data['identity']['site_name']
            : get_bloginfo('name');

        // Determine proper og:type based on context
        $og_type = 'website';
        if (is_singular('post')) {
            $og_type = 'article';
        } elseif (is_singular('page')) {
            $og_type = 'website';
        } elseif (is_home() || is_front_page()) {
            $og_type = 'website';
        }

        echo "<!-- ThinkRank SEO Open Graph Meta Tags -->\n";
        echo "<meta property=\"og:type\" content=\"" . esc_attr($og_type) . "\" />\n";
        echo "<meta property=\"og:title\" content=\"" . esc_attr($title) . "\" />\n";
        echo "<meta property=\"og:description\" content=\"" . esc_attr($description) . "\" />\n";
        echo "<meta property=\"og:url\" content=\"" . esc_url($url) . "\" />\n";
        echo "<meta property=\"og:site_name\" content=\"" . esc_attr($site_name) . "\" />\n";
        echo "<meta property=\"og:locale\" content=\"" . esc_attr(get_locale()) . "\" />\n";

        // Add featured image if available (only for singular posts/pages)
        if (is_singular() && $this->current_post_id) {
            if (has_post_thumbnail($this->current_post_id)) {
                $image_url = get_the_post_thumbnail_url($this->current_post_id, 'large');
                echo "<meta property=\"og:image\" content=\"" . esc_url($image_url) . "\" />\n";
                echo "<meta property=\"og:image:secure_url\" content=\"" . esc_url($image_url) . "\" />\n";

                // Get image dimensions and alt text
                $image_id = get_post_thumbnail_id($this->current_post_id);
                $image_meta = wp_get_attachment_metadata($image_id);
                if ($image_meta) {
                    echo "<meta property=\"og:image:width\" content=\"" . esc_attr($image_meta['width']) . "\" />\n";
                    echo "<meta property=\"og:image:height\" content=\"" . esc_attr($image_meta['height']) . "\" />\n";
                    echo "<meta property=\"og:image:type\" content=\"image/jpeg\" />\n";
                }

                // Add image alt text
                $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
                if ($image_alt) {
                    echo "<meta property=\"og:image:alt\" content=\"" . esc_attr($image_alt) . "\" />\n";
                }
            }

            // Add article specific tags for posts only
            if ($og_type === 'article') {
                echo '<meta property="article:published_time" content="' . esc_attr(get_the_date('c', $this->current_post_id)) . '" />' . "\n";
                echo '<meta property="article:modified_time" content="' . esc_attr(get_the_modified_date('c', $this->current_post_id)) . '" />' . "\n";

                // Add author
                $author_id = get_post_field('post_author', $this->current_post_id);
                $author_name = get_the_author_meta('display_name', $author_id);
                echo "<meta property=\"article:author\" content=\"" . esc_attr($author_name) . "\" />\n";

                // Add categories as article:section
                if (is_single()) {
                    $categories = get_the_category($this->current_post_id);
                    if (!empty($categories)) {
                        echo "<meta property=\"article:section\" content=\"" . esc_attr($categories[0]->name) . "\" />\n";
                    }
                }
            }
        }
        echo "<!-- /ThinkRank SEO Open Graph Meta Tags -->\n";
    }
    
    /**
     * Output Twitter Card meta tags (HIGH PRIORITY)
     * Uses Social Meta Manager with fallback to Site Identity templates
     *
     * @return void
     */
    public function output_twitter_card_tags(): void {
        // Priority 1: Try Social Meta Manager (Social Media tab settings)
        if ($this->social_manager) {
            // Map context for Social Meta Manager (homepage -> site for site-wide settings)
            $social_context = $this->current_context === 'homepage' ? 'site' : $this->current_context;

            $social_data = $this->social_manager->get_output_data(
                $social_context,
                $this->current_post_id
            );



            if ($social_data['enabled'] && !empty($social_data['twitter_tags'])) {
                $this->output_social_twitter_tags($social_data['twitter_tags']);
                return;
            }
        }

        // Priority 2: Fallback to current implementation
        $this->output_basic_twitter_tags();
    }

    /**
     * Output basic Twitter Card tags (fallback implementation)
     *
     * @return void
     */
    private function output_basic_twitter_tags(): void {
        // Get title using priority system: post-specific > Site Identity > default
        $title = '';
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['title'])) {
            $title = $this->current_metadata['title'];
        } else {
            $title = $this->generate_context_title();
        }
        if (!$title) {
            $title = is_singular() ? get_the_title() : get_bloginfo('name');
        }

        $description = $this->get_meta_description();
        if (!$description) {
            $description = is_singular() ? wp_trim_words(get_the_excerpt(), 30) : get_bloginfo('description');
        }

        // Determine card type based on image availability
        $card_type = 'summary';
        if (is_singular() && $this->current_post_id && has_post_thumbnail($this->current_post_id)) {
            $card_type = 'summary_large_image';
        }

        echo "<!-- ThinkRank SEO Twitter Card Meta Tags -->\n";
        echo '<meta name="twitter:card" content="' . esc_attr($card_type) . '" />' . "\n";
        echo "<meta name=\"twitter:title\" content=\"" . esc_attr($title) . "\" />\n";
        echo "<meta name=\"twitter:description\" content=\"" . esc_attr($description) . "\" />\n";

        // Add Twitter image with proper fallback priority
        $twitter_image_url = $this->get_twitter_image_with_fallback();
        if ($twitter_image_url) {
            echo "<meta name=\"twitter:image\" content=\"" . esc_url($twitter_image_url) . "\" />\n";

            // Add image alt text for accessibility (if it's a featured image)
            if (is_singular() && $this->current_post_id && has_post_thumbnail($this->current_post_id)) {
                $featured_image_url = get_the_post_thumbnail_url($this->current_post_id, 'large');
                if ($twitter_image_url === $featured_image_url) {
                    $image_id = get_post_thumbnail_id($this->current_post_id);
                    $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
                    if ($image_alt) {
                        echo "<meta name=\"twitter:image:alt\" content=\"" . esc_attr($image_alt) . "\" />\n";
                    }
                }
            }
        }

        // Add site Twitter handle if configured
        if ($this->site_identity_data && !empty($this->site_identity_data['social']['twitter_username'])) {
            $twitter_handle = $this->site_identity_data['social']['twitter_username'];
            // Ensure handle starts with @
            if (strpos($twitter_handle, '@') !== 0) {
                $twitter_handle = '@' . $twitter_handle;
            }
            echo "<meta name=\"twitter:site\" content=\"" . esc_attr($twitter_handle) . "\" />\n";
        }
        echo "<!-- /ThinkRank SEO Twitter Card Meta Tags -->\n";
    }
    
    /**
     * Output canonical URL
     *
     * @return void
     */
    public function output_canonical_url(): void {
        if (!is_singular()) {
            return;
        }

        $canonical_url = $this->current_post_id ? get_permalink($this->current_post_id) : get_permalink();
        echo "<!-- ThinkRank SEO Canonical URL -->\n";
        echo "<link rel=\"canonical\" href=\"" . esc_url($canonical_url) . "\" />\n";
        echo "<!-- /ThinkRank SEO Canonical URL -->\n";
    }
    


    /**
     * Check if ThinkRank has metadata for current post
     *
     * @return bool True if has ThinkRank metadata
     */
    private function has_thinkrank_metadata(): bool {
        if (!is_singular()) {
            return false;
        }

        return !empty($this->current_metadata['title']) || !empty($this->current_metadata['description']);
    }

    /**
     * Check if current page has SEO data (public method for template functions)
     *
     * @return bool True if has SEO data
     */
    public function has_seo_data(): bool {
        // Check if Site Identity is enabled and active
        if ($this->site_identity_data && $this->site_identity_data['enabled']) {
            return true;
        }

        // Check if post has ThinkRank metadata
        return $this->has_thinkrank_metadata();
    }

    /**
     * Get current breadcrumbs data (public method for template functions)
     *
     * @return array|null Breadcrumb data or null if not available
     */
    public function get_current_breadcrumbs(): ?array {
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return null;
        }

        $settings = $this->site_identity_manager->get_settings('site');

        if (empty($settings['breadcrumbs_enabled'])) {
            return null;
        }

        return $this->generate_breadcrumbs($settings);
    }
    
    /**
     * Get current SEO metadata
     *
     * @return array Current metadata
     */
    public function get_current_metadata(): array {
        return $this->current_metadata;
    }

    /**
     * Generate title based on current context using Site Identity templates
     *
     * @return string|null Generated title or null if no template available
     */
    private function generate_context_title(): ?string {
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return null;
        }

        $template = $this->get_title_template_for_context();
        if (!$template) {
            return null;
        }

        $placeholders = $this->get_title_placeholders();
        return $this->process_title_template($template, $placeholders);
    }

    /**
     * Get title template for current context
     *
     * @return string|null Template string or null if not found
     */
    private function get_title_template_for_context(): ?string {
        $settings = $this->site_identity_manager->get_settings('site');

        switch ($this->current_context) {
            case 'homepage':
                return $settings['homepage_title'] ?? null;
            case 'post':
                return $settings['post_title'] ?? null;
            case 'page':
                return $settings['page_title'] ?? null;
            case 'category':
                return $settings['category_title'] ?? null;
            case 'tag':
                return $settings['tag_title'] ?? null;
            case 'author':
                return $settings['author_title'] ?? null;
            case 'search':
                return $settings['search_title'] ?? null;
            case 'archive':
                return $settings['archive_title'] ?? null;
            default:
                return null;
        }
    }

    /**
     * Get title placeholders for current context
     *
     * @return array Placeholder values
     */
    private function get_title_placeholders(): array {
        global $post, $wp_query;

        $settings = $this->site_identity_manager->get_settings('site');
        $separator = $this->get_title_separator($settings['title_separator'] ?? 'pipe');

        $placeholders = [
            '%site_title%' => $settings['site_name'] ?? get_bloginfo('name'),
            '%site_name%' => $settings['site_name'] ?? get_bloginfo('name'),
            '%site_description%' => $settings['site_description'] ?? get_bloginfo('description'),
            '%tagline%' => $settings['tagline'] ?? get_bloginfo('description'),
            '%separator%' => ' ' . $separator . ' ',
            '%date%' => gmdate('F Y'),
        ];

        // Context-specific placeholders
        switch ($this->current_context) {
            case 'post':
            case 'page':
                if ($this->current_post_id) {
                    $placeholders['%post_title%'] = get_the_title($this->current_post_id);
                    $placeholders['%page_title%'] = get_the_title($this->current_post_id);
                    $post_author = get_post_field('post_author', $this->current_post_id);
                    $placeholders['%author%'] = get_the_author_meta('display_name', $post_author);
                    $placeholders['%author_name%'] = get_the_author_meta('display_name', $post_author);

                    // Get categories for posts
                    $post_type = get_post_type($this->current_post_id);
                    if ($post_type === 'post') {
                        $categories = get_the_category($this->current_post_id);
                        $placeholders['%category%'] = !empty($categories) ? $categories[0]->name : '';
                    }
                }
                break;

            case 'category':
                $category = get_queried_object();
                if ($category) {
                    $placeholders['%category_title%'] = $category->name;
                    $placeholders['%category%'] = $category->name;
                }
                break;

            case 'tag':
                $tag = get_queried_object();
                if ($tag) {
                    $placeholders['%tag_title%'] = $tag->name;
                    $placeholders['%tag%'] = $tag->name;
                }
                break;

            case 'author':
                $author = get_queried_object();
                if ($author) {
                    $placeholders['%author_name%'] = $author->display_name;
                    $placeholders['%author%'] = $author->display_name;
                }
                break;

            case 'search':
                $placeholders['%search_term%'] = get_search_query();
                break;

            case 'archive':
                $placeholders['%archive_title%'] = get_the_archive_title();
                break;
        }

        return $placeholders;
    }

    /**
     * Process title template with placeholders
     *
     * @param string $template Template string
     * @param array $placeholders Placeholder values
     * @return string Processed title
     */
    private function process_title_template(string $template, array $placeholders): string {
        $title = str_replace(array_keys($placeholders), array_values($placeholders), $template);

        // Clean up multiple separators and extra spaces
        $separator = $placeholders['%separator%'] ?? ' | ';
        $title = preg_replace('/\s*' . preg_quote(trim($separator), '/') . '\s*' . preg_quote(trim($separator), '/') . '\s*/', $separator, $title);
        $title = preg_replace('/\s+/', ' ', $title);
        $title = trim($title);

        // Remove trailing separator
        $separator_trimmed = trim($separator);
        if (substr($title, -strlen($separator_trimmed)) === $separator_trimmed) {
            $title = trim(substr($title, 0, -strlen($separator_trimmed)));
        }

        return $title;
    }

    /**
     * Get title separator symbol
     *
     * @param string $separator_type Separator type
     * @return string Separator symbol
     */
    private function get_title_separator(string $separator_type): string {
        $separators = [
            'pipe' => '|',
            'dash' => '-',
            'ndash' => '–',
            'mdash' => '—',
            'middot' => '·',
            'bull' => '•',
            'star' => '*',
            'smstar' => '⋆',
            'gt' => '>',
            'raquo' => '»',
        ];

        return $separators[$separator_type] ?? '|';
    }

    /**
     * Get meta description with fallback system
     *
     * @return string|null Meta description or null if none available
     */
    private function get_meta_description(): ?string {
        // First priority: Post-specific ThinkRank metadata
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['description'])) {
            return $this->current_metadata['description'];
        }

        // Second priority: Site Identity default meta description
        if ($this->site_identity_data && $this->site_identity_data['enabled']) {
            $settings = $this->site_identity_manager->get_settings('site');
            $default_description = $settings['default_meta_description'] ?? '';

            if (!empty($default_description)) {
                return $default_description;
            }
        }

        // Third priority: Generate from content for posts/pages
        if (is_singular() && $this->current_post_id) {
            $post_content = get_post_field('post_content', $this->current_post_id);
            if ($post_content) {
                $excerpt = wp_trim_words(wp_strip_all_tags($post_content), 25, '...');
                if (!empty($excerpt)) {
                    return $excerpt;
                }
            }
        }

        // Fourth priority: Site description for homepage
        if (is_home() || is_front_page()) {
            $site_description = get_bloginfo('description');
            if (!empty($site_description)) {
                return $site_description;
            }
        }

        return null;
    }

    /**
     * Output site-wide schema markup with priority system
     *
     * Priority: Schema Manager > Site Identity (like Twitter Cards approach)
     *
     * @return void
     */
    public function output_site_schema_markup(): void {
        $has_schema_manager_output = false;

        // PRIORITY 1: Always output site-wide schemas (Organization, Website, LocalBusiness, Person)
        if ($this->schema_manager) {
            $site_wide_schemas = $this->schema_manager->get_deployed_schemas('site', null);

            if (!empty($site_wide_schemas)) {
                foreach ($site_wide_schemas as $schema_type => $schema_info) {
                    $this->output_schema_markup($schema_info['data'], $schema_type, 'Schema Manager');
                }
                $has_schema_manager_output = true;
            }
        }

        // PRIORITY 2: Also output page-specific schemas (Article, HowTo, FAQ, etc.) on individual posts/pages
        if ($this->schema_manager && (is_single() || is_page())) {
            $context_type = is_page() ? 'page' : 'post';
            $context_id = get_the_ID();

            $page_specific_schemas = $this->schema_manager->get_deployed_schemas($context_type, $context_id);

            if (!empty($page_specific_schemas)) {
                foreach ($page_specific_schemas as $schema_type => $schema_info) {
                    $this->output_schema_markup($schema_info['data'], $schema_type, 'Schema Manager');
                }
                $has_schema_manager_output = true;
            }
        }

        // Skip Site Identity fallback if any Schema Manager schemas were output
        if ($has_schema_manager_output) {
            return;
        }

        // PRIORITY 2: Fall back to Site Identity schemas (like basic Twitter Cards)
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return;
        }

        $settings = $this->site_identity_manager->get_settings('site');

        // Only output on homepage or if organization schema is enabled
        if (!is_home() && !is_front_page() && empty($settings['organization_schema'])) {
            return;
        }

        $schema = $this->generate_organization_schema($settings);

        if ($schema) {
            $this->output_schema_markup($schema, 'Organization', 'Site Identity');
        }
    }

    /**
     * Output schema markup with consistent formatting
     *
     * @param array  $schema_data Schema data
     * @param string $schema_type Schema type name
     * @param string $source      Source of schema (Schema Manager, Site Identity, etc.)
     * @return void
     */
    private function output_schema_markup(array $schema_data, string $schema_type, string $source): void {
        if (empty($schema_data)) {
            return;
        }

        echo '<!-- ThinkRank ' . esc_html($source) . ': ' . esc_html($schema_type) . ' Schema -->' . "\n";
        echo '<script type="application/ld+json">' . "\n";
        echo wp_json_encode($schema_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
        echo '</script>' . "\n";
        echo '<!-- /ThinkRank ' . esc_html($source) . ': ' . esc_html($schema_type) . ' Schema -->' . "\n";
    }

    /**
     * Generate organization schema markup
     *
     * @param array $settings Site identity settings
     * @return array|null Schema data or null if insufficient data
     */
    private function generate_organization_schema(array $settings): ?array {
        $site_name = $settings['site_name'] ?? get_bloginfo('name');
        $site_description = $settings['site_description'] ?? get_bloginfo('description');

        if (empty($site_name)) {
            return null;
        }

        $schema = [
            '@context' => 'https://schema.org',
            '@type' => 'Organization',
            '@id' => home_url() . '#organization',
            'name' => $site_name,
            'url' => home_url(),
        ];

        // Add description if available
        if (!empty($site_description)) {
            $schema['description'] = $site_description;
        }

        // Add logo if available with proper ImageObject structure
        if (!empty($settings['logo_url'])) {
            $schema['logo'] = [
                '@type' => 'ImageObject',
                '@id' => home_url() . '#logo',
                'url' => $settings['logo_url'],
                'contentUrl' => $settings['logo_url'],
                'caption' => $site_name . ' Logo'
            ];

            // Also add as image property
            $schema['image'] = $schema['logo'];
        }

        // Add social media accounts if available
        $social_urls = [];
        if (!empty($this->site_identity_data['social'])) {
            $social_data = $this->site_identity_data['social'];

            if (!empty($social_data['facebook_url'])) {
                $social_urls[] = $social_data['facebook_url'];
            }
            if (!empty($social_data['twitter_username'])) {
                $twitter_url = 'https://twitter.com/' . ltrim($social_data['twitter_username'], '@');
                $social_urls[] = $twitter_url;
            }
            if (!empty($social_data['linkedin_url'])) {
                $social_urls[] = $social_data['linkedin_url'];
            }
            if (!empty($social_data['instagram_url'])) {
                $social_urls[] = $social_data['instagram_url'];
            }
            if (!empty($social_data['youtube_url'])) {
                $social_urls[] = $social_data['youtube_url'];
            }
        }

        if (!empty($social_urls)) {
            $schema['sameAs'] = $social_urls;
        }

        // Add contact information if available
        if (!empty($settings['contact_email'])) {
            $schema['email'] = $settings['contact_email'];
        }

        return $schema;
    }

    /**
     * Output breadcrumb schema markup
     *
     * @return void
     */
    public function output_breadcrumb_schema(): void {
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return;
        }

        $settings = $this->site_identity_manager->get_settings('site');

        // Only output if breadcrumbs are enabled
        if (empty($settings['breadcrumbs_enabled'])) {
            return;
        }

        $breadcrumbs = $this->generate_breadcrumbs($settings);

        if (!empty($breadcrumbs['schema'])) {
            echo "<!-- ThinkRank SEO Breadcrumb Schema Markup -->\n";
            echo '<script type="application/ld+json">' . "\n";
            echo wp_json_encode($breadcrumbs['schema'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
            echo '</script>' . "\n";
            echo "<!-- /ThinkRank SEO Breadcrumb Schema Markup -->\n";
        }
    }

    /**
     * Output closing comment for ThinkRank SEO
     *
     * @return void
     */
    public function output_closing_comment(): void {
        // Only output if we've output any SEO content
        static $header_output = false;
        if ($header_output || $this->has_seo_output()) {
            echo "<!-- /ThinkRank SEO -->\n";
        }
    }

    /**
     * Check if any SEO content has been output
     *
     * @return bool True if SEO content was output
     */
    private function has_seo_output(): bool {
        // Check if we have meta description or any other SEO data
        return !empty($this->get_meta_description()) ||
               $this->has_thinkrank_metadata() ||
               ($this->site_identity_data && $this->site_identity_data['enabled']);
    }

    /**
     * Display breadcrumbs HTML
     *
     * @return void
     */
    public function display_breadcrumbs(): void {
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return;
        }

        $settings = $this->site_identity_manager->get_settings('site');

        // Only display if breadcrumbs are enabled
        if (empty($settings['breadcrumbs_enabled'])) {
            return;
        }

        $breadcrumbs = $this->generate_breadcrumbs($settings);

        if (!empty($breadcrumbs['html'])) {
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML is properly escaped in generate_breadcrumb_html method
            echo $breadcrumbs['html'];
        }
    }

    /**
     * Generate breadcrumbs data
     *
     * @param array $settings Breadcrumb settings
     * @return array Breadcrumb data with HTML and schema
     */
    private function generate_breadcrumbs(array $settings): array {
        $breadcrumbs = [
            'items' => [],
            'html' => '',
            'schema' => null
        ];

        // Get breadcrumb items
        $items = $this->get_breadcrumb_items($settings);

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

        $breadcrumbs['items'] = $items;

        // Generate HTML
        $breadcrumbs['html'] = $this->generate_breadcrumb_html($items, $settings);

        // Generate schema
        $breadcrumbs['schema'] = $this->generate_breadcrumb_schema($items);

        return $breadcrumbs;
    }

    /**
     * Filter WordPress robots.txt output
     *
     * @param string $output The default robots.txt output
     * @param string $public Whether the site is public
     * @return string Modified robots.txt content
     */
    public function filter_robots_txt(string $output, string $public): string {
        // Only override if Site Identity is enabled and robots.txt management is enabled
        if (!$this->site_identity_data || !$this->site_identity_data['enabled']) {
            return $output;
        }

        $settings = $this->site_identity_manager->get_settings('site');
        if (empty($settings['robots_txt_enabled'])) {
            return $output;
        }

        // Generate ThinkRank robots.txt content
        try {
            $robots_data = $this->site_identity_manager->generate_robots_txt();

            if (!empty($robots_data['content'])) {
                return $robots_data['content'];
            }
        } catch (\Exception $e) {
            // Robots.txt generation failed - fallback to default output
        }

        // Fallback to default output if generation fails
        return $output;
    }

    /**
     * Get breadcrumb items for current page
     *
     * @param array $settings Breadcrumb settings
     * @return array Breadcrumb items
     */
    private function get_breadcrumb_items(array $settings): array {
        $items = [];

        // Always start with home
        $home_text = $settings['breadcrumb_home_text'] ?? 'Home';
        $items[] = [
            'title' => $home_text,
            'url' => home_url(),
            'position' => 1
        ];

        $position = 2;

        if (is_single()) {
            $current_post_id = get_the_ID();

            if ($current_post_id) {
                // Add categories for posts
                $post_type = get_post_type($current_post_id);
                if ($post_type === 'post') {
                    $categories = get_the_category($current_post_id);
                    if (!empty($categories)) {
                        $category = $categories[0];
                        $items[] = [
                            'title' => $category->name,
                            'url' => get_category_link($category->term_id),
                            'position' => $position++
                        ];
                    }
                }

                // Add current post
                if (empty($settings['show_current_page']) || $settings['show_current_page']) {
                    $items[] = [
                        'title' => get_the_title($current_post_id),
                        'url' => get_permalink($current_post_id),
                        'position' => $position,
                        'current' => true
                    ];
                }
            }

        } elseif (is_page()) {
            $current_post_id = get_the_ID();

            if ($current_post_id) {
                // Add parent pages
                $parents = [];
                $parent_id = wp_get_post_parent_id($current_post_id);

                while ($parent_id) {
                    $parent = get_post($parent_id);
                    if ($parent) {
                        $parents[] = [
                            'title' => get_the_title($parent->ID),
                            'url' => get_permalink($parent->ID),
                            'position' => 0 // Will be set later
                        ];
                        $parent_id = $parent->post_parent;
                    } else {
                        break;
                    }
                }

                // Reverse to get correct order
                $parents = array_reverse($parents);

                // Add parents with correct positions
                foreach ($parents as $parent) {
                    $parent['position'] = $position++;
                    $items[] = $parent;
                }

                // Add current page
                if (empty($settings['show_current_page']) || $settings['show_current_page']) {
                    $items[] = [
                        'title' => get_the_title($current_post_id),
                        'url' => get_permalink($current_post_id),
                        'position' => $position,
                        'current' => true
                    ];
                }
            }

        } elseif (is_category()) {
            $category = get_queried_object();

            // Add parent categories
            $parents = [];
            $parent_id = $category->parent;

            while ($parent_id) {
                $parent = get_category($parent_id);
                if ($parent && !is_wp_error($parent)) {
                    $parents[] = [
                        'title' => $parent->name,
                        'url' => get_category_link($parent->term_id),
                        'position' => 0 // Will be set later
                    ];
                    $parent_id = $parent->parent;
                } else {
                    break;
                }
            }

            // Reverse to get correct order
            $parents = array_reverse($parents);

            // Add parents with correct positions
            foreach ($parents as $parent) {
                $parent['position'] = $position++;
                $items[] = $parent;
            }

            // Add current category
            if (empty($settings['show_current_page']) || $settings['show_current_page']) {
                $items[] = [
                    'title' => $category->name,
                    'url' => get_category_link($category->term_id),
                    'position' => $position,
                    'current' => true
                ];
            }
        }

        return $items;
    }

    /**
     * Generate breadcrumb HTML
     *
     * @param array $items Breadcrumb items
     * @param array $settings Breadcrumb settings
     * @return string HTML output
     */
    private function generate_breadcrumb_html(array $items, array $settings): string {
        if (empty($items)) {
            return '';
        }

        $separator = $settings['breadcrumb_separator'] ?? '>';
        $prefix = $settings['breadcrumb_prefix'] ?? '';

        $html = '<nav class="thinkrank-breadcrumbs" aria-label="Breadcrumb">';

        if (!empty($prefix)) {
            $html .= '<span class="breadcrumb-prefix">' . esc_html($prefix) . '</span> ';
        }

        $html .= '<ol class="breadcrumb-list">';

        $total_items = count($items);

        foreach ($items as $index => $item) {
            $is_last = ($index === $total_items - 1);
            $is_current = !empty($item['current']);

            $html .= '<li class="breadcrumb-item' . ($is_current ? ' current' : '') . '">';

            if (!$is_current && !empty($item['url'])) {
                $html .= '<a href="' . esc_url($item['url']) . '">' . esc_html($item['title']) . '</a>';
            } else {
                $html .= '<span>' . esc_html($item['title']) . '</span>';
            }

            if (!$is_last) {
                $html .= ' <span class="breadcrumb-separator">' . esc_html($separator) . '</span> ';
            }

            $html .= '</li>';
        }

        $html .= '</ol>';
        $html .= '</nav>';

        return $html;
    }

    /**
     * Generate breadcrumb schema markup
     *
     * @param array $items Breadcrumb items
     * @return array Schema data
     */
    private function generate_breadcrumb_schema(array $items): array {
        if (empty($items)) {
            return [];
        }

        $schema_items = [];

        foreach ($items as $item) {
            $schema_items[] = [
                '@type' => 'ListItem',
                'position' => $item['position'],
                'name' => $item['title'],
                'item' => $item['url']
            ];
        }

        return [
            '@context' => 'https://schema.org',
            '@type' => 'BreadcrumbList',
            'itemListElement' => $schema_items
        ];
    }

    /**
     * Debug method to verify priority system (remove in production)
     *
     * @return array Debug information about current page SEO data
     */
    public function debug_seo_priority(): array {
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return [];
        }

        $debug = [
            'context' => $this->current_context,
            'is_singular' => is_singular(),
            'post_id' => $this->current_post_id,
            'has_post_metadata' => $this->has_thinkrank_metadata(),
            'post_metadata' => $this->current_metadata,
            'site_identity_enabled' => $this->site_identity_data && $this->site_identity_data['enabled'],
            'final_title' => '',
            'final_description' => '',
            'title_source' => '',
            'description_source' => ''
        ];

        // Determine title source
        if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['title'])) {
            $debug['final_title'] = $this->current_metadata['title'];
            $debug['title_source'] = 'post_metadata';
        } else {
            $generated_title = $this->generate_context_title();
            if ($generated_title) {
                $debug['final_title'] = $generated_title;
                $debug['title_source'] = 'site_identity_template';
            } else {
                $debug['final_title'] = is_singular() ? get_the_title() : get_bloginfo('name');
                $debug['title_source'] = 'wordpress_default';
            }
        }

        // Determine description source
        $description = $this->get_meta_description();
        if ($description) {
            $debug['final_description'] = $description;

            if ($this->has_thinkrank_metadata() && !empty($this->current_metadata['description'])) {
                $debug['description_source'] = 'post_metadata';
            } elseif ($this->site_identity_data && $this->site_identity_data['enabled']) {
                $settings = $this->site_identity_manager->get_settings('site');
                if (!empty($settings['default_meta_description'])) {
                    $debug['description_source'] = 'site_identity_default';
                } else {
                    $debug['description_source'] = 'auto_generated';
                }
            } else {
                $debug['description_source'] = 'auto_generated_or_site_description';
            }
        } else {
            $debug['description_source'] = 'none';
        }

        return $debug;
    }

    /**
     * Get Twitter image with proper fallback priority
     *
     * @since 1.0.0
     *
     * @return string|null Twitter image URL or null if none available
     */
    private function get_twitter_image_with_fallback(): ?string {
        // For posts, check for post-specific Twitter image meta first (future enhancement)
        if (is_singular() && $this->current_post_id) {
            $post_twitter_image = get_post_meta($this->current_post_id, '_thinkrank_twitter_image', true);
            if (!empty($post_twitter_image)) {
                return $post_twitter_image;
            }

            // Check featured image as fallback for posts
            if (has_post_thumbnail($this->current_post_id)) {
                $featured_image_url = get_the_post_thumbnail_url($this->current_post_id, 'large');
                if ($featured_image_url) {
                    return $featured_image_url;
                }
            }
        }

        // Check Social Meta Manager settings for Twitter-specific default image
        if ($this->social_manager) {
            $social_context = $this->current_context === 'homepage' ? 'site' : $this->current_context;
            $social_settings = $this->social_manager->get_settings($social_context, $this->current_post_id);

            // Prioritize Twitter-specific default image
            if (!empty($social_settings['default_twitter_image'])) {
                return $social_settings['default_twitter_image'];
            }

            // Fallback to Open Graph default image
            if (!empty($social_settings['default_og_image'])) {
                return $social_settings['default_og_image'];
            }

            // Final fallback to generic default image
            if (!empty($social_settings['default_image'])) {
                return $social_settings['default_image'];
            }
        }

        // Check Site Identity data for social images
        if ($this->site_identity_data && !empty($this->site_identity_data['social'])) {
            $social_data = $this->site_identity_data['social'];

            // Check for any configured social image
            if (!empty($social_data['default_image'])) {
                return $social_data['default_image'];
            }
        }

        return null;
    }
}

```
