<?php
/**
 * Plugin Name: My Creative SEO (AEO + Readability)
 * Plugin URI: https://mycreativeplan.com/my-creative-seo
 * Description: SEO & AEO plugin: meta title/description/keywords + Focus Keyphrase, Gutenberg UI with live checks, Snippet Preview, Social (OG/X), Breadcrumbs, Basic Schema, Cornerstone, Analyzer & Bulk Editor.
 * Version: 1.1.0
 * Author: MyCreativePlan
 * Author URI: https://mycreativeplan.com
 * Text Domain: my-creative-seo-aeo-readability
 * Domain Path: /languages
 * Requires at least: 5.0
 * Tested up to: 6.8
 * Requires PHP: 7.4
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 *
 * My Creative SEO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * any later version.
 *
 * My Creative SEO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

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

/* -------------------------------------------------------------------------
 * Constants
 * ---------------------------------------------------------------------- */
define( 'MC_SEO_VERSION', '1.1.0' );
define( 'MC_SEO_PLUGIN_FILE', __FILE__ );
define( 'MC_SEO_DIR', plugin_dir_path( __FILE__ ) );
define( 'MC_SEO_URL', plugin_dir_url( __FILE__ ) );
define( 'MC_SEO_OPTIONS_KEY', 'mc_seo_options' ); // sitewide settings

/** Optional Playwright microservice (voor JS-heavy sites) */
if ( ! defined( 'MC_RENDER_URL' ) ) {
  define( 'MC_RENDER_URL', 'http://localhost:8787/render' );
}

/* Analyzer */
require_once MC_SEO_DIR . 'includes/analyzer.php';

/* -------------------------------------------------------------------------
 * Main class
 * ---------------------------------------------------------------------- */
class MC_SEO_Plugin {
  /* Post meta keys */
  const META_TITLE       = '_mc_seo_title';
  const META_DESC        = '_mc_seo_description';
  const META_KEYWORDS    = '_mc_seo_keywords';
  const META_NOINDEX     = '_mc_seo_noindex';
  const META_FOCUS       = '_mc_seo_focus_keyphrase';
  const META_CORNERSTONE = '_mc_seo_cornerstone';

  // Social overrides
  const META_OG_TITLE    = '_mc_seo_og_title';
  const META_OG_DESC     = '_mc_seo_og_desc';
  const META_OG_IMAGE    = '_mc_seo_og_image';

  public static function init() {
    // Load translations
    add_action( 'init', [__CLASS__, 'load_textdomain'] );
    add_action( 'init',                [__CLASS__, 'register_meta' ] );
    add_action( 'add_meta_boxes',      [__CLASS__, 'add_classic_meta_box' ] );
    add_action( 'save_post',           [__CLASS__, 'save_meta_box' ] );
    add_action( 'enqueue_block_editor_assets', [__CLASS__, 'enqueue_editor' ] );

    /* Head output */
    add_action( 'wp_head', [__CLASS__, 'output_meta_tags' ], 1 );
    add_action( 'wp_head', [__CLASS__, 'output_schema_graph' ], 2 );

    /* Titles â€“ apply templates when no explicit SEO title is set */
    add_filter( 'pre_get_document_title', [__CLASS__, 'filter_document_title' ], 20 );

    /* Admin pages */
    add_action( 'admin_menu', [__CLASS__, 'register_admin_pages' ] );

    /* REST routes (Analyzer) */
    add_action( 'rest_api_init', [__CLASS__, 'register_rest_routes'] );

    /* Shortcodes / breadcrumbs */
    add_action( 'init', [__CLASS__, 'register_breadcrumbs_shortcode'] );
  }

  /* ---------------------------------------------------------------------
   * Load translations
   * Note: WordPress.org automatically loads translations for hosted plugins
   * ------------------------------------------------------------------ */
  public static function load_textdomain() {
    // WordPress.org automatically handles plugin translations
    // load_plugin_textdomain() is no longer needed for hosted plugins
  }

  /* ---------------------------------------------------------------------
   * Options (global)
   * ------------------------------------------------------------------ */
  public static function get_options() {
    $defaults = [
      'templates' => [
        'post' => '%%title%% - %%sitename%%',
        'page' => '%%title%% | %%sitename%%',
        'category' => '%%term_title%% - %%sitename%%',
        'post_meta_desc_fallback' => '%%excerpt%%',
        'page_meta_desc_fallback' => '%%excerpt%%'
      ],
      'sep' => 'â€“',
      'entity_type' => 'Organization', // or Person
      'entity_name' => get_bloginfo('name'),
      'entity_logo' => '',
      'default_og_image' => '',
    ];
    $opts = get_option( MC_SEO_OPTIONS_KEY, [] );
    return wp_parse_args( $opts, $defaults );
  }

  public static function update_options( $new ) {
    update_option( MC_SEO_OPTIONS_KEY, $new );
  }

  /* ---------------------------------------------------------------------
   * Post meta registration
   * ------------------------------------------------------------------ */
  public static function register_meta() {
    $str = [
      'single' => true, 'show_in_rest' => true, 'type' => 'string',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => 'sanitize_text_field',
    ];
    register_post_meta( '', self::META_TITLE,       $str );
    register_post_meta( '', self::META_DESC,        $str );
    register_post_meta( '', self::META_KEYWORDS,    $str );
    register_post_meta( '', self::META_FOCUS,       $str );

    register_post_meta( '', self::META_NOINDEX, [
      'single' => true, 'show_in_rest' => true, 'type' => 'boolean',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => function($v){ return (bool)$v; }
    ] );

    register_post_meta( '', self::META_CORNERSTONE, [
      'single' => true, 'show_in_rest' => true, 'type' => 'boolean',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => function($v){ return (bool)$v; }
    ] );

    // Social overrides
    register_post_meta( '', self::META_OG_TITLE, [
      'single' => true, 'show_in_rest' => true, 'type' => 'string',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => 'sanitize_text_field',
    ] );
    register_post_meta( '', self::META_OG_DESC, [
      'single' => true, 'show_in_rest' => true, 'type' => 'string',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => 'sanitize_text_field',
    ] );
    register_post_meta( '', self::META_OG_IMAGE, [
      'single' => true, 'show_in_rest' => true, 'type' => 'string',
      'auth_callback' => function(){ return current_user_can('edit_posts'); },
      'sanitize_callback' => 'esc_url_raw',
    ] );
  }

