<?php

/**
 * Podcast Module
 *
 * Handles podcast SEO functionality including CPT, RSS feeds, and schema.
 *
 * @package ProRank\SEO\Modules\Content
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Content;

use ProRank\SEO\Modules\BaseModule;
use ProRank\SEO\Core\SettingsManager;
use ProRank\SEO\Core\LicenseManager;
use ProRank\SEO\Plugin;

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


/**
 * PodcastModule class
 *
 * Manages podcast custom post type, RSS feeds, and podcast schema
 */


class PodcastModule extends BaseModule {

    /**
     * Required tier for this module
     */
    protected string $feature_tier = 'free';
    
    /**
     * Settings manager instance
     *
     * @var SettingsManager
     */
    private SettingsManager $settings_manager;
    
    /**
     * License manager instance
     *
     * @var LicenseManager
     */
    private LicenseManager $license_manager;
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->slug = 'podcast-seo';
        $this->name = 'Podcast SEO';
        $this->description = 'Optimize your podcast for search engines with dedicated post types, RSS feeds, and structured data.';
$this->parent_slug = 'on-page-seo';
        
        // Initialize dependencies
        $plugin = Plugin::get_instance();
        $this->settings_manager = $plugin->settings();
        $this->license_manager = $plugin->license();
    }
    
    /**
     * Initialize module hooks
     *
     * @return void
     */
    public function init_hooks(): void {
        // Register settings
        $this->register_settings();
        
        // Register REST routes for settings
        add_action('rest_api_init', [$this, 'register_rest_routes']);

        if (did_action('init')) {
            $this->register_podcast_cpt();
            $this->register_podcast_taxonomies();
            $this->add_podcast_feed();
            $this->maybe_flush_rewrite_rules();
        } else {
            // Register CPT with early priority
            add_action('init', [$this, 'register_podcast_cpt'], 5);

            // Register taxonomies
            add_action('init', [$this, 'register_podcast_taxonomies'], 6);

            // Add podcast feed before rewrite rules are considered complete.
            add_action('init', [$this, 'add_podcast_feed'], 9);

            // Check if we need to flush rewrite rules
            add_action('init', [$this, 'maybe_flush_rewrite_rules'], 999);
        }
        
        // Menu hidden - access episodes from Podcast SEO settings page
        
        // Filter feed content
        add_filter('the_content_feed', [$this, 'podcast_feed_content'], 10, 2);
        add_filter('the_excerpt_rss', [$this, 'podcast_feed_excerpt'], 10);
        
        // Add iTunes/podcast namespace
        add_action('rss2_ns', [$this, 'add_podcast_namespace']);

        // Custom podcast feeds should advertise themselves as standard RSS feeds.
        add_filter('feed_content_type', [$this, 'filter_podcast_feed_content_type'], 10, 2);
        
        // Add podcast feed items
        add_action('rss2_item', [$this, 'add_podcast_feed_items']);
        
        // Filter schema graph for advanced schema when the higher tier is active.
        if ($this->license_manager->is_tier('pro')) {
            add_filter('prorank_seo_schema_graph', [$this, 'add_podcast_schema'], 10, 2);
        }
        
        // Add meta boxes
        add_action('add_meta_boxes', [$this, 'add_podcast_meta_boxes']);
        
        // Save meta box data
        add_action('save_post_prorank_podcast', [$this, 'save_podcast_meta'], 10, 2);
        
        // Add custom columns to podcast list
        add_filter('manage_prorank_podcast_posts_columns', [$this, 'add_podcast_columns']);
        add_action('manage_prorank_podcast_posts_custom_column', [$this, 'render_podcast_columns'], 10, 2);
    }
    
    /**
     * Register podcast custom post type
     *
     * @return void
     */
    public function register_podcast_cpt(): void {
        // Check if already registered to avoid re-registration
        if (post_type_exists('prorank_podcast')) {
            return;
        }
        
        $labels = [
            'name'                  => _x('Podcast Episodes', 'Post type general name', 'prorank-seo'),
            'singular_name'         => _x('Episode', 'Post type singular name', 'prorank-seo'),
            'menu_name'             => _x('Podcast', 'Admin Menu text', 'prorank-seo'),
            'name_admin_bar'        => _x('Episode', 'Add New on Toolbar', 'prorank-seo'),
            'add_new'               => __('Add New Episode', 'prorank-seo'),
            'add_new_item'          => __('Add New Episode', 'prorank-seo'),
            'new_item'              => __('New Episode', 'prorank-seo'),
            'edit_item'             => __('Edit Episode', 'prorank-seo'),
            'view_item'             => __('View Episode', 'prorank-seo'),
            'all_items'             => __('All Episodes', 'prorank-seo'),
            'search_items'          => __('Search Episodes', 'prorank-seo'),
            'parent_item_colon'     => __('Parent Episodes:', 'prorank-seo'),
            'not_found'             => __('No episodes found.', 'prorank-seo'),
            'not_found_in_trash'    => __('No episodes found in Trash.', 'prorank-seo'),
            'featured_image'        => _x('Episode Artwork', 'Overrides the "Featured Image" phrase', 'prorank-seo'),
            'set_featured_image'    => _x('Set episode artwork', 'Overrides the "Set featured image" phrase', 'prorank-seo'),
            'remove_featured_image' => _x('Remove episode artwork', 'Overrides the "Remove featured image" phrase', 'prorank-seo'),
            'use_featured_image'    => _x('Use as episode artwork', 'Overrides the "Use as featured image" phrase', 'prorank-seo'),
            'archives'              => _x('Episode archives', 'The post type archive label', 'prorank-seo'),
            'insert_into_item'      => _x('Insert into episode', 'Overrides the "Insert into post" phrase', 'prorank-seo'),
            'uploaded_to_this_item' => _x('Uploaded to this episode', 'Overrides the "Uploaded to this post" phrase', 'prorank-seo'),
            'filter_items_list'     => _x('Filter episodes list', 'Screen reader text', 'prorank-seo'),
            'items_list_navigation' => _x('Episodes list navigation', 'Screen reader text', 'prorank-seo'),
            'items_list'            => _x('Episodes list', 'Screen reader text', 'prorank-seo'),
        ];
        
        $args = [
            'labels'             => $labels,
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => false, // Access from Podcast SEO settings page
            'query_var'          => true,
            'rewrite'            => ['slug' => apply_filters('prorank_podcast_slug', 'podcast')],
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => false,
            'menu_position'      => null,
            'menu_icon'          => 'dashicons-microphone',
            'supports'           => ['title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'custom-fields'],
            'show_in_rest'       => true,
            'taxonomies'         => ['podcast_series', 'podcast_guest'],
        ];
        
        $result = register_post_type('prorank_podcast', $args);
        
        if (is_wp_error($result)) {
            prorank_log('[ProRank SEO] Failed to register podcast CPT: ' . $result->get_error_message());
        } else {
            // Set flag that CPT was registered
            update_option('prorank_podcast_cpt_registered', true);
        }
    }
    
    /**
     * Register podcast taxonomies
     *
     * @return void
     */
    public function register_podcast_taxonomies(): void {
        // Podcast Series (like categories)
        if (!taxonomy_exists('podcast_series')) {
            $series_labels = [
                'name'              => _x('Podcast Series', 'taxonomy general name', 'prorank-seo'),
                'singular_name'     => _x('Series', 'taxonomy singular name', 'prorank-seo'),
                'search_items'      => __('Search Series', 'prorank-seo'),
                'all_items'         => __('All Series', 'prorank-seo'),
                'parent_item'       => __('Parent Series', 'prorank-seo'),
                'parent_item_colon' => __('Parent Series:', 'prorank-seo'),
                'edit_item'         => __('Edit Series', 'prorank-seo'),
                'update_item'       => __('Update Series', 'prorank-seo'),
                'add_new_item'      => __('Add New Series', 'prorank-seo'),
                'new_item_name'     => __('New Series Name', 'prorank-seo'),
                'menu_name'         => __('Series', 'prorank-seo'),
            ];
            
            $series_args = [
                'hierarchical'      => true,
                'labels'            => $series_labels,
                'show_ui'           => true,
                'show_admin_column' => true,
                'query_var'         => true,
                'rewrite'           => ['slug' => 'podcast-series'],
                'show_in_rest'      => true,
            ];
            
            register_taxonomy('podcast_series', ['prorank_podcast'], $series_args);
        }
        
        // Podcast Guests (like tags)
        if (!taxonomy_exists('podcast_guest')) {
            $guest_labels = [
                'name'                       => _x('Guests', 'taxonomy general name', 'prorank-seo'),
                'singular_name'              => _x('Guest', 'taxonomy singular name', 'prorank-seo'),
                'search_items'               => __('Search Guests', 'prorank-seo'),
                'popular_items'              => __('Popular Guests', 'prorank-seo'),
                'all_items'                  => __('All Guests', 'prorank-seo'),
                'parent_item'                => null,
                'parent_item_colon'          => null,
                'edit_item'                  => __('Edit Guest', 'prorank-seo'),
                'update_item'                => __('Update Guest', 'prorank-seo'),
                'add_new_item'               => __('Add New Guest', 'prorank-seo'),
                'new_item_name'              => __('New Guest Name', 'prorank-seo'),
                'separate_items_with_commas' => __('Separate guests with commas', 'prorank-seo'),
                'add_or_remove_items'        => __('Add or remove guests', 'prorank-seo'),
                'choose_from_most_used'      => __('Choose from the most used guests', 'prorank-seo'),
                'not_found'                  => __('No guests found.', 'prorank-seo'),
                'menu_name'                  => __('Guests', 'prorank-seo'),
            ];
            
            $guest_args = [
                'hierarchical'          => false,
                'labels'                => $guest_labels,
                'show_ui'               => true,
                'show_admin_column'     => true,
                'update_count_callback' => '_update_post_term_count',
                'query_var'             => true,
                'rewrite'               => ['slug' => 'podcast-guest'],
                'show_in_rest'          => true,
            ];
            
            register_taxonomy('podcast_guest', ['prorank_podcast'], $guest_args);
        }
    }
    
    /**
     * Maybe flush rewrite rules if needed
     *
     * @return void
     */
    public function maybe_flush_rewrite_rules(): void {
        if (!get_option('prorank_podcast_cpt_registered')) {
            return;
        }

        $needs_flush = !get_option('prorank_podcast_cpt_flushed');

        if (!$needs_flush) {
            $needs_flush = !$this->has_podcast_feed_rule((array) get_option('rewrite_rules', []));
        }

        if ($needs_flush) {
            flush_rewrite_rules();
            update_option('prorank_podcast_cpt_flushed', true);
        }
    }
    
    
    /**
     * Add podcast feed
     *
     * @return void
     */
    public function add_podcast_feed(): void {
        add_feed('podcast', [$this, 'render_podcast_feed']);
        add_rewrite_rule('^feed/podcast/?$', 'index.php?feed=podcast', 'top');
    }
    
    /**
     * Render podcast RSS feed
     *
     * @return void
     */
    public function render_podcast_feed(): void {
        // Load feed template
        $template = locate_template('feed-podcast.php');
        
        if (!$template) {
            // Use our custom template
            load_template(plugin_dir_path(__FILE__) . 'templates/feed-podcast.php');
        } else {
            load_template($template);
        }
    }
    
    /**
     * Add podcast namespace to RSS feed
     *
     * @return void
     */
    public function add_podcast_namespace(): void {
        if (is_feed('podcast')) {
            echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" ';
            echo 'xmlns:content="http://purl.org/rss/1.0/modules/content/" ';
            echo 'xmlns:atom="http://www.w3.org/2005/Atom" ';
            echo 'xmlns:podcast="https://podcastindex.org/namespace/1.0" ';
        }
    }

    /**
     * Force the custom podcast feed to use the RSS content type.
     *
     * @param string $content_type Current content type.
     * @param string $type         Feed type.
     * @return string
     */
    public function filter_podcast_feed_content_type(string $content_type, string $type): string {
        if ($type === 'podcast') {
            return 'application/rss+xml';
        }

        return $content_type;
    }

    /**
     * Determine whether rewrite rules already include the custom podcast feed.
     *
     * @param array $rules Rewrite rules map.
     * @return bool
     */
    private function has_podcast_feed_rule(array $rules): bool {
        foreach ($rules as $pattern => $target) {
            $pattern = (string) $pattern;
            $target = (string) $target;

            if (strpos($target, 'feed=podcast') !== false) {
                return true;
            }

            if (strpos($pattern, 'feed/podcast') !== false) {
                return true;
            }

            if (strpos($pattern, 'podcast/feed') === 0 && strpos($target, 'post_type=prorank_podcast') === false) {
                return true;
            }
        }

        return false;
    }
    
    /**
     * Add podcast-specific items to feed
     *
     * @return void
     */
    public function add_podcast_feed_items(): void {
        if (!is_feed('podcast')) {
            return;
        }

        if (!empty($GLOBALS['prorank_podcast_template_active'])) {
            return;
        }
        
        global $post;
        
        // Get episode meta
        $audio_url = get_post_meta($post->ID, '_prorank_podcast_audio_url', true);
        $duration = get_post_meta($post->ID, '_prorank_podcast_duration', true);
        $episode_number = get_post_meta($post->ID, '_prorank_podcast_episode_number', true);
        $season_number = get_post_meta($post->ID, '_prorank_podcast_season_number', true);
        $explicit = get_post_meta($post->ID, '_prorank_podcast_explicit', true);
        $episode_type = get_post_meta($post->ID, '_prorank_podcast_episode_type', true);
        $transcript = get_post_meta($post->ID, '_prorank_podcast_transcript', true);
        
        $settings = wp_parse_args(get_option('prorank_seo_podcast', []), $this->get_default_settings());
        $enable_transcripts = !empty($settings['enable_transcripts']);

        // Audio enclosure
        if ($audio_url) {
            $audio_size = $this->get_remote_file_size($audio_url);
            $audio_type = $this->get_audio_mime_type($audio_url);
            
            echo '<enclosure url="' . esc_url($audio_url) . '" ';
            echo 'length="' . esc_attr($audio_size) . '" ';
            echo 'type="' . esc_attr($audio_type) . '" />' . "\n";
            
            // iTunes specific tags
            echo '<itunes:duration>' . esc_html($duration) . '</itunes:duration>' . "\n";
            
            if ($episode_number) {
                echo '<itunes:episode>' . esc_html($episode_number) . '</itunes:episode>' . "\n";
            }
            
            if ($season_number) {
                echo '<itunes:season>' . esc_html($season_number) . '</itunes:season>' . "\n";
            }
            
            echo '<itunes:explicit>' . ($explicit ? 'yes' : 'no') . '</itunes:explicit>' . "\n";
            
            if ($episode_type) {
                echo '<itunes:episodeType>' . esc_html($episode_type) . '</itunes:episodeType>' . "\n";
            }
            
            // Add episode summary
            $summary = get_the_excerpt();
            if ($summary) {
                echo '<itunes:summary>' . esc_html($summary) . '</itunes:summary>' . "\n";
            }
            
            // Add episode image
            $thumbnail_id = get_post_thumbnail_id($post->ID);
            if ($thumbnail_id) {
                $image_url = wp_get_attachment_url($thumbnail_id);
                if ($image_url) {
                    echo '<itunes:image href="' . esc_url($image_url) . '" />' . "\n";
                }
            }
            
            // Add transcript support when enabled.
            if ($enable_transcripts && $transcript) {
                echo '<podcast:transcript url="' . esc_url(get_permalink($post->ID) . '#transcript') . '" type="text/html" />' . "\n";
            }
            
            // Add funding link if configured
            if (!empty($settings['funding_url'])) {
                echo '<podcast:funding url="' . esc_url($settings['funding_url']) . '">' . esc_html__('Support the Show', 'prorank-seo') . '</podcast:funding>' . "\n";
            }
        }
    }
    
    /**
     * Filter podcast feed content
     *
     * @param string $content The content.
     * @param string $feed_type Feed type.
     * @return string
     */
    public function podcast_feed_content(string $content, string $feed_type = ''): string {
        if (is_feed('podcast')) {
            // Add show notes if available
            global $post;
            $show_notes = get_post_meta($post->ID, '_prorank_podcast_show_notes', true);
            
            if ($show_notes) {
                $content = $show_notes . "\n\n" . $content;
            }
        }
        
        return $content;
    }
    
    /**
     * Filter podcast feed excerpt
     *
     * @param string $excerpt The excerpt.
     * @return string
     */
    public function podcast_feed_excerpt(string $excerpt): string {
        if (is_feed('podcast')) {
            // Use custom episode description if available
            global $post;
            $episode_description = get_post_meta($post->ID, '_prorank_podcast_description', true);
            
            if ($episode_description) {
                return $episode_description;
            }
        }
        
        return $excerpt;
    }
    
    /**
     * Add podcast schema to graph.
     *
     * @param array $graph Current schema graph.
     * @param mixed $context_object Current context object.
     * @return array Modified graph.
     */
    public function add_podcast_schema(array $graph, $context_object): array {
        // Only add for single podcast episode pages
        if (!is_singular('prorank_podcast') || !$context_object instanceof \WP_Post) {
            return $graph;
        }
        
        // Get episode meta
        $audio_url = get_post_meta($context_object->ID, '_prorank_podcast_audio_url', true);
        $duration = get_post_meta($context_object->ID, '_prorank_podcast_duration', true);
        $episode_number = get_post_meta($context_object->ID, '_prorank_podcast_episode_number', true);
        $season_number = get_post_meta($context_object->ID, '_prorank_podcast_season_number', true);
        $video_url = get_post_meta($context_object->ID, '_prorank_podcast_video_url', true);
        $transcript = get_post_meta($context_object->ID, '_prorank_podcast_transcript', true);
        $chapters = get_post_meta($context_object->ID, '_prorank_podcast_chapters', true);
        
        // Build PodcastEpisode schema
        $episode_schema = [
            '@type' => 'PodcastEpisode',
            '@id' => get_permalink($context_object) . '#podcast-episode',
            'url' => get_permalink($context_object),
            'name' => get_the_title($context_object),
            'datePublished' => get_the_date('c', $context_object),
            'description' => get_the_excerpt($context_object),
        ];
        
        // Add audio
        if ($audio_url) {
            $episode_schema['associatedMedia'] = [
                '@type' => 'MediaObject',
                'contentUrl' => $audio_url,
            ];
            
            if ($duration) {
                $episode_schema['associatedMedia']['duration'] = $this->format_duration_iso8601($duration);
            }
        }
        
        // Add episode number
        if ($episode_number) {
            $episode_schema['episodeNumber'] = intval($episode_number);
        }
        
        // Add series information
        $series = wp_get_post_terms($context_object->ID, 'podcast_series');
        if (!empty($series)) {
            $series_name = $series[0]->name;
            $episode_schema['partOfSeries'] = [
                '@type' => 'PodcastSeries',
                'name' => $series_name,
                'url' => get_term_link($series[0]),
            ];
            
            if ($season_number) {
                $episode_schema['partOfSeason'] = [
                    '@type' => 'PodcastSeason',
                    'seasonNumber' => intval($season_number),
                    'partOfSeries' => [
                        '@type' => 'PodcastSeries',
                        'name' => $series_name,
                    ],
                ];
            }
        }
        
        // Add image
        $thumbnail_id = get_post_thumbnail_id($context_object);
        if ($thumbnail_id) {
            $image_data = wp_get_attachment_image_src($thumbnail_id, 'full');
            if ($image_data) {
                $episode_schema['image'] = [
                    '@type' => 'ImageObject',
                    'url' => $image_data[0],
                    'width' => $image_data[1],
                    'height' => $image_data[2],
                ];
            }
        }
        
        // Add video (2025 feature)
        if ($video_url) {
            $episode_schema['video'] = [
                '@type' => 'VideoObject',
                'contentUrl' => $video_url,
                'embedUrl' => $video_url,
                'uploadDate' => get_the_date('c', $context_object),
                'description' => get_the_excerpt($context_object),
            ];
        }
        
        // Add transcript (2025 feature for accessibility and SEO)
        if ($transcript) {
            $episode_schema['transcript'] = wp_strip_all_tags($transcript);
            $episode_schema['accessibilityFeature'] = 'transcript';
            
            // Add speakable schema for voice search
            $episode_schema['speakable'] = [
                '@type' => 'SpeakableSpecification',
                'cssSelector' => ['.episode-transcript'],
            ];
        }
        
        // Add chapters as hasPart (2025 feature)
        if ($chapters) {
            $chapter_lines = explode("\n", $chapters);
            $parts = [];
            foreach ($chapter_lines as $line) {
                if (preg_match('/^(\d{2}:\d{2}:\d{2})\s+(.+)$/', trim($line), $matches)) {
                    $parts[] = [
                        '@type' => 'Clip',
                        'name' => $matches[2],
                        'startOffset' => $this->format_duration_iso8601($matches[1]),
                    ];
                }
            }
            if (!empty($parts)) {
                $episode_schema['hasPart'] = $parts;
            }
        }
        
        // Add WebPage schema for better SEO (2025 standard)
        $webpage_schema = [
            '@type' => 'WebPage',
            '@id' => get_permalink($context_object) . '#webpage',
            'url' => get_permalink($context_object),
            'name' => get_the_title($context_object) . ' - ' . get_bloginfo('name'),
            'isPartOf' => [
                '@id' => home_url() . '#website',
            ],
            'datePublished' => get_the_date('c', $context_object),
            'dateModified' => get_the_modified_date('c', $context_object),
            'breadcrumb' => [
                '@id' => get_permalink($context_object) . '#breadcrumb',
            ],
            'primaryImageOfPage' => $thumbnail_id ? [
                '@id' => get_permalink($context_object) . '#primaryimage',
            ] : null,
            'mainEntity' => [
                '@id' => get_permalink($context_object) . '#podcast-episode',
            ],
        ];
        
        // Add the schemas to the graph
        $graph[] = $episode_schema;
        $graph[] = $webpage_schema;
        
        return $graph;
    }
    
    /**
     * Add podcast meta boxes
     *
     * @return void
     */
    public function add_podcast_meta_boxes(): void {
        add_meta_box(
            'prorank_podcast_details',
            __('Episode Details', 'prorank-seo'),
            [$this, 'render_podcast_meta_box'],
            'prorank_podcast',
            'normal',
            'high'
        );
    }
    
    /**
     * Render podcast meta box
     *
     * @param \WP_Post $post Current post object.
     * @return void
     */
    public function render_podcast_meta_box(\WP_Post $post): void {
        // Add nonce for security
        wp_nonce_field('prorank_podcast_meta', 'prorank_podcast_meta_nonce');
        
        // Get existing values
        $audio_url = get_post_meta($post->ID, '_prorank_podcast_audio_url', true);
        $duration = get_post_meta($post->ID, '_prorank_podcast_duration', true);
        $episode_number = get_post_meta($post->ID, '_prorank_podcast_episode_number', true);
        $season_number = get_post_meta($post->ID, '_prorank_podcast_season_number', true);
        $explicit = get_post_meta($post->ID, '_prorank_podcast_explicit', true);
        $episode_type = get_post_meta($post->ID, '_prorank_podcast_episode_type', true);
        $description = get_post_meta($post->ID, '_prorank_podcast_description', true);
        $show_notes = get_post_meta($post->ID, '_prorank_podcast_show_notes', true);
        // Accessibility fields
        $transcript = get_post_meta($post->ID, '_prorank_podcast_transcript', true);
        ?>
        <div class="prorank-podcast-meta-box">
            <h4><?php esc_html_e('Audio File', 'prorank-seo'); ?></h4>
            
            <p>
                <label for="prorank_podcast_audio_url"><?php esc_html_e('Audio URL:', 'prorank-seo'); ?></label><br>
                <input type="url" id="prorank_podcast_audio_url" name="prorank_podcast_audio_url" value="<?php echo esc_url($audio_url); ?>" class="widefat" />
                <span class="description"><?php esc_html_e('Direct URL to the audio file (MP3 recommended)', 'prorank-seo'); ?></span>
            </p>
            
            <p>
                <label for="prorank_podcast_duration"><?php esc_html_e('Duration:', 'prorank-seo'); ?></label><br>
                <input type="text" id="prorank_podcast_duration" name="prorank_podcast_duration" value="<?php echo esc_attr($duration); ?>" placeholder="HH:MM:SS" />
                <span class="description"><?php esc_html_e('Format: HH:MM:SS (e.g., 01:23:45)', 'prorank-seo'); ?></span>
            </p>
            
            <h4><?php esc_html_e('Episode Information', 'prorank-seo'); ?></h4>
            
            <p>
                <label for="prorank_podcast_episode_number"><?php esc_html_e('Episode Number:', 'prorank-seo'); ?></label><br>
                <input type="number" id="prorank_podcast_episode_number" name="prorank_podcast_episode_number" value="<?php echo esc_attr($episode_number); ?>" min="1" />
            </p>
            
            <p>
                <label for="prorank_podcast_season_number"><?php esc_html_e('Season Number:', 'prorank-seo'); ?></label><br>
                <input type="number" id="prorank_podcast_season_number" name="prorank_podcast_season_number" value="<?php echo esc_attr($season_number); ?>" min="1" />
            </p>
            
            <p>
                <label for="prorank_podcast_episode_type"><?php esc_html_e('Episode Type:', 'prorank-seo'); ?></label><br>
                <select id="prorank_podcast_episode_type" name="prorank_podcast_episode_type">
                    <option value="full" <?php selected($episode_type, 'full'); ?>><?php esc_html_e('Full Episode', 'prorank-seo'); ?></option>
                    <option value="trailer" <?php selected($episode_type, 'trailer'); ?>><?php esc_html_e('Trailer', 'prorank-seo'); ?></option>
                    <option value="bonus" <?php selected($episode_type, 'bonus'); ?>><?php esc_html_e('Bonus', 'prorank-seo'); ?></option>
                </select>
            </p>
            
            <p>
                <label for="prorank_podcast_explicit">
                    <input type="checkbox" id="prorank_podcast_explicit" name="prorank_podcast_explicit" value="1" <?php checked($explicit, '1'); ?> />
                    <?php esc_html_e('Explicit content', 'prorank-seo'); ?>
                </label>
            </p>
            
            <h4><?php esc_html_e('Episode Description', 'prorank-seo'); ?></h4>
            
            <p>
                <label for="prorank_podcast_description"><?php esc_html_e('Short Description (for podcast apps):', 'prorank-seo'); ?></label><br>
                <textarea id="prorank_podcast_description" name="prorank_podcast_description" rows="3" class="widefat"><?php echo esc_textarea($description); ?></textarea>
                <span class="description"><?php esc_html_e('Brief episode description for podcast directories', 'prorank-seo'); ?></span>
            </p>
            
            <p>
                <label for="prorank_podcast_show_notes"><?php esc_html_e('Show Notes:', 'prorank-seo'); ?></label><br>
                <?php
                wp_editor($show_notes, 'prorank_podcast_show_notes', [
                    'textarea_rows' => 10,
                    'media_buttons' => true,
                    'teeny' => false,
                ]);
                ?>
                <span class="description"><?php esc_html_e('Detailed show notes with timestamps, links, and resources', 'prorank-seo'); ?></span>
            </p>
            
            <h4><?php esc_html_e('Accessibility & SEO', 'prorank-seo'); ?></h4>

            <p>
                <label for="prorank_podcast_transcript"><?php esc_html_e('Episode Transcript:', 'prorank-seo'); ?></label><br>
                <?php
                wp_editor($transcript, 'prorank_podcast_transcript', [
                    'textarea_rows' => 15,
                    'media_buttons' => false,
                    'teeny' => true,
                ]);
                ?>
                <span class="description"><?php esc_html_e('Full episode transcript for accessibility and SEO', 'prorank-seo'); ?></span>
            </p>
        </div>
        <?php
    }
    
    /**
     * Save podcast meta box data
     *
     * @param int      $post_id Post ID.
     * @param \WP_Post $post    Post object.
     * @return void
     */
    public function save_podcast_meta(int $post_id, \WP_Post $post): void {
        // Check nonce
        $podcast_meta_nonce = (string) prorank_get_input_value($_POST, 'prorank_podcast_meta_nonce', '');
        if ($podcast_meta_nonce === '' || !wp_verify_nonce($podcast_meta_nonce, 'prorank_podcast_meta')) {
            return;
        }
        
        // Check autosave
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        
        // Check permissions
        if (!current_user_can('edit_post', $post_id)) {
            return;
        }
        
        // Save audio URL
        if (prorank_has_input($_POST, 'prorank_podcast_audio_url')) {
            update_post_meta($post_id, '_prorank_podcast_audio_url', esc_url_raw((string) prorank_get_input_value($_POST, 'prorank_podcast_audio_url', '')));
        }
        
        // Save duration
        if (prorank_has_input($_POST, 'prorank_podcast_duration')) {
            update_post_meta($post_id, '_prorank_podcast_duration', prorank_get_post_var('prorank_podcast_duration'));
        }
        
        // Save episode number
        if (prorank_has_input($_POST, 'prorank_podcast_episode_number')) {
            update_post_meta($post_id, '_prorank_podcast_episode_number', intval(prorank_get_post_var('prorank_podcast_episode_number')));
        }
        
        // Save season number
        if (prorank_has_input($_POST, 'prorank_podcast_season_number')) {
            update_post_meta($post_id, '_prorank_podcast_season_number', intval(prorank_get_post_var('prorank_podcast_season_number')));
        }
        
        // Save episode type
        if (prorank_has_input($_POST, 'prorank_podcast_episode_type')) {
            update_post_meta($post_id, '_prorank_podcast_episode_type', prorank_get_post_var('prorank_podcast_episode_type'));
        }
        
        // Save explicit flag
        $explicit = prorank_has_input($_POST, 'prorank_podcast_explicit') ? '1' : '0';
        update_post_meta($post_id, '_prorank_podcast_explicit', $explicit);
        
        // Save description
        if (prorank_has_input($_POST, 'prorank_podcast_description')) {
            update_post_meta(
                $post_id,
                '_prorank_podcast_description',
                sanitize_textarea_field((string) prorank_get_input_value($_POST, 'prorank_podcast_description', ''))
            );
        }
        
        // Save show notes
        if (prorank_has_input($_POST, 'prorank_podcast_show_notes')) {
            update_post_meta($post_id, '_prorank_podcast_show_notes', wp_kses_post((string) prorank_get_input_value($_POST, 'prorank_podcast_show_notes', '')));
        }
        
        // Save transcript
        if (prorank_has_input($_POST, 'prorank_podcast_transcript')) {
            update_post_meta($post_id, '_prorank_podcast_transcript', wp_kses_post((string) prorank_get_input_value($_POST, 'prorank_podcast_transcript', '')));
        }

        // Generate stable episode GUID if not exists
        $existing_guid = get_post_meta($post_id, '_prorank_podcast_guid', true);
        if (empty($existing_guid)) {
            $episode_guid = $this->generate_episode_guid($post_id);
            update_post_meta($post_id, '_prorank_podcast_guid', $episode_guid);
        }
    }

    /**
     * Generate a stable episode GUID (UUIDv5 based on post ID and site URL)
     *
     * @param int $post_id Post ID.
     * @return string UUID string.
     */
    private function generate_episode_guid(int $post_id): string {
        // Use podcast namespace UUID for episodes
        $namespace = 'ead4c236-bf58-58c6-a2c6-a6b28d128cb6';
        $unique_string = home_url('/') . '/podcast/episode/' . $post_id;
        return $this->uuid_v5($namespace, $unique_string);
    }

    /**
     * Add custom columns to podcast list
     *
     * @param array $columns Existing columns.
     * @return array Modified columns.
     */
    public function add_podcast_columns(array $columns): array {
        $new_columns = [];
        
        foreach ($columns as $key => $value) {
            $new_columns[$key] = $value;
            
            // Add after title
            if ($key === 'title') {
                $new_columns['episode_number'] = __('Episode', 'prorank-seo');
                $new_columns['duration'] = __('Duration', 'prorank-seo');
            }
        }
        
        return $new_columns;
    }
    
    /**
     * Render custom podcast columns
     *
     * @param string $column  Column name.
     * @param int    $post_id Post ID.
     * @return void
     */
    public function render_podcast_columns(string $column, int $post_id): void {
        switch ($column) {
            case 'episode_number':
                $episode = absint( get_post_meta( $post_id, '_prorank_podcast_episode_number', true ) );
                $season  = absint( get_post_meta( $post_id, '_prorank_podcast_season_number', true ) );
                
                if ($season && $episode) {
                    echo esc_html( sprintf( 'S%d E%d', $season, $episode ) );
                } elseif ($episode) {
                    echo esc_html( sprintf( 'E%d', $episode ) );
                } else {
                    echo '—';
                }
                break;
                
            case 'duration':
                $duration = get_post_meta($post_id, '_prorank_podcast_duration', true);
                echo $duration ? esc_html($duration) : '—';
                break;
        }
    }
    
    /**
     * Get remote file size
     *
     * @param string $url File URL.
     * @return int File size in bytes.
     */
    private function get_remote_file_size(string $url): int {
        $headers = get_headers($url, true);
        
        if (isset($headers['Content-Length'])) {
            return intval($headers['Content-Length']);
        }
        
        // Default to 0 if we can't determine size
        return 0;
    }
    
    /**
     * Get audio MIME type from URL
     *
     * @param string $url Audio URL.
     * @return string MIME type.
     */
    private function get_audio_mime_type(string $url): string {
        $extension = strtolower(pathinfo(wp_parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
        
        $mime_types = [
            'mp3' => 'audio/mpeg',
            'm4a' => 'audio/mp4',
            'wav' => 'audio/wav',
            'ogg' => 'audio/ogg',
        ];
        
        return $mime_types[$extension] ?? 'audio/mpeg';
    }
    
    /**
     * Format duration to ISO 8601
     *
     * @param string $duration Duration in HH:MM:SS format.
     * @return string ISO 8601 duration.
     */
    private function format_duration_iso8601(string $duration): string {
        $parts = explode(':', $duration);
        $hours = intval($parts[0] ?? 0);
        $minutes = intval($parts[1] ?? 0);
        $seconds = intval($parts[2] ?? 0);
        
        $iso_duration = 'PT';
        
        if ($hours > 0) {
            $iso_duration .= $hours . 'H';
        }
        if ($minutes > 0) {
            $iso_duration .= $minutes . 'M';
        }
        if ($seconds > 0) {
            $iso_duration .= $seconds . 'S';
        }
        
        return $iso_duration;
    }
    
    /**
     * Get default podcast settings
     *
     * @return array
     */
    public function get_default_settings(): array {
        return [
            // Basic Information
            'podcast_title' => get_bloginfo('name') . ' Podcast',
            'podcast_subtitle' => '',
            'podcast_description' => get_bloginfo('description'),
            'podcast_author' => get_bloginfo('name'),
            'podcast_email' => get_option('admin_email'),
            'podcast_copyright' => '© ' . wp_date('Y') . ' ' . get_bloginfo('name'),

            // Podcast Details
            'podcast_image' => '',
            'podcast_category' => 'Technology',
            'podcast_subcategory' => '',
            'podcast_explicit' => 'no',
            'podcast_language' => str_replace('_', '-', get_locale()),

            // Owner Information (Apple Podcasts requirement)
            'podcast_owner_name' => get_bloginfo('name'),
            'podcast_owner_email' => get_option('admin_email'),

            // RSS Feed Configuration
            'enable_podcast_feed' => true,
            'podcast_feed_limit' => 50,
            'enable_itunes_tags' => true,
            'podcast_redirect_feed' => '',

            // Free podcast enhancements
            'enable_transcripts' => false,
            'funding_url' => '',
            'podcast_funding_text' => 'Support the Show',

            // Podcast Status Tags (2025)
            'podcast_complete' => 'no',
            'podcast_locked' => 'no',
            'podcast_block' => 'no',
            'podcast_guid' => '',

            // Podcast Type
            'podcast_type' => 'episodic',
            'podcast_medium' => 'audio',

            // Trailer Support
            'podcast_trailer_url' => '',
            'podcast_trailer_title' => '',
            'podcast_trailer_pubdate' => '',
            'podcast_trailer_duration' => '',

            // Schema Settings
            'advanced_schema_enabled' => false,
            'schema_person_data' => '',
            'schema_organization_data' => '',
        ];
    }

    /**
     * Register podcast settings
     *
     * @return void
     */
    protected function register_settings(): void {
        // Ensure default settings exist in the unified option
        $existing = get_option('prorank_seo_podcast', []);
        if (empty($existing)) {
            update_option('prorank_seo_podcast', $this->get_default_settings());
        }
    }
    
    /**
     * Register REST API routes for podcast settings
     *
     * @return void
     */
    public function register_rest_routes(): void {
        register_rest_route('prorank-seo/v1', '/podcast/settings', [
            [
                'methods' => \WP_REST_Server::READABLE,
                'callback' => [$this, 'get_podcast_settings'],
                'permission_callback' => [$this, 'rest_permission_check'],
            ],
            [
                'methods' => \WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_podcast_settings'],
                'permission_callback' => [$this, 'rest_permission_check'],
                'args' => [
                    'settings' => [
                        'type' => 'object',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Feed validation endpoint
        register_rest_route('prorank-seo/v1', '/podcast/validate-feed', [
            'methods' => \WP_REST_Server::READABLE,
            'callback' => [$this, 'validate_podcast_feed'],
            'permission_callback' => [$this, 'rest_permission_check'],
        ]);
    }

    /**
     * Validate podcast feed and return issues
     *
     * @param \WP_REST_Request $request Request object.
     * @return \WP_REST_Response
     */
    public function validate_podcast_feed(\WP_REST_Request $request): \WP_REST_Response {
        $settings = get_option('prorank_seo_podcast', []);
        $defaults = $this->get_default_settings();
        $settings = wp_parse_args($settings, $defaults);

        $errors = [];
        $warnings = [];
        $info = [];

        // Check if feed is enabled
        if (empty($settings['enable_podcast_feed'])) {
            $errors[] = [
                'code' => 'feed_disabled',
                'message' => __('Podcast feed is disabled. Enable it to generate your RSS feed.', 'prorank-seo'),
            ];
            return new \WP_REST_Response([
                'success' => false,
                'valid' => false,
                'errors' => $errors,
                'warnings' => $warnings,
                'info' => $info,
            ]);
        }

        // Required fields checks
        if (empty($settings['podcast_title'])) {
            $errors[] = [
                'code' => 'missing_title',
                'message' => __('Podcast title is required for Apple Podcasts.', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_subtitle']) && empty($settings['podcast_description'])) {
            $errors[] = [
                'code' => 'missing_description',
                'message' => __('Podcast description is required for Apple Podcasts.', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_image'])) {
            $errors[] = [
                'code' => 'missing_image',
                'message' => __('Podcast artwork is required. Apple requires 3000x3000px JPG or PNG.', 'prorank-seo'),
            ];
        } else {
            // Validate image dimensions if possible
            $image_id = attachment_url_to_postid($settings['podcast_image']);
            if ($image_id) {
                $image_data = wp_get_attachment_metadata($image_id);
                if ($image_data && isset($image_data['width'], $image_data['height'])) {
                    if ($image_data['width'] < 1400 || $image_data['height'] < 1400) {
                        $warnings[] = [
                            'code' => 'image_too_small',
                            'message' => sprintf(
/* translators: %1$: value, %2$: value */
                                __('Podcast artwork is %1$dx%2$d. Apple recommends 3000x3000px minimum.', 'prorank-seo'),
                                $image_data['width'],
                                $image_data['height']
                            ),
                        ];
                    }
                    if ($image_data['width'] !== $image_data['height']) {
                        $warnings[] = [
                            'code' => 'image_not_square',
                            'message' => __('Podcast artwork should be square (1:1 aspect ratio).', 'prorank-seo'),
                        ];
                    }
                }
            }
        }

        if (empty($settings['podcast_author'])) {
            $warnings[] = [
                'code' => 'missing_author',
                'message' => __('Podcast author/host name is recommended.', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_owner_name']) || empty($settings['podcast_owner_email'])) {
            $errors[] = [
                'code' => 'missing_owner',
                'message' => __('Owner name and email are required by Apple Podcasts.', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_category'])) {
            $errors[] = [
                'code' => 'missing_category',
                'message' => __('Podcast category is required for Apple Podcasts.', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_language'])) {
            $warnings[] = [
                'code' => 'missing_language',
                'message' => __('Podcast language is recommended (e.g., en-US).', 'prorank-seo'),
            ];
        }

        if (empty($settings['podcast_explicit'])) {
            $warnings[] = [
                'code' => 'missing_explicit',
                'message' => __('Explicit content flag should be set (yes or no).', 'prorank-seo'),
            ];
        }

        // Podcast 2.0 checks
        if (empty($settings['podcast_guid'])) {
            $warnings[] = [
                'code' => 'missing_guid',
                'message' => __('Podcast GUID is recommended for Podcast 2.0 compliance.', 'prorank-seo'),
            ];
        }

        // Check for episodes
        $episode_count = wp_count_posts('prorank_podcast');
        $published_episodes = isset($episode_count->publish) ? (int) $episode_count->publish : 0;

        if ($published_episodes === 0) {
            $warnings[] = [
                'code' => 'no_episodes',
                'message' => __('No published podcast episodes found. Add episodes before submitting to directories.', 'prorank-seo'),
            ];
        } else {
            $info[] = [
                'code' => 'episode_count',
                'message' => sprintf(
/* translators: %1$: value */
                    _n('%1$d published episode found.', '%d published episodes found.', $published_episodes, 'prorank-seo'),
                    $published_episodes
                ),
            ];

            // Check episodes for required fields
            $episodes_without_audio = 0;
            $episodes_without_duration = 0;
            $episodes = get_posts([
                'post_type' => 'prorank_podcast',
                'posts_per_page' => 50,
                'post_status' => 'publish',
            ]);

            $invalid_audio_urls = [];
            $check_enclosures = $request->get_param('check_enclosures') !== false;

            foreach ($episodes as $episode) {
                $audio_url = get_post_meta($episode->ID, '_prorank_podcast_audio_url', true);
                $duration = get_post_meta($episode->ID, '_prorank_podcast_duration', true);

                if (empty($audio_url)) {
                    $episodes_without_audio++;
                } elseif ($check_enclosures && count($invalid_audio_urls) < 5) {
                    // Validate audio URL accessibility via HEAD request (limit to first 5)
                    $head_response = wp_remote_head($audio_url, [
                        'timeout' => 5,
                        'redirection' => 3,
                    ]);

                    if (is_wp_error($head_response)) {
                        $invalid_audio_urls[] = [
                            'episode' => $episode->post_title,
                            'url' => $audio_url,
                            'error' => $head_response->get_error_message(),
                        ];
                    } else {
                        $status_code = wp_remote_retrieve_response_code($head_response);
                        if ($status_code !== 200) {
                            $invalid_audio_urls[] = [
                                'episode' => $episode->post_title,
                                'url' => $audio_url,
/* translators: %1$: value */
                                'error' => sprintf(__('HTTP %1$d response', 'prorank-seo'), $status_code),
                            ];
                        }
                    }
                }

                if (empty($duration)) {
                    $episodes_without_duration++;
                }
            }

            if ($episodes_without_audio > 0) {
                $errors[] = [
                    'code' => 'episodes_missing_audio',
                    'message' => sprintf(
/* translators: %1$: value */
                        _n('%1$d episode is missing audio URL.', '%d episodes are missing audio URLs.', $episodes_without_audio, 'prorank-seo'),
                        $episodes_without_audio
                    ),
                ];
            }

            if ($episodes_without_duration > 0) {
                $warnings[] = [
                    'code' => 'episodes_missing_duration',
                    'message' => sprintf(
/* translators: %1$: value */
                        _n('%1$d episode is missing duration.', '%d episodes are missing duration.', $episodes_without_duration, 'prorank-seo'),
                        $episodes_without_duration
                    ),
                ];
            }

            // Report invalid audio URLs
            if (!empty($invalid_audio_urls)) {
                foreach ($invalid_audio_urls as $invalid) {
                    $errors[] = [
                        'code' => 'invalid_audio_url',
                        'message' => sprintf(
                            /* translators: 1: Episode title, 2: Error message */
                            __('Episode "%1$s" has inaccessible audio file: %2$s', 'prorank-seo'),
                            $invalid['episode'],
                            $invalid['error']
                        ),
                    ];
                }
            }
        }

        // Feature recommendations
        if (empty($settings['enable_transcripts'])) {
            $info[] = [
                'code' => 'transcripts_disabled',
                'message' => __('Enable transcripts for better accessibility and SEO.', 'prorank-seo'),
            ];
        }

        if (empty($settings['funding_url'])) {
            $info[] = [
                'code' => 'no_funding',
                'message' => __('Add a funding URL to enable listener support.', 'prorank-seo'),
            ];
        }

        // Feed URL info
        $feed_url = get_feed_link('podcast');
        $feed_response = wp_remote_head($feed_url, [
            'timeout' => 5,
            'redirection' => 3,
        ]);
        if (is_wp_error($feed_response)) {
            $errors[] = [
                'code' => 'feed_unreachable',
                'message' => sprintf(
                    __('The configured podcast feed URL could not be reached: %s', 'prorank-seo'),
                    $feed_response->get_error_message()
                ),
            ];
        } else {
            $feed_status = wp_remote_retrieve_response_code($feed_response);
            if ($feed_status !== 200) {
                $errors[] = [
                    'code' => 'feed_unreachable',
                    'message' => sprintf(
                        __('The configured podcast feed URL returned HTTP %d.', 'prorank-seo'),
                        $feed_status
                    ),
                ];
            }
        }
        $info[] = [
            'code' => 'feed_url',
            'message' => $feed_url,
        ];

        return new \WP_REST_Response([
            'success' => true,
            'valid' => empty($errors),
            'errors' => $errors,
            'warnings' => $warnings,
            'info' => $info,
            'feed_url' => $feed_url,
            'episode_count' => $published_episodes,
        ]);
    }
    
    /**
     * Get podcast settings via REST API
     *
     * @param \WP_REST_Request $request Request object.
     * @return \WP_REST_Response
     */
    public function get_podcast_settings(\WP_REST_Request $request): \WP_REST_Response {
        $defaults = $this->get_default_settings();
        $saved = get_option('prorank_seo_podcast', []);
        $settings = wp_parse_args($saved, $defaults);

        $settings = $this->strip_premium_podcast_settings($settings);

        // Auto-generate GUID if not set (2025 Podcast 2.0 requirement)
        if (empty($settings['podcast_guid'])) {
            $settings['podcast_guid'] = $this->generate_podcast_guid();
            $saved = $this->strip_premium_podcast_settings($saved);
            $saved['podcast_guid'] = $settings['podcast_guid'];
            update_option('prorank_seo_podcast', $saved);
        }

        $settings['feed_url'] = get_feed_link('podcast');

        return new \WP_REST_Response([
            'success' => true,
            'data' => $settings,
        ]);
    }

    /**
     * Update podcast settings via REST API
     *
     * @param \WP_REST_Request $request Request object.
     * @return \WP_REST_Response
     */
    public function update_podcast_settings(\WP_REST_Request $request): \WP_REST_Response {
        $new_settings = $request->get_param('settings');

        if (!is_array($new_settings)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Invalid settings format.', 'prorank-seo'),
            ], 400);
        }

        // Get existing settings
        $existing = get_option('prorank_seo_podcast', []);

        // Sanitize and merge settings
        $sanitized = $this->sanitize_podcast_settings($new_settings);
        $updated = array_merge($existing, $sanitized);
        $updated = $this->strip_premium_podcast_settings($updated);

        // Ensure GUID is set
        if (empty($updated['podcast_guid'])) {
            $updated['podcast_guid'] = $this->generate_podcast_guid();
        }

        // Save to unified option
        update_option('prorank_seo_podcast', $updated);
        update_option('prorank_podcast_cpt_flushed', false);
        flush_rewrite_rules();

        $updated['feed_url'] = get_feed_link('podcast');

        return new \WP_REST_Response([
            'success' => true,
            'message' => __('Podcast settings updated successfully.', 'prorank-seo'),
            'data' => $updated,
        ]);
    }

    /**
     * Sanitize podcast settings
     *
     * @param array $settings Raw settings.
     * @return array Sanitized settings.
     */
    private function sanitize_podcast_settings(array $settings): array {
        $sanitized = [];

        // Boolean fields
        $bool_fields = [
            'enable_podcast_feed',
            'enable_itunes_tags',
            'enable_transcripts',
            'advanced_schema_enabled',
        ];

        foreach ($bool_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = (bool) $settings[$field];
            }
        }

        // Text fields
        $text_fields = [
            'podcast_title', 'podcast_author', 'podcast_category',
            'podcast_subcategory', 'podcast_explicit', 'podcast_language',
            'podcast_copyright', 'podcast_owner_name', 'podcast_type',
            'podcast_medium', 'podcast_complete', 'podcast_locked',
            'podcast_block', 'podcast_trailer_title', 'podcast_funding_text',
            'podcast_guid'
        ];

        foreach ($text_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = sanitize_text_field($settings[$field]);
            }
        }

        // Textarea fields
        $textarea_fields = [
            'podcast_subtitle', 'podcast_description',
            'schema_person_data', 'schema_organization_data'
        ];

        foreach ($textarea_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = sanitize_textarea_field($settings[$field]);
            }
        }

        // Email fields
        $email_fields = ['podcast_email', 'podcast_owner_email'];
        foreach ($email_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = sanitize_email($settings[$field]);
            }
        }

        // URL fields
        $url_fields = [
            'podcast_image', 'podcast_redirect_feed', 'funding_url',
            'podcast_trailer_url'
        ];
        foreach ($url_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = esc_url_raw($settings[$field]);
            }
        }

        // Integer fields
        $int_fields = [
            'podcast_feed_limit', 'podcast_spotify_limit', 'podcast_value_suggested'
        ];
        foreach ($int_fields as $field) {
            if (isset($settings[$field])) {
                $sanitized[$field] = absint($settings[$field]);
            }
        }

        // Date/time field
        if (isset($settings['podcast_trailer_pubdate'])) {
            $sanitized['podcast_trailer_pubdate'] = sanitize_text_field($settings['podcast_trailer_pubdate']);
        }
        if (isset($settings['podcast_trailer_duration'])) {
            $sanitized['podcast_trailer_duration'] = sanitize_text_field($settings['podcast_trailer_duration']);
        }

        return $sanitized;
    }

    /**
     * Remove premium-only podcast keys from the free option payload.
     *
     * @param array $settings Podcast settings.
     * @return array
     */
    private function strip_premium_podcast_settings(array $settings): array {
        unset(
            $settings['enable_spotify_tags'],
            $settings['enable_youtube_tags'],
            $settings['enable_chapters'],
            $settings['enable_video_podcasts'],
            $settings['enable_value4value'],
            $settings['lightning_address'],
            $settings['podcast_value_recipient'],
            $settings['podcast_value_type'],
            $settings['podcast_value_method'],
            $settings['podcast_value_suggested'],
            $settings['podcast_spotify_limit'],
            $settings['podcast_spotify_country'],
            $settings['podcast_youtube_channelid']
        );

        return $settings;
    }

    /**
     * Generate podcast GUID (UUIDv5 per Podcast 2.0 specification)
     *
     * @return string UUID
     */
    private function generate_podcast_guid(): string {
        $feed_url = get_feed_link('podcast');
        $namespace = 'ead4c236-bf58-58c6-a2c6-a6b28d128cb6'; // Podcast namespace UUID
        return $this->uuid_v5($namespace, $feed_url);
    }

    /**
     * Generate UUIDv5
     *
     * @param string $namespace Namespace UUID.
     * @param string $name Name to hash.
     * @return string UUID
     */
    private function uuid_v5(string $namespace, string $name): string {
        // Convert namespace to binary
        $ns_hex = str_replace(['-', '{', '}'], '', $namespace);
        $ns_bytes = hex2bin($ns_hex);

        // Hash namespace + name
        $hash = sha1($ns_bytes . $name);

        // Format as UUID
        return sprintf(
            '%s-%s-%s-%s-%s',
            substr($hash, 0, 8),
            substr($hash, 8, 4),
            '5' . substr($hash, 13, 3), // Version 5
            dechex((hexdec(substr($hash, 16, 2)) & 0x3f) | 0x80) . substr($hash, 18, 2),
            substr($hash, 20, 12)
        );
    }
    
    /**
     * Check REST API permissions
     *
     * @return bool
     */
    public function rest_permission_check(): bool {
        return current_user_can('manage_options');
    }
}
