<?php

namespace WPSN\Modules\Social;

use WPSN\Core\AbstractModule;
use WPSN\Core\Branding;
use WPSN\SEO\DocumentResolver;
use WPSN\SEO\MetaKeys;
use WPSN\SEO\SeoMetaRepository;

defined('ABSPATH') || exit;

final class SocialModule extends AbstractModule
{
    public function id(): string
    {
        return 'social';
    }

    public function name(): string
    {
        return __('Social', 'wp-seo-next');
    }

    public function description(): string
    {
        return __('OpenGraph + Twitter Cards (with overrides).', 'wp-seo-next');
    }

    public function defaultEnabled(): bool
    {
        return true;
    }

    public function register(): void
    {
        // Storage / UI
        add_action('init', [$this, 'registerMetaKeys']);
        add_action('init', [$this, 'registerTermMetaKeys']);
        add_action('init', [$this, 'registerTermUiHooks'], 20);
        add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets']);
        add_action('add_meta_boxes', [$this, 'addMetaBox']);
        add_action('save_post', [$this, 'saveMetaBox'], 10, 2);

        // Front-end output
        add_action('wp_head', [$this, 'render'], 6);
    }

    public function registerMetaKeys(): void
    {
        // Register meta for all public post types (except attachments).
        $postTypes = get_post_types(['public' => true], 'names');
        foreach ($postTypes as $pt) {
            if ($pt === 'attachment') {
                continue;
            }

            register_post_meta($pt, MetaKeys::OG_TITLE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_text_field',
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);

            register_post_meta($pt, MetaKeys::OG_DESCRIPTION, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_textarea_field',
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);

            register_post_meta($pt, MetaKeys::OG_IMAGE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => [self::class, 'sanitizeOgImageUrl'],
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);

            // Twitter overrides
            register_post_meta($pt, MetaKeys::TWITTER_TITLE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_text_field',
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);

            register_post_meta($pt, MetaKeys::TWITTER_DESCRIPTION, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_textarea_field',
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);

            register_post_meta($pt, MetaKeys::TWITTER_IMAGE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => [self::class, 'sanitizeOgImageUrl'],
                'auth_callback'     => static function (bool $allowed, string $metaKey, int $postId, ...$rest): bool {
                    return current_user_can('edit_post', $postId);
                },
            ]);
        }
    }

    public function registerTermMetaKeys(): void
    {
        $taxonomies = get_taxonomies(['public' => true], 'names');
        foreach ($taxonomies as $taxonomy) {
            $taxonomy = sanitize_key((string) $taxonomy);
            if ($taxonomy === '') {
                continue;
            }

            $authCb = static function (bool $allowed, string $metaKey, int $termId, ...$rest): bool {
                $term = get_term($termId);
                if (!$term || is_wp_error($term)) {
                    return current_user_can('manage_options');
                }
                $tax = get_taxonomy($term->taxonomy);
                if (!$tax) {
                    return current_user_can('manage_options');
                }
                return current_user_can($tax->cap->edit_terms);
            };

            register_term_meta($taxonomy, MetaKeys::OG_TITLE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_text_field',
                'auth_callback'     => $authCb,
            ]);

            register_term_meta($taxonomy, MetaKeys::OG_DESCRIPTION, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_textarea_field',
                'auth_callback'     => $authCb,
            ]);

            register_term_meta($taxonomy, MetaKeys::OG_IMAGE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => [self::class, 'sanitizeOgImageUrl'],
                'auth_callback'     => $authCb,
            ]);

            // Twitter overrides for terms (fallback to OG in SeoMetaRepository).
            register_term_meta($taxonomy, MetaKeys::TWITTER_TITLE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_text_field',
                'auth_callback'     => $authCb,
            ]);

            register_term_meta($taxonomy, MetaKeys::TWITTER_DESCRIPTION, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => 'sanitize_textarea_field',
                'auth_callback'     => $authCb,
            ]);

            register_term_meta($taxonomy, MetaKeys::TWITTER_IMAGE, [
                'type'              => 'string',
                'single'            => true,
                'show_in_rest'      => true,
                'sanitize_callback' => [self::class, 'sanitizeOgImageUrl'],
                'auth_callback'     => $authCb,
            ]);
        }
    }

    public static function sanitizeOgImageUrl($value): string
    {
        $url = is_string($value) ? trim($value) : '';
        if ($url === '') {
            return '';
        }

        // Allow site-relative.
        if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
            $url = home_url($url);
        }

        return esc_url_raw($url);
    }

    public function addMetaBox(): void
    {
        // Classic editor fallback.
        $boxTitle = Branding::labelWithBrand($this->settings, __('Social', 'wp-seo-next'));

        $postTypes = get_post_types(['public' => true], 'names');
        foreach ($postTypes as $pt) {
            if ($pt === 'attachment') {
                continue;
            }

            add_meta_box(
                'wpsn_social_meta',
                $boxTitle,
                [$this, 'renderMetaBox'],
                $pt,
                'normal',
                'default'
            );
        }
    }

    public function renderMetaBox(\WP_Post $post): void
    {
        wp_nonce_field('wpsn_save_social_meta', 'wpsn_social_nonce');

        $ogTitle = (string) get_post_meta($post->ID, MetaKeys::OG_TITLE, true);
        $ogDesc  = (string) get_post_meta($post->ID, MetaKeys::OG_DESCRIPTION, true);
        $ogImg   = (string) get_post_meta($post->ID, MetaKeys::OG_IMAGE, true);

        $twTitle = (string) get_post_meta($post->ID, MetaKeys::TWITTER_TITLE, true);
        $twDesc  = (string) get_post_meta($post->ID, MetaKeys::TWITTER_DESCRIPTION, true);
        $twImg   = (string) get_post_meta($post->ID, MetaKeys::TWITTER_IMAGE, true);

        ?>
        <p>
            <label for="wpsn_og_title"><strong><?php echo esc_html__('OpenGraph title', 'wp-seo-next'); ?></strong></label><br/>
            <input id="wpsn_og_title" type="text" class="widefat" name="wpsn_social[og_title]" value="<?php echo esc_attr($ogTitle); ?>" />
            <span class="description"><?php echo esc_html__('Leave empty to use SEO title.', 'wp-seo-next'); ?></span>
        </p>

        <p>
            <label for="wpsn_og_desc"><strong><?php echo esc_html__('OpenGraph description', 'wp-seo-next'); ?></strong></label><br/>
            <textarea id="wpsn_og_desc" class="widefat" rows="3" name="wpsn_social[og_description]"><?php echo esc_textarea($ogDesc); ?></textarea>
            <span class="description"><?php echo esc_html__('Leave empty to use meta description.', 'wp-seo-next'); ?></span>
        </p>

        <div style="margin: 12px 0;">
            <label for="wpsn_og_image"><strong><?php echo esc_html__('OpenGraph image URL', 'wp-seo-next'); ?></strong></label><br/>
            <div class="wpsn-media-field">
                <input id="wpsn_og_image" type="url" class="widefat wpsn-media-url" name="wpsn_social[og_image]" value="<?php echo esc_attr($ogImg); ?>" placeholder="https://..." />
                <p style="margin-top:8px;">
                    <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select OpenGraph image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                    <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                </p>
                <img class="wpsn-media-preview" src="<?php echo esc_url($ogImg); ?>" style="<?php echo $ogImg !== '' ? 'max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;' : 'display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;'; ?>" alt="" />
                <p class="description" style="margin: 6px 0 0;"><?php echo esc_html__('Leave empty to use Featured Image → Default OG image → Site Icon.', 'wp-seo-next'); ?></p>
            </div>
        </div>

        <hr/>
        <h4 style="margin: 12px 0 8px;"><?php echo esc_html__('Twitter Cards', 'wp-seo-next'); ?></h4>

        <p>
            <label for="wpsn_tw_title"><strong><?php echo esc_html__('Twitter title', 'wp-seo-next'); ?></strong></label><br/>
            <input id="wpsn_tw_title" type="text" class="widefat" name="wpsn_social[twitter_title]" value="<?php echo esc_attr($twTitle); ?>" />
            <span class="description"><?php echo esc_html__('Leave empty to use OpenGraph title.', 'wp-seo-next'); ?></span>
        </p>

        <p>
            <label for="wpsn_tw_desc"><strong><?php echo esc_html__('Twitter description', 'wp-seo-next'); ?></strong></label><br/>
            <textarea id="wpsn_tw_desc" class="widefat" rows="3" name="wpsn_social[twitter_description]"><?php echo esc_textarea($twDesc); ?></textarea>
            <span class="description"><?php echo esc_html__('Leave empty to use OpenGraph description.', 'wp-seo-next'); ?></span>
        </p>

        <div style="margin: 12px 0;">
            <label for="wpsn_tw_image"><strong><?php echo esc_html__('Twitter image URL', 'wp-seo-next'); ?></strong></label><br/>
            <div class="wpsn-media-field">
                <input id="wpsn_tw_image" type="url" class="widefat wpsn-media-url" name="wpsn_social[twitter_image]" value="<?php echo esc_attr($twImg); ?>" placeholder="https://..." />
                <p style="margin-top:8px;">
                    <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select Twitter image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                    <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                </p>
                <img class="wpsn-media-preview" src="<?php echo esc_url($twImg); ?>" style="<?php echo $twImg !== '' ? 'max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;' : 'display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;'; ?>" alt="" />
                <p class="description" style="margin: 6px 0 0;"><?php echo esc_html__('Leave empty to use OpenGraph image.', 'wp-seo-next'); ?></p>
            </div>
        </div>
        <?php
    }

    public function saveMetaBox(int $postId, \WP_Post $post): void
    {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        if (wp_is_post_revision($postId)) {
            return;
        }
        if (!isset($_POST['wpsn_social_nonce']) || !wp_verify_nonce((string) wp_unslash($_POST['wpsn_social_nonce']), 'wpsn_save_social_meta')) {
            return;
        }
        if (!current_user_can('edit_post', $postId)) {
            return;
        }

        $incoming = $_POST['wpsn_social'] ?? [];
        $incoming = is_array($incoming) ? wp_unslash($incoming) : [];

        $ogTitle = isset($incoming['og_title']) ? sanitize_text_field((string) $incoming['og_title']) : '';
        $ogDesc  = isset($incoming['og_description']) ? sanitize_textarea_field((string) $incoming['og_description']) : '';
        $ogImg   = isset($incoming['og_image']) ? (string) $incoming['og_image'] : '';
        $ogImg   = self::sanitizeOgImageUrl($ogImg);

        $twTitle = isset($incoming['twitter_title']) ? sanitize_text_field((string) $incoming['twitter_title']) : '';
        $twDesc  = isset($incoming['twitter_description']) ? sanitize_textarea_field((string) $incoming['twitter_description']) : '';
        $twImg   = isset($incoming['twitter_image']) ? (string) $incoming['twitter_image'] : '';
        $twImg   = self::sanitizeOgImageUrl($twImg);

        $this->updateOrDeleteMeta($postId, MetaKeys::OG_TITLE, $ogTitle);
        $this->updateOrDeleteMeta($postId, MetaKeys::OG_DESCRIPTION, $ogDesc);
        $this->updateOrDeleteMeta($postId, MetaKeys::OG_IMAGE, $ogImg);

        $this->updateOrDeleteMeta($postId, MetaKeys::TWITTER_TITLE, $twTitle);
        $this->updateOrDeleteMeta($postId, MetaKeys::TWITTER_DESCRIPTION, $twDesc);
        $this->updateOrDeleteMeta($postId, MetaKeys::TWITTER_IMAGE, $twImg);
    }

    private function updateOrDeleteMeta(int $postId, string $key, string $value): void
    {
        $value = trim($value);
        if ($value === '') {
            delete_post_meta($postId, $key);
            return;
        }
        update_post_meta($postId, $key, $value);
    }

    public function render(): void
    {
        if (is_admin() || is_feed()) {
            return;
        }

        /** @var DocumentResolver $resolver */
        $resolver = $this->container->get('document_resolver');
        $ctx = $resolver->resolve();

        /** @var SeoMetaRepository $repo */
        $repo = $this->container->get('meta_repo');
        $meta = $repo->forContext($ctx);

        $siteName = get_bloginfo('name');
        $canonical = is_string($meta->canonical) && trim($meta->canonical) !== '' ? trim($meta->canonical) : $ctx->url;

        $title = is_string($meta->ogTitle) ? trim($meta->ogTitle) : '';
        $desc  = is_string($meta->ogDescription) ? trim($meta->ogDescription) : '';
        $imageUrl = is_string($meta->ogImage) ? trim($meta->ogImage) : '';

        $twTitle = is_string($meta->twitterTitle) ? trim($meta->twitterTitle) : '';
        $twDesc  = is_string($meta->twitterDescription) ? trim($meta->twitterDescription) : '';
        $twImageUrl = is_string($meta->twitterImage) ? trim($meta->twitterImage) : '';

        $ogType = $ctx->isSingular() ? 'article' : 'website';

        /**
         * Filter OpenGraph type.
         *
         * Useful for integrations (e.g. WooCommerce product pages use og:type=product).
         *
         * @since 0.23.0
         */
        $ogType = apply_filters('wpsn/social/og_type', $ogType, $ctx, $meta);
        $ogType = is_string($ogType) ? trim($ogType) : '';
        if ($ogType === '') {
            $ogType = $ctx->isSingular() ? 'article' : 'website';
        }

        $locale = get_locale();
        $locale = is_string($locale) ? str_replace('-', '_', $locale) : '';

        echo "\n";

        // Optional debug comment (suppressed in white‑label hide-branding mode).
        if (!Branding::hideBranding($this->settings)) {
            $brand = Branding::brand($this->settings);
            $brand = sanitize_text_field($brand);
            // Prevent comment breakouts (e.g. "-->").
            $brand = str_replace(['--', '>', '<'], '', $brand);
            if ($brand !== '') {
                echo "<!-- " . esc_html($brand) . ": Social -->\n";
            }
        }

        if ($locale !== '') {
            echo '<meta property="og:locale" content="' . esc_attr($locale) . '" />' . "\n";
        }

        echo '<meta property="og:type" content="' . esc_attr($ogType) . '" />' . "\n";

        if ($title !== '') {
            echo '<meta property="og:title" content="' . esc_attr($title) . '" />' . "\n";
        }
        if ($desc !== '') {
            echo '<meta property="og:description" content="' . esc_attr($desc) . '" />' . "\n";
        }
        if ($canonical !== '') {
            echo '<meta property="og:url" content="' . esc_url($canonical) . '" />' . "\n";
        }
        if (is_string($siteName) && $siteName !== '') {
            echo '<meta property="og:site_name" content="' . esc_attr($siteName) . '" />' . "\n";
        }
        if ($imageUrl !== '') {
            echo '<meta property="og:image" content="' . esc_url($imageUrl) . '" />' . "\n";
        }

        // Twitter Cards
        $card = $twImageUrl !== '' ? 'summary_large_image' : 'summary';
        echo '<meta name="twitter:card" content="' . esc_attr($card) . '" />' . "\n";
        if ($twTitle !== '') {
            echo '<meta name="twitter:title" content="' . esc_attr($twTitle) . '" />' . "\n";
        }
        if ($twDesc !== '') {
            echo '<meta name="twitter:description" content="' . esc_attr($twDesc) . '" />' . "\n";
        }
        if ($twImageUrl !== '') {
            echo '<meta name="twitter:image" content="' . esc_url($twImageUrl) . '" />' . "\n";
        }

        /**
         * Allow modules to output extra social meta tags.
         *
         * @since 0.23.0
         */
        do_action('wpsn/social/after', $ctx, $meta);
    }

    public function registerTermUiHooks(): void
    {
        if (!is_admin()) {
            return;
        }

        $taxonomies = get_taxonomies(['public' => true], 'names');
        foreach ($taxonomies as $taxonomy) {
            $taxonomy = sanitize_key((string) $taxonomy);
            if ($taxonomy === '') {
                continue;
            }

            add_action($taxonomy . '_add_form_fields', [$this, 'renderTermFieldsAdd']);
            add_action($taxonomy . '_edit_form_fields', [$this, 'renderTermFieldsEdit'], 10, 2);
            add_action('created_' . $taxonomy, [$this, 'saveTermFields'], 10, 2);
            add_action('edited_' . $taxonomy, [$this, 'saveTermFields'], 10, 2);
        }
    }

    public function enqueueAdminAssets(string $hook): void
    {
        if (!is_admin()) {
            return;
        }

        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if (!$screen) {
            return;
        }

        $isTermScreen = in_array($screen->base, ['edit-tags', 'term'], true);
        $isPostScreen = ($screen->base === 'post');

        if (!$isTermScreen && !$isPostScreen) {
            return;
        }

        if ($isTermScreen) {
            // Only for public taxonomies.
            $taxonomy = isset($screen->taxonomy) ? (string) $screen->taxonomy : '';
            if ($taxonomy !== '') {
                $tax = get_taxonomy($taxonomy);
                if (!$tax || empty($tax->public)) {
                    return;
                }
            }
        }

        if ($isPostScreen) {
            $pt = isset($screen->post_type) ? (string) $screen->post_type : '';
            if ($pt === 'attachment') {
                return;
            }
            if ($pt !== '') {
                $pto = get_post_type_object($pt);
                if (!$pto || empty($pto->public)) {
                    return;
                }
            }
        }

        wp_enqueue_media();
        wp_enqueue_script(
            'wpsn-media-picker',
            WPSN_PLUGIN_URL . 'assets/admin/media-picker.js',
            ['jquery'],
            WPSN_VERSION,
            true
        );
    }

    public function renderTermFieldsAdd(string $taxonomy): void
    {
        $tax = get_taxonomy($taxonomy);
        if (!$tax || !current_user_can($tax->cap->edit_terms)) {
            return;
        }

        wp_nonce_field('wpsn_save_term_social', 'wpsn_term_social_nonce');

        ?>
        <div class="form-field">
            <label><strong><?php echo esc_html__('OpenGraph title', 'wp-seo-next'); ?></strong></label>
            <input type="text" name="wpsn_term_social[og_title]" value="" />
            <p class="description"><?php echo esc_html__('Leave empty to use default SEO title.', 'wp-seo-next'); ?></p>
        </div>
        <div class="form-field">
            <label><strong><?php echo esc_html__('OpenGraph description', 'wp-seo-next'); ?></strong></label>
            <textarea name="wpsn_term_social[og_description]" rows="3" cols="40"></textarea>
            <p class="description"><?php echo esc_html__('Leave empty to use default meta description.', 'wp-seo-next'); ?></p>
        </div>
        <div class="form-field">
            <label><strong><?php echo esc_html__('OpenGraph image', 'wp-seo-next'); ?></strong></label>
            <div class="wpsn-media-field">
                <input type="url" class="regular-text wpsn-media-url" name="wpsn_term_social[og_image]" value="" placeholder="https://..." />
                <p style="margin-top:8px;">
                    <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select OpenGraph image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                    <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                </p>
                <img class="wpsn-media-preview" src="" style="display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;" alt="" />
                <p class="description"><?php echo esc_html__('Leave empty to use Default OG image / Site Icon.', 'wp-seo-next'); ?></p>
            </div>
        </div>

        <hr style="margin:16px 0;" />

        <div class="form-field">
            <label><strong><?php echo esc_html__('Twitter title', 'wp-seo-next'); ?></strong></label>
            <input type="text" name="wpsn_term_social[twitter_title]" value="" />
            <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph title.', 'wp-seo-next'); ?></p>
        </div>
        <div class="form-field">
            <label><strong><?php echo esc_html__('Twitter description', 'wp-seo-next'); ?></strong></label>
            <textarea name="wpsn_term_social[twitter_description]" rows="3" cols="40"></textarea>
            <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph description.', 'wp-seo-next'); ?></p>
        </div>
        <div class="form-field">
            <label><strong><?php echo esc_html__('Twitter image', 'wp-seo-next'); ?></strong></label>
            <div class="wpsn-media-field">
                <input type="url" class="regular-text wpsn-media-url" name="wpsn_term_social[twitter_image]" value="" placeholder="https://..." />
                <p style="margin-top:8px;">
                    <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select Twitter image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                    <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                </p>
                <img class="wpsn-media-preview" src="" style="display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;" alt="" />
                <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph image.', 'wp-seo-next'); ?></p>
            </div>
        </div>
        <?php
    }

    public function renderTermFieldsEdit(\WP_Term $term, string $taxonomy): void
    {
        $tax = get_taxonomy($taxonomy);
        if (!$tax || !current_user_can($tax->cap->edit_terms)) {
            return;
        }

        wp_nonce_field('wpsn_save_term_social', 'wpsn_term_social_nonce');

        $ogTitle = (string) get_term_meta($term->term_id, MetaKeys::OG_TITLE, true);
        $ogDesc  = (string) get_term_meta($term->term_id, MetaKeys::OG_DESCRIPTION, true);
        $ogImg   = (string) get_term_meta($term->term_id, MetaKeys::OG_IMAGE, true);

        $twTitle = (string) get_term_meta($term->term_id, MetaKeys::TWITTER_TITLE, true);
        $twDesc  = (string) get_term_meta($term->term_id, MetaKeys::TWITTER_DESCRIPTION, true);
        $twImg   = (string) get_term_meta($term->term_id, MetaKeys::TWITTER_IMAGE, true);

        ?>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_og_title"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('OG Title', 'wp-seo-next'))); ?></label></th>
            <td>
                <input id="wpsn_term_og_title" type="text" class="regular-text" name="wpsn_term_social[og_title]" value="<?php echo esc_attr($ogTitle); ?>" />
                <p class="description"><?php echo esc_html__('Leave empty to use default SEO title.', 'wp-seo-next'); ?></p>
            </td>
        </tr>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_og_desc"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('OG Description', 'wp-seo-next'))); ?></label></th>
            <td>
                <textarea id="wpsn_term_og_desc" name="wpsn_term_social[og_description]" rows="4" cols="40" class="large-text"><?php echo esc_textarea($ogDesc); ?></textarea>
                <p class="description"><?php echo esc_html__('Leave empty to use default meta description.', 'wp-seo-next'); ?></p>
            </td>
        </tr>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_og_image"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('OG Image', 'wp-seo-next'))); ?></label></th>
            <td>
                <div class="wpsn-media-field">
                    <input id="wpsn_term_og_image" type="url" class="regular-text wpsn-media-url" name="wpsn_term_social[og_image]" value="<?php echo esc_attr($ogImg); ?>" placeholder="https://..." />
                    <p style="margin-top:8px;">
                        <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select OpenGraph image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                        <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                    </p>
                    <img class="wpsn-media-preview" src="<?php echo esc_url($ogImg); ?>" style="<?php echo $ogImg !== '' ? 'max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;' : 'display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;'; ?>" alt="" />
                    <p class="description"><?php echo esc_html__('Leave empty to use Default OG image / Site Icon.', 'wp-seo-next'); ?></p>
                </div>
            </td>
        </tr>

        <tr class="form-field">
            <th scope="row" colspan="2" style="padding-top:14px;"><h3 style="margin:0;"><?php echo esc_html__('Twitter Cards', 'wp-seo-next'); ?></h3></th>
        </tr>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_twitter_title"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('Twitter Title', 'wp-seo-next'))); ?></label></th>
            <td>
                <input id="wpsn_term_twitter_title" type="text" class="regular-text" name="wpsn_term_social[twitter_title]" value="<?php echo esc_attr($twTitle); ?>" />
                <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph title.', 'wp-seo-next'); ?></p>
            </td>
        </tr>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_twitter_desc"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('Twitter Description', 'wp-seo-next'))); ?></label></th>
            <td>
                <textarea id="wpsn_term_twitter_desc" name="wpsn_term_social[twitter_description]" rows="4" cols="40" class="large-text"><?php echo esc_textarea($twDesc); ?></textarea>
                <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph description.', 'wp-seo-next'); ?></p>
            </td>
        </tr>
        <tr class="form-field">
            <th scope="row"><label for="wpsn_term_twitter_image"><?php echo esc_html(Branding::labelWithBrand($this->settings, __('Twitter Image', 'wp-seo-next'))); ?></label></th>
            <td>
                <div class="wpsn-media-field">
                    <input id="wpsn_term_twitter_image" type="url" class="regular-text wpsn-media-url" name="wpsn_term_social[twitter_image]" value="<?php echo esc_attr($twImg); ?>" placeholder="https://..." />
                    <p style="margin-top:8px;">
                        <button type="button" class="button wpsn-media-select" data-title="<?php echo esc_attr__('Select Twitter image', 'wp-seo-next'); ?>" data-button="<?php echo esc_attr__('Use this image', 'wp-seo-next'); ?>"><?php echo esc_html__('Select from media library', 'wp-seo-next'); ?></button>
                        <button type="button" class="button wpsn-media-clear"><?php echo esc_html__('Clear', 'wp-seo-next'); ?></button>
                    </p>
                    <img class="wpsn-media-preview" src="<?php echo esc_url($twImg); ?>" style="<?php echo $twImg !== '' ? 'max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;' : 'display:none;max-width:300px;height:auto;border:1px solid rgba(0,0,0,.12);border-radius:6px;'; ?>" alt="" />
                    <p class="description"><?php echo esc_html__('Leave empty to use OpenGraph image.', 'wp-seo-next'); ?></p>
                </div>
            </td>
        </tr>
        <?php
    }

    public function saveTermFields(int $termId, int $ttId = 0): void
    {
        $taxonomy = isset($_POST['taxonomy']) ? sanitize_key((string) wp_unslash($_POST['taxonomy'])) : '';
        if ($taxonomy === '') {
            $term = get_term($termId);
            $taxonomy = ($term && !is_wp_error($term)) ? (string) $term->taxonomy : '';
        }
        $tax = $taxonomy !== '' ? get_taxonomy($taxonomy) : null;
        if (!$tax || empty($tax->public)) {
            return;
        }
        if (!current_user_can($tax->cap->edit_terms)) {
            return;
        }
        if (!isset($_POST['wpsn_term_social_nonce']) || !wp_verify_nonce((string) wp_unslash($_POST['wpsn_term_social_nonce']), 'wpsn_save_term_social')) {
            return;
        }

        $incoming = $_POST['wpsn_term_social'] ?? [];
        $incoming = is_array($incoming) ? wp_unslash($incoming) : [];

        $ogTitle = isset($incoming['og_title']) ? sanitize_text_field((string) $incoming['og_title']) : '';
        $ogDesc  = isset($incoming['og_description']) ? sanitize_textarea_field((string) $incoming['og_description']) : '';
        $ogImg   = isset($incoming['og_image']) ? (string) $incoming['og_image'] : '';
        $ogImg   = self::sanitizeOgImageUrl($ogImg);

        $twTitle = isset($incoming['twitter_title']) ? sanitize_text_field((string) $incoming['twitter_title']) : '';
        $twDesc  = isset($incoming['twitter_description']) ? sanitize_textarea_field((string) $incoming['twitter_description']) : '';
        $twImg   = isset($incoming['twitter_image']) ? (string) $incoming['twitter_image'] : '';
        $twImg   = self::sanitizeOgImageUrl($twImg);

        $this->updateOrDeleteTermMeta($termId, MetaKeys::OG_TITLE, $ogTitle);
        $this->updateOrDeleteTermMeta($termId, MetaKeys::OG_DESCRIPTION, $ogDesc);
        $this->updateOrDeleteTermMeta($termId, MetaKeys::OG_IMAGE, $ogImg);

        $this->updateOrDeleteTermMeta($termId, MetaKeys::TWITTER_TITLE, $twTitle);
        $this->updateOrDeleteTermMeta($termId, MetaKeys::TWITTER_DESCRIPTION, $twDesc);
        $this->updateOrDeleteTermMeta($termId, MetaKeys::TWITTER_IMAGE, $twImg);
    }

    private function updateOrDeleteTermMeta(int $termId, string $key, string $value): void
    {
        $value = trim($value);
        if ($value === '') {
            delete_term_meta($termId, $key);
            return;
        }
        update_term_meta($termId, $key, $value);
    }
}