  /* ---------------------------------------------------------------------
   * Meta box (classic) + Cornerstone + Recommendations container
   * ------------------------------------------------------------------ */
  public static function add_classic_meta_box() {
    add_meta_box(
      'mc-seo-box',
      __('My Creative SEO', 'my-creative-seo-aeo-readability'),
      [__CLASS__, 'render_classic_meta_box'],
      ['post','page'],
      'normal',
      'high'
    );
  }

  public static function render_classic_meta_box( $post ) {
    wp_nonce_field( 'mc_seo_meta', 'mc_seo_nonce' );
    $t = get_post_meta( $post->ID, self::META_TITLE, true );
    $d = get_post_meta( $post->ID, self::META_DESC, true );
    $k = get_post_meta( $post->ID, self::META_KEYWORDS, true );
    $f = get_post_meta( $post->ID, self::META_FOCUS, true );
    $x = (bool) get_post_meta( $post->ID, self::META_NOINDEX, true );
    $c = (bool) get_post_meta( $post->ID, self::META_CORNERSTONE, true );
    ?>
    <style>
      .mc-field{margin-bottom:12px}
      .mc-field label{font-weight:600;display:block;margin-bottom:4px}
      .mc-field input[type="text"], .mc-field textarea{width:100%;max-width:100%}
      .mc-badge{display:inline-block;background:#eefdf7;border:1px solid #d4f7e8;border-radius:999px;padding:2px 8px;margin-right:6px}
      .mc-card{background:#fff;border:1px solid #e8ecec;border-radius:14px;padding:12px 14px;margin-top:12px}
      .mc-card-title{font-weight:700;margin-bottom:6px}
      .mc-chip{display:inline-block;background:#eefdf7;border:1px solid #d4f7e8;border-radius:999px;padding:2px 8px;margin-right:6px;font-size:12px}
      .mc-checks{margin:6px 0 0 18px}
      .mc-checks li{display:flex;gap:6px;align-items:center;margin:3px 0}
      .good{color:#139e52;font-weight:600}.bad{color:#e53935;font-weight:600}.warn{color:#ff9800;font-weight:600}
    </style>

    <div class="mc-field">
      <label for="mc_seo_title"><?php esc_html_e('SEO Title (overrides <title>)', 'my-creative-seo-aeo-readability'); ?></label>
      <input type="text" id="mc_seo_title" name="mc_seo_title" value="<?php echo esc_attr($t); ?>" maxlength="70" 
             aria-describedby="mc_seo_title_help" class="regular-text"/>
      <div id="mc_seo_title_help" class="description">
        <span class="mc-badge"><?php echo esc_html__('30â€“60 chars ideal', 'my-creative-seo-aeo-readability'); ?></span>
        <?php esc_html_e('This title will appear in search results and browser tabs.', 'my-creative-seo-aeo-readability'); ?>
      </div>
    </div>

    <div class="mc-field">
      <label for="mc_seo_desc"><?php esc_html_e('Meta Description', 'my-creative-seo-aeo-readability'); ?></label>
      <textarea id="mc_seo_desc" name="mc_seo_desc" rows="3" maxlength="180" 
                aria-describedby="mc_seo_desc_help" class="large-text"><?php echo esc_textarea($d); ?></textarea>
      <div id="mc_seo_desc_help" class="description">
        <span class="mc-badge"><?php echo esc_html__('~120â€“160 chars', 'my-creative-seo-aeo-readability'); ?></span>
        <?php esc_html_e('This description will appear below your title in search results.', 'my-creative-seo-aeo-readability'); ?>
      </div>
    </div>

    <div class="mc-field">
      <label for="mc_seo_keywords"><?php esc_html_e('Meta Keywords (legacy)', 'my-creative-seo-aeo-readability'); ?></label>
      <input type="text" id="mc_seo_keywords" name="mc_seo_keywords" value="<?php echo esc_attr($k); ?>" 
             placeholder="<?php esc_attr_e('keyword1, keyword2', 'my-creative-seo-aeo-readability'); ?>" 
             aria-describedby="mc_seo_keywords_help" class="regular-text"/>
      <div id="mc_seo_keywords_help" class="description">
        <?php esc_html_e('Legacy feature. Most search engines ignore meta keywords.', 'my-creative-seo-aeo-readability'); ?>
      </div>
    </div>

    <div class="mc-field">
      <label for="mc_seo_focus"><?php esc_html_e('Focus Keyphrase', 'my-creative-seo-aeo-readability'); ?></label>
      <input type="text" id="mc_seo_focus" name="mc_seo_focus" value="<?php echo esc_attr($f); ?>" 
             placeholder="<?php esc_attr_e('your main keyphrase', 'my-creative-seo-aeo-readability'); ?>" 
             aria-describedby="mc_seo_focus_help" class="regular-text"/>
      <div id="mc_seo_focus_help" class="description">
        <?php esc_html_e('The main keyword or phrase you want this content to rank for. Used for content analysis only.', 'my-creative-seo-aeo-readability'); ?>
      </div>
    </div>

    <div class="mc-field">
      <fieldset>
        <legend class="screen-reader-text"><?php esc_html_e('SEO Options', 'my-creative-seo-aeo-readability'); ?></legend>
        <label for="mc_seo_noindex">
          <input type="checkbox" id="mc_seo_noindex" name="mc_seo_noindex" value="1" <?php checked( $x ); ?>
                 aria-describedby="mc_seo_noindex_help"/> 
          <?php esc_html_e('Noindex this page', 'my-creative-seo-aeo-readability'); ?>
        </label>
        <div id="mc_seo_noindex_help" class="description">
          <?php esc_html_e('Prevent search engines from indexing this page.', 'my-creative-seo-aeo-readability'); ?>
        </div>
      </fieldset>
    </div>

    <div class="mc-field">
      <fieldset>
        <legend class="screen-reader-text"><?php esc_html_e('Content Priority', 'my-creative-seo-aeo-readability'); ?></legend>
        <label for="mc_seo_cornerstone">
          <input type="checkbox" id="mc_seo_cornerstone" name="mc_seo_cornerstone" value="1" <?php checked( $c ); ?>
                 aria-describedby="mc_seo_cornerstone_help"/> 
          <?php esc_html_e('Mark as Cornerstone Content', 'my-creative-seo-aeo-readability'); ?>
        </label>
        <div id="mc_seo_cornerstone_help" class="description">
          <?php esc_html_e('Important content that represents your expertise. Gets stricter SEO analysis.', 'my-creative-seo-aeo-readability'); ?>
        </div>
      </fieldset>
    </div>

    <!-- Live Recommendations (mirror van sidebar) -->
    <div id="mc-seo-reco" class="mc-card">
      <div class="mc-card-title"><?php esc_html_e('Recommendations (live)', 'my-creative-seo-aeo-readability'); ?></div>
      <p style="opacity:.7;margin:0;"><?php esc_html_e('Start typing in the editor to see checks here as well.', 'my-creative-seo-aeo-readability'); ?></p>
    </div>
    <?php
  }

  public static function save_meta_box( $post_id ) {
    if ( ! isset($_POST['mc_seo_nonce']) || ! wp_verify_nonce( $_POST['mc_seo_nonce'], 'mc_seo_meta' ) ) return;
    if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return;
    if ( ! current_user_can( 'edit_post', $post_id ) ) return;

    $t = isset($_POST['mc_seo_title']) ? sanitize_text_field( $_POST['mc_seo_title'] ) : '';
    $d = isset($_POST['mc_seo_desc']) ? sanitize_textarea_field( $_POST['mc_seo_desc'] ) : '';
    $k = isset($_POST['mc_seo_keywords']) ? sanitize_text_field( $_POST['mc_seo_keywords'] ) : '';
    $f = isset($_POST['mc_seo_focus']) ? sanitize_text_field( $_POST['mc_seo_focus'] ) : '';
    $x = ! empty($_POST['mc_seo_noindex']) ? 1 : 0;
    $c = ! empty($_POST['mc_seo_cornerstone']) ? 1 : 0;

    update_post_meta( $post_id, self::META_TITLE, $t );
    update_post_meta( $post_id, self::META_DESC, $d );
    update_post_meta( $post_id, self::META_KEYWORDS, $k );
    update_post_meta( $post_id, self::META_FOCUS, $f );
    update_post_meta( $post_id, self::META_NOINDEX, $x );
    update_post_meta( $post_id, self::META_CORNERSTONE, $c );
  }

  /* ---------------------------------------------------------------------
   * Editor assets (+ data voor snippet preview & templates)
   * ------------------------------------------------------------------ */
  public static function enqueue_editor() {
    wp_enqueue_script(
      'mc-seo-editor',
      MC_SEO_URL . 'assets/editor.js',
      [ 'wp-plugins','wp-edit-post','wp-components','wp-element','wp-data','wp-i18n','wp-block-editor','wp-url' ],
      MC_SEO_VERSION,
      true
    );
    wp_enqueue_style( 'mc-seo-editor', MC_SEO_URL . 'assets/editor.css', [], MC_SEO_VERSION );

    $opts = self::get_options();
    wp_localize_script( 'mc-seo-editor', 'MCSEO', [
      'metaKeys' => [
        'title'       => self::META_TITLE,
        'desc'        => self::META_DESC,
        'keywords'    => self::META_KEYWORDS,
        'noindex'     => self::META_NOINDEX,
        'focus'       => self::META_FOCUS,
        'cornerstone' => self::META_CORNERSTONE,
        // Social overrides
        'og_title'    => self::META_OG_TITLE,
        'og_desc'     => self::META_OG_DESC,
        'og_image'    => self::META_OG_IMAGE,
      ],
      'restAnalyze' => esc_url_raw( rest_url( 'mc-seo/v1/analyze' ) ),
      'nonce'       => wp_create_nonce( 'wp_rest' ),
      'templates'   => $opts['templates'],
      'sep'         => $opts['sep'],
      'site'        => [
        'name' => get_bloginfo('name'),
        'url'  => home_url('/'),
        'icon' => get_site_icon_url(32) ?: '',
      ],
      'snippet'     => [ 'mobileWidth' => 375 ],
      'assets'      => [ 'logo' => MC_SEO_URL . 'assets/mc-logo.png' ], // UI header logo
    ] );
  }

  /* ---------------------------------------------------------------------
   * Title templating (for front-end document title)
   * ------------------------------------------------------------------ */
  public static function filter_document_title( $title ) {
    if ( is_admin() ) return $title;
    if ( ! is_singular() ) return $title;

    global $post;
    if ( ! $post ) return $title;

    $custom = trim( (string) get_post_meta( $post->ID, self::META_TITLE, true ) );
    if ( $custom !== '' ) return $custom;

    $opts = self::get_options();
    $tpl = is_page() ? $opts['templates']['page'] : $opts['templates']['post'];
    return self::apply_title_template( $tpl, $post );
  }

  protected static function apply_title_template( $tpl, $post ) {
    $repl = [
      '%%title%%'      => get_the_title( $post ),
      '%%sitename%%'   => get_bloginfo('name'),
      '%%sep%%'        => self::get_options()['sep'],
      '%%parent_title%%' => ( $p = get_post($post->post_parent) ) ? get_the_title($p) : '',
      '%%date%%'       => get_the_date( '', $post ),
    ];
    return trim( strtr( $tpl, $repl ) );
  }

  protected static function apply_desc_fallback( $post ) {
    $opts = self::get_options();
    $tpl  = is_page( $post ) ? ($opts['templates']['page_meta_desc_fallback'] ?? '') : ($opts['templates']['post_meta_desc_fallback'] ?? '' );
    $excerpt = has_excerpt( $post ) ? wp_strip_all_tags( get_the_excerpt( $post ) ) : wp_trim_words( wp_strip_all_tags( $post->post_content ), 26, '' );
    $repl = [ '%%excerpt%%' => $excerpt, '%%title%%'=> get_the_title($post), '%%sitename%%'=> get_bloginfo('name') ];
    $desc = trim( strtr( $tpl, $repl ) );
    return $desc;
  }

  /* ---------------------------------------------------------------------
   * Head tags: meta + OG + X (Twitter) cards
   * ------------------------------------------------------------------ */
  public static function output_meta_tags() {
    if ( ! is_singular() ) return;
    global $post; if ( ! $post ) return;

    $opts     = self::get_options();
    $noindex  = (bool) get_post_meta( $post->ID, self::META_NOINDEX, true );
    $title    = trim( (string) get_post_meta( $post->ID, self::META_TITLE, true ) );
    $desc     = trim( (string) get_post_meta( $post->ID, self::META_DESC, true ) );
    $keywords = trim( (string) get_post_meta( $post->ID, self::META_KEYWORDS, true ) );

    if ( $title === '' ) $title = self::apply_title_template( is_page() ? $opts['templates']['page'] : $opts['templates']['post'], $post );
    if ( $desc  === '' ) $desc  = self::apply_desc_fallback( $post );

    // Social overrides
    $og_title = get_post_meta( $post->ID, self::META_OG_TITLE, true );
    $og_desc  = get_post_meta( $post->ID, self::META_OG_DESC, true );
    $og_image = get_post_meta( $post->ID, self::META_OG_IMAGE, true );
    $og_title = $og_title ?: $title;
    $og_desc  = $og_desc  ?: $desc;

    /* Basic <title> + meta description/keywords */
    if ( $title ) echo '<title>' . esc_html( $title ) . "</title>\n";
    if ( $desc ) {
      echo '<meta name="description" content="' . esc_attr( $desc ) . "\" />\n";
      echo '<meta property="og:description" content="' . esc_attr( $og_desc ) . "\" />\n";
      echo '<meta name="twitter:description" content="' . esc_attr( $og_desc ) . "\" />\n"; // X gebruikt nog steeds twitter:* metatag
    }
    if ( $keywords ) echo '<meta name="keywords" content="' . esc_attr( $keywords ) . "\" />\n";
    if ( $noindex )  echo "<meta name=\"robots\" content=\"noindex,follow\" />\n";

    /* OG / X (Twitter) */
    $url   = get_permalink( $post );
    $type  = is_singular('post') ? 'article' : 'website';
    $image = $og_image ?: ( get_the_post_thumbnail_url( $post, 'full' ) ?: ( $opts['default_og_image'] ?: '' ) );

    echo '<meta property="og:title" content="' . esc_attr( $og_title ) . "\" />\n";
    echo '<meta property="og:type" content="' . esc_attr( $type ) . "\" />\n";
    echo '<meta property="og:url" content="' . esc_url( $url ) . "\" />\n";
    if ( $image ) echo '<meta property="og:image" content="' . esc_url( $image ) . "\" />\n";

    echo '<meta name="twitter:card" content="summary_large_image" />' . "\n"; // X
    echo '<meta name="twitter:title" content="' . esc_attr( $og_title ) . "\" />\n";
    if ( $image ) echo '<meta name="twitter:image" content="' . esc_url( $image ) . "\" />\n";
  }

  /* ---------------------------------------------------------------------
   * Schema Graph (basic)
   * ------------------------------------------------------------------ */
  public static function output_schema_graph() {
    if ( is_admin() ) return;

    $opts = self::get_options();
    $site = [
      '@context' => 'https://schema.org',
      '@type'    => 'WebSite',
      'url'      => home_url('/'),
      'name'     => get_bloginfo('name'),
    ];

    $entity = [
      '@type' => $opts['entity_type'] === 'Person' ? 'Person' : 'Organization',
      'name'  => $opts['entity_name'],
    ];
    if ( ! empty($opts['entity_logo']) ) {
      $entity['logo'] = [ '@type'=>'ImageObject', 'url' => esc_url( $opts['entity_logo'] ) ];
    }

    $graph = [ $site, $entity ];

    if ( is_singular() ) {
      global $post;
      $item = [
        '@type' => is_singular('post') ? 'Article' : 'WebPage',
        '@id'   => get_permalink($post).'#main',
        'url'   => get_permalink($post),
        'headline' => get_the_title($post),
        'datePublished' => get_post_time( 'c', true, $post ),
        'dateModified'  => get_post_modified_time( 'c', true, $post ),
      ];
      $author = get_userdata( $post->post_author );
      if ( $author ) $item['author'] = [ '@type'=>'Person', 'name' => $author->display_name ];
      $img = get_the_post_thumbnail_url( $post, 'full' );
      if ( $img ) $item['image'] = [ '@type'=>'ImageObject', 'url' => esc_url($img) ];
      $graph[] = $item;
    }

    $json = [ '@context'=>'https://schema.org', '@graph' => $graph ];
    echo "<script type=\"application/ld+json\">" . wp_json_encode( $json ) . "</script>\n";
  }

  /* ---------------------------------------------------------------------
   * Admin: Settings (templates, schema entity, defaults)
   * ------------------------------------------------------------------ */
  public static function register_admin_pages() {
    add_menu_page(
      __('My Creative SEO', 'my-creative-seo-aeo-readability'),
      __('My Creative SEO', 'my-creative-seo-aeo-readability'),
      'manage_options',
      'mc-seo',
      [__CLASS__, 'render_settings_page'],
      'dashicons-chart-line', 81
    );

    add_submenu_page(
      'mc-seo',
      __('SEO Settings', 'my-creative-seo-aeo-readability'),
      __('Settings', 'my-creative-seo-aeo-readability'),
      'manage_options',
      'mc-seo',
      [__CLASS__, 'render_settings_page']
    );

    add_submenu_page(
      'mc-seo',
      __('SEO Analyzer', 'my-creative-seo-aeo-readability'),
      __('Analyzer', 'my-creative-seo-aeo-readability'),
      'manage_options',
      'mc-seo-analyzer',
      [__CLASS__, 'render_analyzer_page']
    );

    add_management_page(
      __('Bulk SEO Editor', 'my-creative-seo-aeo-readability'),
      __('Bulk SEO Editor', 'my-creative-seo-aeo-readability'),
      'edit_others_posts',
      'mc-seo-bulk',
      [__CLASS__, 'render_bulk_editor_page']
    );
  }

  public static function render_settings_page() {
    // Add contextual help
    self::add_settings_help_tabs();
    
    if ( isset($_POST['mc_seo_save']) && check_admin_referer('mc_seo_opts') ) {
      $opts = self::get_options();
      $opts['templates']['post']  = wp_kses_post( wp_unslash( $_POST['tpl_post'] ?? '' ) );
      $opts['templates']['page']  = wp_kses_post( wp_unslash( $_POST['tpl_page'] ?? '' ) );
      $opts['templates']['category'] = wp_kses_post( wp_unslash( $_POST['tpl_cat'] ?? '' ) );
      $opts['templates']['post_meta_desc_fallback'] = sanitize_text_field( $_POST['tpl_post_desc'] ?? '' );
      $opts['templates']['page_meta_desc_fallback'] = sanitize_text_field( $_POST['tpl_page_desc'] ?? '' );
      $opts['sep'] = sanitize_text_field( $_POST['sep'] ?? 'â€“' );
      $opts['entity_type'] = ($_POST['entity_type'] ?? 'Organization') === 'Person' ? 'Person' : 'Organization';
      $opts['entity_name'] = sanitize_text_field( $_POST['entity_name'] ?? get_bloginfo('name') );
      $opts['entity_logo'] = esc_url_raw( $_POST['entity_logo'] ?? '' );
      $opts['default_og_image'] = esc_url_raw( $_POST['default_og_image'] ?? '' );
      self::update_options( $opts );
      echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Settings saved successfully!', 'my-creative-seo-aeo-readability') . '</p></div>';
    }
    $o = self::get_options();
    ?>
    <div class="wrap">
      <h1>My Creative SEO â€” Settings</h1>
      <form method="post">
        <?php wp_nonce_field('mc_seo_opts'); ?>
        <h2>Title & Meta Templates</h2>
        <table class="form-table">
          <tr><th>Post title template</th><td><input type="text" name="tpl_post" value="<?php echo esc_attr($o['templates']['post']); ?>" class="regular-text" /></td></tr>
          <tr><th>Page title template</th><td><input type="text" name="tpl_page" value="<?php echo esc_attr($o['templates']['page']); ?>" class="regular-text" /></td></tr>
          <tr><th>Category title template</th><td><input type="text" name="tpl_cat" value="<?php echo esc_attr($o['templates']['category']); ?>" class="regular-text" /></td></tr>
          <tr><th>Post description fallback</th><td><input type="text" name="tpl_post_desc" value="<?php echo esc_attr($o['templates']['post_meta_desc_fallback']); ?>" class="regular-text" /></td></tr>
          <tr><th>Page description fallback</th><td><input type="text" name="tpl_page_desc" value="<?php echo esc_attr($o['templates']['page_meta_desc_fallback']); ?>" class="regular-text" /></td></tr>
          <tr><th>Separator (%%sep%%)</th><td><input type="text" name="sep" value="<?php echo esc_attr($o['sep']); ?>" class="small-text" /></td></tr>
        </table>

        <h2>Knowledge Graph Entity</h2>
        <table class="form-table">
          <tr><th>Type</th><td>
            <label><input type="radio" name="entity_type" value="Organization" <?php checked($o['entity_type'],'Organization'); ?>/> Organization</label>
            &nbsp;
            <label><input type="radio" name="entity_type" value="Person" <?php checked($o['entity_type'],'Person'); ?>/> Person</label>
          </td></tr>
          <tr><th>Name</th><td><input type="text" name="entity_name" value="<?php echo esc_attr($o['entity_name']); ?>" class="regular-text" /></td></tr>
          <tr><th>Logo URL</th><td><input type="url" name="entity_logo" value="<?php echo esc_attr($o['entity_logo']); ?>" class="regular-text code" /></td></tr>
        </table>

        <h2>Social Defaults</h2>
        <table class="form-table">
          <tr><th>Default OG/X image URL</th><td><input type="url" name="default_og_image" value="<?php echo esc_attr($o['default_og_image']); ?>" class="regular-text code" /></td></tr>
        </table>

        <p><button class="button button-primary" name="mc_seo_save" value="1">Save settings</button></p>
      </form>
      <p><small>Available title variables: <code>%%title%%</code> <code>%%sitename%%</code> <code>%%sep%%</code> <code>%%parent_title%%</code> <code>%%date%%</code><br/>Description fallback variables: <code>%%excerpt%%</code> (auto-generated)</small></p>
    </div>
    <?php
  }

  /* ---------------------------------------------------------------------
   * Breadcrumbs shortcode + schema
   * ------------------------------------------------------------------ */
  public static function register_breadcrumbs_shortcode() {
    add_shortcode( 'mc_seo_breadcrumbs', [__CLASS__, 'breadcrumbs_shortcode_cb'] );
  }

  public static function breadcrumbs_shortcode_cb( $atts = [] ) {
    $trail = [];
    $trail[] = ['url'=>home_url('/'), 'label'=>__('Home','my-creative-seo-aeo-readability')];

    if ( is_singular() ) {
      global $post;
      $parents = array_reverse( get_post_ancestors( $post ) );
      foreach ( $parents as $pid ) {
        $trail[] = ['url'=>get_permalink($pid), 'label'=>get_the_title($pid)];
      }
      $trail[] = ['url'=>get_permalink(), 'label'=>get_the_title()];
    } elseif ( is_category() || is_tag() || is_tax() ) {
      $term = get_queried_object();
      if ( $term && ! is_wp_error($term) ) {
        if ( $term->parent ) {
          $parents = array_reverse( get_ancestors( $term->term_id, $term->taxonomy ) );
          foreach ( $parents as $tid ) {
            $trail[] = ['url'=>get_term_link($tid), 'label'=>get_term($tid)->name];
          }
        }
        $trail[] = ['url'=>get_term_link($term), 'label'=>$term->name];
      }
    }

    // HTML
    $html = '<nav class="mc-breadcrumbs" aria-label="Breadcrumbs"><ol>';
    foreach ( $trail as $i => $n ) {
      $last = ($i === count($trail)-1);
      $html .= '<li'.($last?' class="current"':'').'>';
      if ( ! $last ) $html .= '<a href="'.esc_url($n['url']).'">'.esc_html($n['label']).'</a>';
      else $html .= '<span>'.esc_html($n['label']).'</span>';
      $html .= '</li>';
    }
    $html .= '</ol></nav>';

    // Schema
    $items = [];
    foreach ( $trail as $i => $n ) {
      $items[] = [
        '@type'    => 'ListItem',
        'position' => $i + 1,
        'name'     => wp_strip_all_tags( $n['label'] ),
        'item'     => esc_url( $n['url'] )
      ];
    }
    $schema = [
      '@context' => 'https://schema.org',
      '@type'    => 'BreadcrumbList',
      'itemListElement' => $items
    ];
    $html .= '<script type="application/ld+json">'. wp_json_encode($schema) .'</script>';

    // Basic styling (soft)
    $html .= '<style>.mc-breadcrumbs ol{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0;padding:0}.mc-breadcrumbs li{color:#0b1b1a;opacity:.9}.mc-breadcrumbs li a{color:#00c875;text-decoration:none}.mc-breadcrumbs li.current span{font-weight:600}.mc-breadcrumbs li:not(:last-child):after{content:"â€º";margin-left:8px;opacity:.5}</style>';

    return $html;
  }

  /* ---------------------------------------------------------------------
   * Contextual Help Tabs
   * ------------------------------------------------------------------ */
  public static function add_settings_help_tabs() {
    $screen = get_current_screen();
    if ( ! $screen ) return;

    $screen->add_help_tab( [
      'id'      => 'mc-seo-overview',
      'title'   => __('Overview', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('My Creative SEO helps optimize your content for search engines and answer engines (AEO). Use these settings to configure global templates and schema markup.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p>' . __('You can customize how titles and meta descriptions are generated for different content types, set up your organization information for schema markup, and configure social media defaults.', 'my-creative-seo-aeo-readability') . '</p>'
    ] );

    $screen->add_help_tab( [
      'id'      => 'mc-seo-templates',
      'title'   => __('Title Templates', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('Title templates determine how page titles are displayed in search results and browser tabs. You can use the following placeholders:', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<ul>' .
                   '<li><code>{{title}}</code> - ' . __('The post/page title', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li><code>{{site}}</code> - ' . __('Your site name', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li><code>{{sep}}</code> - ' . __('The separator you chose', 'my-creative-seo-aeo-readability') . '</li>' .
                   '</ul>' .
                   '<p>' . __('Example: <code>{{title}} {{sep}} {{site}}</code> becomes "My Article - My Website"', 'my-creative-seo-aeo-readability') . '</p>'
    ] );

    $screen->add_help_tab( [
      'id'      => 'mc-seo-schema',
      'title'   => __('Schema & Social', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('Schema markup helps search engines understand your content better. Configure your organization or personal information here.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p>' . __('Social media settings control how your content appears when shared on platforms like Facebook and Twitter. Set a default image to use when individual posts don\'t have featured images.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p><strong>' . __('Pro tip:', 'my-creative-seo-aeo-readability') . '</strong> ' . __('Use high-quality images (1200x630px) for best social media results.', 'my-creative-seo-aeo-readability') . '</p>'
    ] );

    $screen->set_help_sidebar(
      '<p><strong>' . __('Need Help?', 'my-creative-seo-aeo-readability') . '</strong></p>' .
      '<p><a href="https://mycreativeplan.com/docs/my-creative-seo" target="_blank">' . __('Documentation', 'my-creative-seo-aeo-readability') . '</a></p>' .
      '<p><a href="https://wordpress.org/support/plugin/my-creative-seo/" target="_blank">' . __('Support Forums', 'my-creative-seo-aeo-readability') . '</a></p>' .
      '<p><a href="https://mycreativeplan.com/contact" target="_blank">' . __('Contact Support', 'my-creative-seo-aeo-readability') . '</a></p>'
    );
  }

  public static function add_analyzer_help_tabs() {
    $screen = get_current_screen();
    if ( ! $screen ) return;

    $screen->add_help_tab( [
      'id'      => 'mc-seo-analyzer-overview',
      'title'   => __('How It Works', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('The SEO Analyzer evaluates websites for both traditional SEO and modern AEO (Answer Engine Optimization) factors.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p>' . __('Enter any domain name to get a comprehensive analysis including:', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<ul>' .
                   '<li>' . __('Meta tags completeness and optimization', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('Content structure and readability', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('Technical SEO factors', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('AEO readiness for featured snippets', 'my-creative-seo-aeo-readability') . '</li>' .
                   '</ul>'
    ] );

    $screen->add_help_tab( [
      'id'      => 'mc-seo-analyzer-scoring',
      'title'   => __('Understanding Scores', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('The analyzer uses a 10-point scoring system:', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<ul>' .
                   '<li><strong>8-10:</strong> <span style="color:#139e52">' . __('Excellent optimization', 'my-creative-seo-aeo-readability') . '</span></li>' .
                   '<li><strong>5-7:</strong> <span style="color:#ff9800">' . __('Good but needs improvement', 'my-creative-seo-aeo-readability') . '</span></li>' .
                   '<li><strong>0-4:</strong> <span style="color:#e53935">' . __('Significant optimization needed', 'my-creative-seo-aeo-readability') . '</span></li>' .
                   '</ul>' .
                   '<p>' . __('Focus on fixing the most critical issues first (marked in red) before addressing improvements (marked in orange).', 'my-creative-seo-aeo-readability') . '</p>'
    ] );

    $screen->set_help_sidebar(
      '<p><strong>' . __('Analyzer Tips', 'my-creative-seo-aeo-readability') . '</strong></p>' .
      '<p>' . __('â€¢ Test your own site regularly', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Compare with competitors', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Focus on content structure', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Optimize for featured snippets', 'my-creative-seo-aeo-readability') . '</p>'
    );
  }

  public static function add_bulk_editor_help_tabs() {
    $screen = get_current_screen();
    if ( ! $screen ) return;

    $screen->add_help_tab( [
      'id'      => 'mc-seo-bulk-overview',
      'title'   => __('Bulk Editor', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('The Bulk SEO Editor allows you to quickly update SEO titles and meta descriptions for multiple posts and pages at once.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p>' . __('This tool shows your most recently modified content first, making it easy to optimize new content or update existing posts.', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<p><strong>' . __('Best Practices:', 'my-creative-seo-aeo-readability') . '</strong></p>' .
                   '<ul>' .
                   '<li>' . __('Keep titles between 30-60 characters', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('Write descriptions between 120-160 characters', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('Include your focus keywords naturally', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li>' . __('Make each title and description unique', 'my-creative-seo-aeo-readability') . '</li>' .
                   '</ul>'
    ] );

    $screen->add_help_tab( [
      'id'      => 'mc-seo-bulk-tips',
      'title'   => __('Optimization Tips', 'my-creative-seo-aeo-readability'),
      'content' => '<p>' . __('Use this editor to ensure consistency across your site\'s SEO elements:', 'my-creative-seo-aeo-readability') . '</p>' .
                   '<ul>' .
                   '<li><strong>' . __('SEO Titles:', 'my-creative-seo-aeo-readability') . '</strong> ' . __('Should be compelling and include your main keyword', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li><strong>' . __('Meta Descriptions:', 'my-creative-seo-aeo-readability') . '</strong> ' . __('Act as advertising copy to encourage clicks', 'my-creative-seo-aeo-readability') . '</li>' .
                   '<li><strong>' . __('Consistency:', 'my-creative-seo-aeo-readability') . '</strong> ' . __('Use similar formats across related content', 'my-creative-seo-aeo-readability') . '</li>' .
                   '</ul>' .
                   '<p>' . __('Remember: Changes are saved immediately when you click "Save changes". You can edit individual posts for more detailed SEO settings.', 'my-creative-seo-aeo-readability') . '</p>'
    ] );

    $screen->set_help_sidebar(
      '<p><strong>' . __('Quick Actions', 'my-creative-seo-aeo-readability') . '</strong></p>' .
      '<p>' . __('â€¢ Focus on empty fields first', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Check character counts', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Keep it scannable', 'my-creative-seo-aeo-readability') . '</p>' .
      '<p>' . __('â€¢ Test with preview tools', 'my-creative-seo-aeo-readability') . '</p>'
    );
  }

  /* ---------------------------------------------------------------------
   * Analyzer admin page (licht gestileerd)
   * ------------------------------------------------------------------ */
  public static function render_analyzer_page() {
    // Add contextual help
    self::add_analyzer_help_tabs();
    ?>
    <div class="wrap mc-seo-wrap">
      <h1><?php esc_html_e('My Creative SEO Analyzer', 'my-creative-seo-aeo-readability'); ?></h1>
      <p><?php esc_html_e('Analyze a domain: homepage + estimated top pages, with AEO/SEO/readability checks.', 'my-creative-seo-aeo-readability'); ?></p>

      <div class="mc-card">
        <div class="mc-row">
          <input type="text" id="mc-seo-domain" placeholder="e.g. mycreativeplan.com" class="regular-text" style="max-width:420px;">
          <button class="button button-primary" id="mc-seo-go"><?php esc_html_e('Analyze', 'my-creative-seo-aeo-readability'); ?></button>
        </div>
        <p id="mc-seo-loading" style="display:none;"><?php esc_html_e('Fetching...', 'my-creative-seo-aeo-readability'); ?></p>
        <div id="mc-seo-output" class="mc-output"></div>
      </div>
    </div>
    <style>
      .mc-card{background:#fff;border:1px solid #e8ecec;border-radius:16px;padding:16px;margin-top:12px}
      .mc-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
      .mc-output .good{color:#139e52;font-weight:700}.mc-output .warn{color:#ff9800;font-weight:700}.mc-output .bad{color:#e53935;font-weight:700}
      .mc-chip{display:inline-block;background:#eefdf7;border:1px solid #d4f7e8;border-radius:999px;padding:2px 8px;margin-right:6px}
      .mc-hero{background:linear-gradient(135deg,#ffffff 0%,#f7fffd 70%);border:1px solid #e8ecec;border-radius:16px;padding:14px;margin:10px 0}
      .mc-bar{height:10px;background:#eceff1;border-radius:999px;overflow:hidden}
      .mc-bar-fill{height:100%;background:linear-gradient(90deg,#00c875,#00fff0)}
    </style>
    <script>
    (function(){
      const btn = document.getElementById('mc-seo-go');
      const domainField = document.getElementById('mc-seo-domain');
      const loading = document.getElementById('mc-seo-loading');
      const out = document.getElementById('mc-seo-output');

      function esc(str){ return String(str||'').replace(/[&<>"']/g, s => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[s])); }
      function label(k){
        const labels = {title:'Title present',title_len:'Title 30â€“60 chars',description:'Meta description',description_len:'Description 90â€“160 chars',headings:'H1â€“H3 present',h1:'Exactly one H1',h2h3:'H2/H3 present',word_count:'~600+ words',readability:'â‰¤ 22 words/sentence',keywords_meta:'Meta keywords tag',keywords_usage:'Keywords echoed',viewport:'Viewport tag',favicon:'Favicon',structured_data:'Structured Data',canonical:'Canonical tag',noindex:'Indexable',robots:'robots.txt',sitemap:'sitemap.xml',load_time:'Speed < 2s',alt_text:'80%+ images have alt',links:'Has links'}; return labels[k]||k;
      }

      async function go(){
        const domainRaw = (domainField.value||'').trim();
        if(!domainRaw){ out.innerHTML = '<p class="bad">Enter a domain</p>'; return; }
        loading.style.display = 'block'; out.innerHTML = '';
        try {
          const url = new URL('<?php echo esc_js( rest_url( 'mc-seo/v1/analyze' ) ); ?>'); url.searchParams.set('domain', domainRaw);
          const res = await fetch(url.toString(), { headers: { 'X-WP-Nonce': '<?php echo esc_js( wp_create_nonce('wp_rest') ); ?>' } });
          if(!res.ok) throw new Error('Network error');
          const data = await res.json(); loading.style.display = 'none';
          if (data.error){ out.innerHTML = `<p class="bad">${esc(data.error)} ${data.status? '(HTTP '+data.status+')':''} ${data.curl? ' â€“ '+esc(data.curl):''}</p>`; return; }
          const hp = data.homepage || data; if (hp.score_breakdown){ if (typeof hp.robots_txt!=='undefined') hp.score_breakdown.robots=!!hp.robots_txt; if (typeof hp.sitemap_xml!=='undefined') hp.score_breakdown.sitemap=!!hp.sitemap_xml; }
          const scClass = hp.score>=8?'good':(hp.score>=5?'warn':'bad');
          let html = '';
          html += `<h2>Homepage</h2>`;
          html += `<div><span class="${scClass}" style="font-size:32px;font-weight:800">${hp.score||0}/10</span> <span class="mc-chip">Load: ${hp.load_time_ms||0} ms</span> <span class="mc-chip">Words: ${hp.word_count||0}</span> <span class="mc-chip">Avg W/S: ${hp.readability_avg_wps||'-'}</span> ${hp.aeo_ready?'<span class="mc-chip">AEO ready</span>':''}</div>`;
          if (hp.title) html += `<p><strong>Title:</strong> ${esc(hp.title)}</p>`;
          if (hp.description) html += `<p><strong>Description:</strong> ${esc(hp.description)}</p>`;
          if (hp.score_breakdown){
            html += `<details><summary><strong>Checks</strong></summary><ul>`;
            Object.keys(hp.score_breakdown).forEach(k=>{ const ok = hp.score_breakdown[k]; if (ok===null) return; html += `<li>${ok?'âœ…':'âŒ'} ${esc(label(k))}</li>`; });
            html += `</ul></details>`;
          }
          html += `<h3>Headings (H1â€“H3)</h3><ul>${(hp.headings||[]).slice(0,8).map(h=>`<li>${esc(h)}</li>`).join('')}</ul>`;
          html += `<h3>Top on-page keywords</h3><ul>${Object.entries(hp.top_keywords||{}).map(([w,c])=>`<li>${esc(w)} (${c})</li>`).join('')}</ul>`;
          const pages = data.top_pages || []; html += `<hr><h2>Estimated Top Pages</h2>`;
          if (!pages.length){ html += `<p>No pages detected.</p>`; } else {
            pages.forEach((p,i)=>{ const cls = p.score>=8?'good':(p.score>=5?'warn':'bad'); html += `<div style="border:1px solid #eee;border-radius:8px;padding:10px;margin:10px 0"><div style="font-size:12px;color:#666">${esc(p.url||'')}</div><strong>${esc(p.title||'(no title)')}</strong><div><span class="${cls}" style="font-weight:800">${p.score||0}/10</span> <span class="mc-chip">Load: ${p.load_time_ms||0} ms</span> <span class="mc-chip">Words: ${p.word_count||0}</span></div></div>`; });
          }
          out.innerHTML = html;
        } catch(e){ loading.style.display = 'none'; out.innerHTML = '<p class="bad">Error fetching data.</p>'; }
      }
      if (btn) btn.addEventListener('click', go);
      if (domainField) domainField.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); go(); } });
    })();
    </script>
    <?php
  }

  /* ---------------------------------------------------------------------
   * Bulk Editor (lite) â€“ titles & descriptions
   * ------------------------------------------------------------------ */
  public static function render_bulk_editor_page() {
    // Add contextual help
    self::add_bulk_editor_help_tabs();
    
    if ( isset($_POST['mc_seo_bulk_save']) && check_admin_referer('mc_seo_bulk') ) {
      foreach ( (array) ($_POST['rows'] ?? []) as $pid => $row ) {
        $pid = absint($pid);
        if ( ! current_user_can('edit_post',$pid) ) continue;
        update_post_meta( $pid, self::META_TITLE, sanitize_text_field( $row['t'] ?? '' ) );
        update_post_meta( $pid, self::META_DESC,  sanitize_textarea_field( $row['d'] ?? '' ) );
      }
      echo '<div class="updated"><p>Saved.</p></div>';
    }

    $q = new WP_Query([
      'post_type' => ['post','page'],
      'post_status' => 'any',
      'posts_per_page' => 50,
      'orderby' => 'modified',
      'order' => 'DESC',
    ]);
    ?>
    <div class="wrap">
      <h1>Bulk SEO Editor (lite)</h1>
      <form method="post">
        <?php wp_nonce_field('mc_seo_bulk'); ?>
        <table class="widefat fixed striped">
          <thead><tr><th>ID</th><th>Title</th><th>SEO Title</th><th>Meta Description</th></tr></thead>
          <tbody>
          <?php foreach ( $q->posts as $p ):
            $t = get_post_meta($p->ID, self::META_TITLE, true);
            $d = get_post_meta($p->ID, self::META_DESC,  true);
          ?>
            <tr>
              <td><?php echo (int)$p->ID; ?></td>
              <td><a href="<?php echo esc_url(get_edit_post_link($p->ID)); ?>"><?php echo esc_html(get_the_title($p)); ?></a></td>
              <td><input type="text" name="rows[<?php echo (int)$p->ID; ?>][t]" value="<?php echo esc_attr($t); ?>" style="width:100%" /></td>
              <td><textarea name="rows[<?php echo (int)$p->ID; ?>][d]" rows="2" style="width:100%"><?php echo esc_textarea($d); ?></textarea></td>
            </tr>
          <?php endforeach; ?>
          </tbody>
        </table>
        <p><button class="button button-primary" name="mc_seo_bulk_save" value="1">Save changes</button></p>
      </form>
    </div>
    <?php
  }

  /* ---------------------------------------------------------------------
   * REST: Analyzer endpoint
   * ------------------------------------------------------------------ */
  public static function register_rest_routes() {
    register_rest_route( 'mc-seo/v1', '/analyze', [
      'methods' => 'GET',
      'permission_callback' => function(){ return current_user_can('edit_posts'); },
      'args' => [
        'domain' => [ 'type' => 'string', 'required' => false ],
        'url'    => [ 'type' => 'string', 'required' => false ],
        'post_id'=> [ 'type' => 'integer','required' => false ],
      ],
      'callback' => function( WP_REST_Request $req ){
        $domain = trim( (string) $req->get_param('domain') );
        $url    = trim( (string) $req->get_param('url') );
        $post_id= absint( $req->get_param('post_id') );

        // Enhanced URL validation
        if ( $url && ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
          return new WP_Error('mc_invalid_url', 'Invalid URL format', [ 'status' => 400 ]);
        }
        
        if ( $domain ) {
          // Basic domain validation
          if ( ! preg_match('/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $domain) && stripos($domain, 'http') !== 0 ) {
            return new WP_Error('mc_invalid_domain', 'Invalid domain format', [ 'status' => 400 ]);
          }
        }

        if ( $post_id ) {
          $u = get_permalink( $post_id );
          if ( ! $u ) return new WP_Error('mc_bad_post', 'Invalid post_id', [ 'status' => 400 ]);
          return MC_SEO_Analyzer::analyze_domain( $u );
        }
        if ( $url )    return MC_SEO_Analyzer::analyze_domain( esc_url_raw( $url ) );
        if ( $domain ){ 
          if ( stripos($domain, 'http') !== 0 ) $domain = 'https://' . $domain; 
          return MC_SEO_Analyzer::analyze_domain( esc_url_raw( $domain ) ); 
        }
        return new WP_Error('mc_missing_param','Provide "domain", "url", or "post_id".',['status'=>400]);
      }
    ] );
  }
}
MC_SEO_Plugin::init();
