<?php
/**
 * Rank Forge — Video SEO
 *
 * VideoObject schema, auto-detection of YouTube/Vimeo embeds,
 * manual video fields in metabox, video sitemap.
 *
 * PRO feature.
 *
 * @package RankForge
 * @since 1.2.0
 */

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

class RANKFORGE_Video_Seo {
    private static $instance = null;
    public static function instance() { if (!self::$instance) self::$instance = new self(); return self::$instance; }

    private function __construct() {
        add_action('save_post', [$this, 'auto_detect_videos'], 18, 2);
        add_action('wp_head', [$this, 'output_video_schema'], 6);
    }

    /**
 * Auto-detect video embeds in post content.
 */
    public function auto_detect_videos($post_id, $post) {
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
        if ($post->post_status !== 'publish') return;

        $content = $post->post_content;
        $videos = [];

        // YouTube
        if (preg_match_all('/(?:youtube\.com\/(?:embed\/|watch\?v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $content, $matches)) {
            foreach (array_unique($matches[1]) as $id) {
                $videos[] = [
                    'platform'  => 'youtube',
                    'id'        => $id,
                    'url'       => 'https://www.youtube.com/watch?v=' . $id,
                    'embed_url' => 'https://www.youtube.com/embed/' . $id,
                    'thumbnail' => 'https://img.youtube.com/vi/' . $id . '/maxresdefault.jpg',
                ];
            }
        }

        // Vimeo
        if (preg_match_all('/(?:vimeo\.com\/|player\.vimeo\.com\/video\/)(\d+)/', $content, $matches)) {
            foreach (array_unique($matches[1]) as $id) {
                $videos[] = [
                    'platform'  => 'vimeo',
                    'id'        => $id,
                    'url'       => 'https://vimeo.com/' . $id,
                    'embed_url' => 'https://player.vimeo.com/video/' . $id,
                    'thumbnail' => '', // Vimeo requires API call for thumbnail
                ];
            }
        }

        // HTML5 <video>
        if (preg_match_all('/<video[^>]*src=["\']([^"\']+)["\']/', $content, $matches)) {
            foreach (array_unique($matches[1]) as $src) {
                $videos[] = [
                    'platform'  => 'html5',
                    'id'        => md5($src),
                    'url'       => $src,
                    'embed_url' => $src,
                    'thumbnail' => '',
                ];
            }
        }

        if (!empty($videos)) {
            update_post_meta($post_id, '_rankforge_videos', wp_json_encode($videos));
        } else {
            delete_post_meta($post_id, '_rankforge_videos');
        }
    }

    /**
 * Output VideoObject JSON-LD schema.
 */
    public function output_video_schema() {
        if (!is_singular()) return;

        $post_id = get_the_ID();
        $post = get_post($post_id);
        if (!$post) return;

        // Manual video takes priority
        $video_url = get_post_meta($post_id, '_rankforge_video_url', true);
        $video_thumb = get_post_meta($post_id, '_rankforge_video_thumbnail', true);
        $video_duration = get_post_meta($post_id, '_rankforge_video_duration', true);
        $video_desc = get_post_meta($post_id, '_rankforge_video_description', true);

        // Fallback to auto-detected
        if (empty($video_url)) {
            $detected = json_decode(get_post_meta($post_id, '_rankforge_videos', true), true);
            if (empty($detected)) return;

            $first = $detected[0];
            $video_url = $first['url'];
            $video_thumb = $first['thumbnail'] ?: get_the_post_thumbnail_url($post_id, 'full');
            $video_desc = get_the_excerpt($post);
        }

        if (empty($video_url)) return;

        $schema = [
            '@context' => 'https://schema.org',
            '@type' => 'VideoObject',
            'name' => get_the_title($post_id),
            'description' => $video_desc ?: get_the_excerpt($post),
            'thumbnailUrl' => $video_thumb ?: '',
            'uploadDate' => get_the_date('c', $post_id),
            'contentUrl' => $video_url,
        ];

        if ($video_duration) {
            $schema['duration'] = $video_duration; // ISO 8601, e.g., PT1M30S
        }

        // Embed URL for YouTube/Vimeo
        $detected = json_decode(get_post_meta($post_id, '_rankforge_videos', true), true);
        if (!empty($detected[0]['embed_url'])) {
            $schema['embedUrl'] = $detected[0]['embed_url'];
        }

        echo '<script type="application/ld+json">' . wp_json_encode($schema, JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE) . '</script>' . "\n";
    }

    /**
 * Render video fields in analyzer metabox.
 */
    public function render_metabox_fields($post) {
        $video_url = esc_attr(get_post_meta($post->ID, '_rankforge_video_url', true));
        $video_thumb = esc_attr(get_post_meta($post->ID, '_rankforge_video_thumbnail', true));
        $video_duration = esc_attr(get_post_meta($post->ID, '_rankforge_video_duration', true));
        $video_desc = esc_textarea(get_post_meta($post->ID, '_rankforge_video_description', true));
        $detected = json_decode(get_post_meta($post->ID, '_rankforge_videos', true), true) ?: [];
        $count = count($detected);
        ?>
        <details style="margin-top:12px;border:1px solid #E5E7EB;border-radius:8px;overflow:hidden;">
            <summary style="padding:10px 12px;background:#FFF7ED;cursor:pointer;font-size:14px;font-weight:600;color:#1A1A2E;">
                <?php esc_html_e('Video SEO', 'rankforge'); ?><?php if (class_exists('Forge_Docs_Link')) Forge_Docs_Link::render('rankforge', 'schema-markup-rich-results-in-google', __('Video SEO', 'rankforge'), __('Attach URL, thumbnail, duration so Google renders VideoObject rich results for this post.', 'rankforge')); ?>
                <?php if ($count): ?>
                    <span style="background:#FF6B35;color:#fff;padding:1px 6px;border-radius:10px;font-size:14px;margin-left:4px;"><?php echo esc_html( $count ); ?> <?php esc_html_e('detected', 'rankforge'); ?></span>
                <?php endif; ?>
            </summary>
            <div style="padding:12px;">
                <div style="margin-bottom:8px;">
                    <label style="display:block;font-size:14px;font-weight:600;color:#6B7280;margin-bottom:3px;"><?php esc_html_e('Video URL', 'rankforge'); ?></label>
                    <input type="url" name="rankforge_video_url" value="<?php echo esc_url( $video_url ); ?>" placeholder="https://youtube.com/watch?v=..." style="width:100%;padding:6px 8px;border:1px solid #E5E7EB;border-radius:6px;font-size:14px;">
                </div>
                <div style="margin-bottom:8px;">
                    <label style="display:block;font-size:14px;font-weight:600;color:#6B7280;margin-bottom:3px;"><?php esc_html_e('Thumbnail URL', 'rankforge'); ?></label>
                    <input type="url" name="rankforge_video_thumbnail" value="<?php echo esc_url( $video_thumb ); ?>" placeholder="https://..." style="width:100%;padding:6px 8px;border:1px solid #E5E7EB;border-radius:6px;font-size:14px;">
                </div>
                <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px;">
                    <div>
                        <label style="display:block;font-size:14px;font-weight:600;color:#6B7280;margin-bottom:3px;"><?php esc_html_e('Duration (ISO 8601)', 'rankforge'); ?></label>
                        <input type="text" name="rankforge_video_duration" value="<?php echo esc_attr( $video_duration ); ?>" placeholder="PT5M30S" style="width:100%;padding:6px 8px;border:1px solid #E5E7EB;border-radius:6px;font-size:14px;">
                    </div>
                    <div>
                        <label style="display:block;font-size:14px;font-weight:600;color:#6B7280;margin-bottom:3px;"><?php esc_html_e('Auto-detected', 'rankforge'); ?></label>
                        <input type="text" value="<?php echo esc_attr( $count ? $count . ' video(s)' : 'None' ); ?>" disabled style="width:100%;padding:6px 8px;border:1px solid #E5E7EB;border-radius:6px;font-size:14px;background:#F8F9FC;">
                    </div>
                </div>
                <div>
                    <label style="display:block;font-size:14px;font-weight:600;color:#6B7280;margin-bottom:3px;"><?php esc_html_e('Video Description', 'rankforge'); ?></label>
                    <textarea name="rankforge_video_description" rows="2" placeholder="<?php esc_attr_e('Short description of the video...', 'rankforge'); ?>" style="width:100%;padding:6px 8px;border:1px solid #E5E7EB;border-radius:6px;font-size:14px;resize:vertical;"><?php echo esc_textarea( $video_desc ); ?></textarea>
                </div>
                <p style="margin:6px 0 0;font-size:14px;color:#6B7280;"><?php esc_html_e('Leave empty to use auto-detected video data. Manual entries take priority.', 'rankforge'); ?></p>
            </div>
        </details>
        <?php
    }

    /**
 * Save video meta fields (called from analyzer save handler).
 */
    public function save_video_meta($post_id) {
        // Nonce verified in RANKFORGE_Analyzer::auto_analyze before this method
        // is called (wp_verify_nonce of $_POST['rankforge_meta_nonce'] /
        // 'rankforge_save_meta'); capability checked there too.
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in RANKFORGE_Analyzer::auto_analyze caller.
        if (isset($_POST['rankforge_video_url'])) {
            update_post_meta($post_id, '_rankforge_video_url', esc_url_raw(wp_unslash($_POST['rankforge_video_url'])));
        }
        if (isset($_POST['rankforge_video_thumbnail'])) {
            update_post_meta($post_id, '_rankforge_video_thumbnail', esc_url_raw(wp_unslash($_POST['rankforge_video_thumbnail'])));
        }
        if (isset($_POST['rankforge_video_duration'])) {
            update_post_meta($post_id, '_rankforge_video_duration', sanitize_text_field(wp_unslash($_POST['rankforge_video_duration'])));
        }
        if (isset($_POST['rankforge_video_description'])) {
            update_post_meta($post_id, '_rankforge_video_description', sanitize_textarea_field(wp_unslash($_POST['rankforge_video_description'])));
        }
        // phpcs:enable WordPress.Security.NonceVerification.Missing
    }

    /**
 * Generate video sitemap XML.
 */
    public function generate_video_sitemap_xml() {
        $cached = get_transient('rankforge_video_sitemap');
        if ($cached) return $cached;

        global $wpdb;
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- own table from $wpdb->prefix; one-time/admin read
        $posts = $wpdb->get_results(
            "SELECT p.ID, p.post_title, p.post_modified_gmt
             FROM {$wpdb->posts} p
             INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_rankforge_videos'
             WHERE p.post_status = 'publish' AND pm.meta_value != ''
             ORDER BY p.post_modified_gmt DESC
             LIMIT 1000"
        );

        $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' . "\n";
        $xml .= '        xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">' . "\n";

        foreach ($posts as $post) {
            if (RANKFORGE_Robots_Meta::is_noindex($post->ID)) continue;

            $videos = json_decode(get_post_meta($post->ID, '_rankforge_videos', true), true);
            if (empty($videos)) continue;

            $video = $videos[0]; // First video

            // Google requires <video:title>, <video:description> and
            // <video:thumbnail_loc>. Fall back to the post excerpt/title for the
            // description and the featured image for the thumbnail.
            $description = !empty($video['description'])
                ? $video['description']
                : ($post->post_excerpt !== '' ? $post->post_excerpt : $post->post_title);
            $thumbnail = !empty($video['thumbnail'])
                ? $video['thumbnail']
                : (string) get_the_post_thumbnail_url($post->ID, 'full');

            $xml .= '<url>' . "\n";
            $xml .= '  <loc>' . esc_url(get_permalink($post->ID)) . '</loc>' . "\n";
            $xml .= '  <video:video>' . "\n";
            $xml .= '    <video:title>' . esc_xml($post->post_title) . '</video:title>' . "\n";
            $xml .= '    <video:description>' . esc_xml(wp_strip_all_tags($description)) . '</video:description>' . "\n";
            if ($thumbnail) {
                $xml .= '    <video:thumbnail_loc>' . esc_url($thumbnail) . '</video:thumbnail_loc>' . "\n";
            }
            $xml .= '    <video:content_loc>' . esc_url($video['url']) . '</video:content_loc>' . "\n";
            $xml .= '  </video:video>' . "\n";
            $xml .= '</url>' . "\n";
        }

        $xml .= '</urlset>';

        set_transient('rankforge_video_sitemap', $xml, HOUR_IN_SECONDS);
        return $xml;
    }
}

/** Helper: escape XML special chars */
if (!function_exists('esc_xml')) {
    // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- intentional ACF/WPML/core compatibility
    function esc_xml($str) {
        return htmlspecialchars($str, ENT_XML1 | ENT_QUOTES, 'UTF-8');
    }
}
