/**
 * Dashboard Tab JavaScript
 *
 * Handles trial activation, SEO calendar rendering, and dashboard interactivity.
 */

(function ($) {
  'use strict';

  // Calendar state
  let calendarData = [];
  let currentMonth = new Date();
  let calendarVariant = 'locked';
  let trialDayLimit = 7;
  let totalPlannedCount = 0; // Total planned posts across all months (from backend)

  // Setup loader state
  let setupPollingInterval = null;
  let statusCycleInterval = null;
  let setupStartTime = null;
  const SETUP_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
  const POLL_INTERVAL_MS = 15 * 1000; // 15 seconds
  const STATUS_CYCLE_MS = 4000; // 4 seconds

  // Status phrases for the setup loader
  const statusPhrases = [
    'Analyzing your content...',
    'Learning your writing style...',
    'Researching keywords...',
    'Studying your competitors...',
    'Finding content gaps...',
    'Identifying opportunities...',
    'Building your strategy...',
    'Planning your calendar...',
    'Organizing topics...',
    'Prioritizing keywords...',
    'Creating content briefs...',
    'Generating your first post...',
    'Almost there...'
  ];

  /**
   * Initialize dashboard functionality.
   */
  function initDashboard() {
    // Note: Trial start handler is in admin.js to avoid duplicate handlers
    // when both scripts are loaded on the same page.

    // Initialize calendar if present
    const $calendar = $('.saiap-seo-calendar');
    if ($calendar.length) {
      calendarVariant = $calendar.data('variant') || 'locked';
      trialDayLimit = parseInt($calendar.data('trial-day-limit'), 10) || 7;
      
      // Check if setup is running
      const isSetupRunning = $calendar.data('setup-running') === true || $calendar.data('setup-running') === 'true';
      
      if (isSetupRunning) {
        initSetupLoader();
      } else {
        initCalendar();
      }
    }

    // Autopilot settings handlers
    $(document).on('click', '.saiap-save-autopilot-settings', handleSaveAutopilotSettings);

    // Calendar navigation
    $(document).on('click', '.saiap-calendar-prev', navigatePrevMonth);
    $(document).on('click', '.saiap-calendar-next', navigateNextMonth);
    $(document).on('click', '.saiap-pipeline-rerun', handleRerunPipeline);

    // Calendar item interactions - clicking a card opens modal, locked items redirect to checkout
    $(document).on('click', '.saiap-calendar-item-card', handleCardClick);
    $(document).on('click', '.saiap-calendar-day--has-item.saiap-calendar-day--locked', handleLockedDayClick);
    $(document).on('click', '.saiap-calendar-item-card__view-link', handleViewPostClick);

    // Modal close
    $(document).on('click', '.saiap-modal__close, .saiap-modal__backdrop', closeModal);

    // GSC panel toggle
    initGscPanel();

    // Review prompt handlers
    $(document).on('click', '.saiap-review-btn-positive', handleReviewPositive);
    $(document).on('click', '.saiap-review-btn-negative', handleReviewNegative);
    $(document).on('click', '.saiap-review-btn-leave-review', handleReviewClick);
    $(document).on('click', '.saiap-review-dismiss-permanent', handleReviewDismissPermanent);
    $(document).on('click', '.saiap-review-dismiss-temp', handleReviewDismissTemp);
    // Treat X/backdrop close on the review modal as a temp dismiss so the
    // server records 'dismissed_temp'; otherwise the modal re-shows on every
    // page load (saiap_should_show_review_modal returns true while status is
    // empty). Generic closeModal still fires too and hides the DOM.
    $(document).on('click', '#saiap-review-modal .saiap-modal__close, #saiap-review-modal .saiap-modal__backdrop', handleReviewDismissTemp);
    $(document).on('click', '.saiap-review-btn-send-feedback', handleReviewSendFeedback);
    $(document).on('click', '.saiap-review-btn-close', closeReviewModal);

    // Setup retry handler
    $(document).on('click', '.saiap-setup-retry-btn', handleSetupRetry);

    // Generate Now button handler
    $(document).on('click', '.saiap-generate-now-btn', handleGenerateNow);
    $(document).on('click', '.saiap-change-date-btn', handleChangeDateClick);
    $(document).on('click', '.saiap-change-date-save', handleChangeDateSave);
    $(document).on('click', '.saiap-change-date-cancel', handleChangeDateCancel);
    $(document).on('click', '.saiap-skip-item-btn', handleSkipItem);
    $(document).on('click', '.saiap-edit-item-btn', handleEditItem);
    $(document).on('click', '.saiap-edit-save-btn', handleEditSave);
    $(document).on('click', '.saiap-edit-cancel-btn', handleEditCancel);

    // GSC onboarding init lives in the `$(document).ready` block at the bottom
    // of this file — calling it from here too binds the Skip handler twice and
    // fires two `/api/trial/setup` POSTs per click (BUG-StrategySessionDuplicateEvents).
  }

  // ===========================================================================
  // GSC Panel Toggle + Chart
  // ===========================================================================

  function initGscPanel() {
    var $panel = $('.saiap-panel-toggle');
    if (!$panel.length) return;

    // Icon toggle switching
    $panel.on('click', '.saiap-panel-toggle__icon', function () {
      var $icon = $(this);
      var panelName = $icon.data('panel');
      $panel.find('.saiap-panel-toggle__icon').removeClass('saiap-panel-toggle__icon--active');
      $icon.addClass('saiap-panel-toggle__icon--active');
      $panel.find('.saiap-panel-toggle__content').removeClass('saiap-panel-toggle__content--active').hide();
      $panel.find('.saiap-panel-toggle__content[data-panel="' + panelName + '"]').addClass('saiap-panel-toggle__content--active').show();
    });

    var hasLicense = $panel.data('has-license') === 1 || $panel.data('has-license') === '1';
    if (!hasLicense) {
      // No license — hide chart icon entirely, show settings only
      $panel.find('.saiap-panel-toggle__icons').hide();
      $panel.find('.saiap-panel-toggle__content[data-panel="chart"]').hide().removeClass('saiap-panel-toggle__content--active');
      $panel.find('.saiap-panel-toggle__content[data-panel="settings"]').show().addClass('saiap-panel-toggle__content--active');
      return;
    }

    // Check GSC status and load chart or CTA
    var statusUrl = $panel.data('gsc-status-url');
    var chartUrl = $panel.data('gsc-chart-url');
    var $loading = $('#saiap-gsc-chart-loading');
    var $canvas = $('#saiap-gsc-chart-root');
    var $cta = $('#saiap-gsc-chart-cta');
    var $empty = $('#saiap-gsc-chart-empty');

    fetch(statusUrl)
      .then(function (r) { return r.json(); })
      .then(function (status) {
        $loading.hide();
        if (!status.connected) {
          $cta.show();
          return;
        }
        // Connected — fetch chart data
        return fetch(chartUrl)
          .then(function (r) { return r.json(); })
          .then(function (data) {
            if (!data.days || data.days.length === 0) {
              $empty.show();
              return;
            }
            $canvas.show();
            $('#saiap-gsc-chart-tagline').show();
            window.renderGscChartReact(data.days);
          });
      })
      .catch(function () {
        $loading.hide();
        $cta.show();
      });
  }


  // ===========================================================================
  // Setup Loader Functions
  // ===========================================================================

  /**
   * Initialize the setup loader with polling and status cycling.
   */
  function initSetupLoader() {
    const $loader = $('.saiap-setup-loader');
    if (!$loader.length) {
      // No loader present, just load calendar
      initCalendar();
      return;
    }

    // Get setup start time from data attribute
    const setupTriggered = parseInt($loader.data('setup-triggered'), 10) || 0;
    setupStartTime = setupTriggered ? setupTriggered * 1000 : Date.now();

    // Start status cycling
    let statusIndex = 0;
    statusCycleInterval = setInterval(function () {
      statusIndex = (statusIndex + 1) % statusPhrases.length;
      cycleStatus(statusPhrases[statusIndex]);
    }, STATUS_CYCLE_MS);

    // Start polling for calendar items
    setupPollingInterval = setInterval(pollForCalendarItems, POLL_INTERVAL_MS);

    // Also poll immediately
    pollForCalendarItems();

    // Check for timeout periodically
    setInterval(checkSetupTimeout, 30000);
  }

  /**
   * Cycle the status text with a fade animation.
   */
  function cycleStatus(newText) {
    const $status = $('.saiap-setup-loader__status');
    if (!$status.length) return;

    $status.addClass('saiap-status-fade');
    
    setTimeout(function () {
      $status.text(newText);
      $status.removeClass('saiap-status-fade');
    }, 300);
  }

  /**
   * Poll for calendar items. When items exist, hide loader and show calendar.
   */
  function pollForCalendarItems() {
    const startDate = formatDate(new Date());
    const endDate = formatDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000));

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_calendar_items',
        nonce: saiapAdmin.trialNonce,
        start_date: startDate,
        end_date: endDate
      },
      success: function (response) {
        if (response.success && response.data.items && response.data.items.length > 0) {
          // Calendar items exist! Hide loader and show calendar
          onSetupComplete(response.data);
        }
        // If no items yet, keep polling
      },
      error: function () {
        // On error, keep polling - might be transient
      }
    });
  }

  /**
   * Handle setup completion - hide loader, show calendar, update status.
   */
  function onSetupComplete(data) {
    var items = data.items || [];

    // Stop polling and status cycling
    if (setupPollingInterval) {
      clearInterval(setupPollingInterval);
      setupPollingInterval = null;
    }
    if (statusCycleInterval) {
      clearInterval(statusCycleInterval);
      statusCycleInterval = null;
    }

    // Hide the setup loader
    const $loader = $('.saiap-setup-loader');
    $loader.fadeOut(300, function () {
      $(this).remove();
    });

    // Hide the calendar loading spinner
    $('.saiap-seo-calendar__loading').hide();

    // Update status steps to complete
    updateStatusStepsComplete();

    // Clear setup flag so page refresh shows correct state
    clearSetupFlag();

    // Mark calendar as having items
    $('.saiap-seo-calendar').addClass('saiap-seo-calendar--has-items');

    // Store the items and render calendar
    calendarData = items;
    // Update total planned count (all planned items across all months)
    if (typeof data.total_planned_count !== 'undefined') {
      totalPlannedCount = data.total_planned_count;
    }
    currentMonth = new Date();
    renderCalendar();

    // Update scheduled count (after totalPlannedCount is set)
    updateScheduledCount();

    // Track setup completion
    trackSetupEvent('setup_complete', {
      calendar_items: items.length
    });

    // Fetch and create first post draft
    createFirstPostDraft();
  }

  // First post polling state
  let firstPostPollingInterval = null;
  let firstPostPollCount = 0;
  const FIRST_POST_POLL_INTERVAL_MS = 10 * 1000; // 10 seconds
  const FIRST_POST_MAX_POLLS = 36; // 6 minutes max (36 * 10s)

  /**
   * Start polling for the first post to become available.
   * The first post is generated in Step 8, which runs after calendar generation.
   */
  function createFirstPostDraft() {
    firstPostPollCount = 0;
    showFirstPostGeneratingIndicator();
    pollForFirstPost();
  }

  /**
   * Show indicator that first post is being generated.
   */
  function showFirstPostGeneratingIndicator() {
    // Remove any existing indicator
    $('.saiap-first-post-generating').remove();

    var $indicator = $('<div class="saiap-first-post-generating">' +
      '<div class="saiap-first-post-generating__content">' +
        '<span class="saiap-first-post-generating__spinner"></span>' +
        '<span class="saiap-first-post-generating__text">Generating your first post...</span>' +
      '</div>' +
    '</div>');

    // Insert after the calendar header
    $('.saiap-seo-calendar__header').after($indicator);
    $indicator.hide().fadeIn(300);
  }

  /**
   * Update the indicator to show that the first post is ready.
   */
  function updateFirstPostIndicatorToReady(data) {
    var $indicator = $('.saiap-first-post-generating');
    if ($indicator.length === 0) {
      return;
    }

    // Update classes for success state
    $indicator.removeClass('saiap-first-post-generating--loading')
              .addClass('saiap-first-post-generating--ready');

    // Update content
    var title = data && data.title ? data.title : 'Your first post';
    var editUrl = data && data.post_id ? saiapAdmin.adminUrl + 'post.php?post=' + data.post_id + '&action=edit' : null;

    var newContent = '<div class="saiap-first-post-generating__content">' +
      '<span class="saiap-first-post-generating__checkmark">&#10003;</span>' +
      '<span class="saiap-first-post-generating__text">Your first post is ready: <strong>' + escapeHtml(title) + '</strong></span>' +
      (editUrl ? '<a href="' + editUrl + '" class="button button-small saiap-first-post-generating__button" target="_blank">Edit Draft</a>' : '') +
    '</div>';

    $indicator.html(newContent);
  }

  /**
   * Hide the generating indicator (for error/timeout cases).
   */
  function hideFirstPostGeneratingIndicator() {
    $('.saiap-first-post-generating').fadeOut(200, function () {
      $(this).remove();
    });
  }

  /**
   * Poll the backend for first post availability.
   */
  function pollForFirstPost() {
    firstPostPollCount++;

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_create_first_post_draft',
        nonce: saiapAdmin.formNonce
      },
      success: function (response) {
        if (response.success && response.data.post_id) {
          // First post created successfully
          stopFirstPostPolling();
          // Update banner to show ready state (keep it visible)
          updateFirstPostIndicatorToReady(response.data);
          // Refresh calendar and open modal for the first post
          openFirstPostModal(response.data);
          trackSetupEvent('first_post_created', {
            success: true,
            post_id: response.data.post_id
          });
        } else if (firstPostPollCount < FIRST_POST_MAX_POLLS) {
          // Not available yet, schedule retry
          firstPostPollingInterval = setTimeout(pollForFirstPost, FIRST_POST_POLL_INTERVAL_MS);
        } else {
          // Max polls reached, give up
          stopFirstPostPolling();
          hideFirstPostGeneratingIndicator();
          trackSetupEvent('first_post_created', {
            success: false,
            reason: 'timeout'
          });
        }
      },
      error: function () {
        if (firstPostPollCount < FIRST_POST_MAX_POLLS) {
          // Network error, retry
          firstPostPollingInterval = setTimeout(pollForFirstPost, FIRST_POST_POLL_INTERVAL_MS);
        } else {
          // Max polls reached, give up
          stopFirstPostPolling();
          hideFirstPostGeneratingIndicator();
          console.log('First post creation skipped after max retries');
          trackSetupEvent('first_post_created', {
            success: false,
            reason: 'network_error'
          });
        }
      }
    });
  }

  /**
   * Stop polling for first post.
   */
  function stopFirstPostPolling() {
    if (firstPostPollingInterval) {
      clearTimeout(firstPostPollingInterval);
      firstPostPollingInterval = null;
    }
  }

  /**
   * Refresh calendar and open modal for the first post.
   * This provides a better UX than a notification banner.
   */
  function openFirstPostModal(data) {
    var calendarItemId = data.calendar_item_id;
    var wpPostId = data.post_id;

    // Refresh calendar data to get the updated status (draft)
    var startDate = formatDate(new Date());
    var endDate = formatDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000));

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_calendar_items',
        nonce: saiapAdmin.trialNonce,
        start_date: startDate,
        end_date: endDate
      },
      success: function (response) {
        if (response.success && response.data.items) {
          // Update calendar data and re-render to show draft status (yellow)
          calendarData = response.data.items;
          renderCalendar();

          // Find the calendar item
          var item = null;
          if (calendarItemId) {
            item = calendarData.find(function (i) {
              return i.id === calendarItemId;
            });
          }

          // If we found the item, open its modal
          if (item) {
            // Ensure the item has the WP post ID for the modal
            item.wp_post_id = wpPostId;
            showItemModal(item);
          } else {
            // Fallback: just open the first item if we can't find the specific one
            if (calendarData.length > 0) {
              var firstItem = calendarData[0];
              firstItem.wp_post_id = wpPostId;
              showItemModal(firstItem);
            }
          }
        }
      },
      error: function () {
        // On error, still try to render calendar and open modal
        renderCalendar();
      }
    });
  }

  /**
   * Escape HTML for safe insertion.
   */
  function escapeHtml(text) {
    var div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }

  /**
   * Update all status steps to complete state.
   */
  function updateStatusStepsComplete() {
    var checkmarkSvg = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">' +
      '<path d="M13.3 4.3L6 11.6L2.7 8.3L3.4 7.6L6 10.2L12.6 3.6L13.3 4.3Z" fill="currentColor"/>' +
      '</svg>';

    var completedLabels = {
      'analyzing': 'Site analyzed',
      'strategy': 'SEO strategy created',
      'calendar': 'Managing content calendar'
    };

    $('.saiap-dashboard-status-step').each(function () {
      var $step = $(this);

      // Update class to complete
      $step.removeClass('saiap-dashboard-status-step--in-progress saiap-dashboard-status-step--pending')
           .addClass('saiap-dashboard-status-step--complete');

      // Replace icon with checkmark
      $step.find('.saiap-dashboard-status-step__icon').html(checkmarkSvg);

      // Update label to past tense based on step type
      var $label = $step.find('.saiap-dashboard-status-step__label');
      var labelText = $label.text().toLowerCase();

      if (labelText.indexOf('analyzing') !== -1) {
        $label.text(completedLabels['analyzing']);
      } else if (labelText.indexOf('strategy') !== -1 || labelText.indexOf('seo') !== -1) {
        $label.text(completedLabels['strategy']);
      }
    });
  }

  /**
   * Update the "Posts Scheduled" metric count.
   *
   * Uses totalPlannedCount from the backend (all planned items across all months,
   * not just the current month view).
   */
  function updateScheduledCount() {
    // Update the "Posts Scheduled" metric card value
    var $metricCards = $('.saiap-metric-card');
    $metricCards.each(function() {
      var $card = $(this);
      var labelText = $card.find('.saiap-metric-card__label').text().toLowerCase();
      if (labelText.indexOf('scheduled') !== -1) {
        $card.find('.saiap-metric-card__value').text(totalPlannedCount);
      }
    });
  }

  function checkCalendarCapHint() {
    var cap = parseInt(saiapAdmin.planCadenceCap, 10) || 0;
    if (!cap) return;

    var calendarCap = Math.ceil(cap * 30 / 7);
    var $hint = $('#saiap-calendar-cap-hint');

    if (totalPlannedCount >= calendarCap) {
      var nextTierCap = cap === 7 ? 14 : (cap === 14 ? 21 : 0);
      var nextTierPosts = nextTierCap ? Math.ceil(nextTierCap * 30 / 7) : 0;
      var msg = totalPlannedCount + '/' + calendarCap;
      if (nextTierPosts) {
        msg += ' · ';
      }

      if ($hint.length) {
        $hint.find('.saiap-calendar-cap-hint__count').text(msg);
        $hint.toggle(!!nextTierPosts || totalPlannedCount >= calendarCap);
      } else {
        var checkoutUrl = saiapAdmin.checkoutUrl || '#';
        var html = '<span id="saiap-calendar-cap-hint" class="saiap-calendar-cap-hint">' +
          '<span class="saiap-calendar-cap-hint__count">' + msg + '</span>';
        if (nextTierPosts) {
          html += '<a href="' + checkoutUrl + '" target="_blank" class="saiap-calendar-cap-hint__link">Upgrade for ' + nextTierPosts + '</a>';
        }
        html += '</span>';

        var $metricCards = $('.saiap-metric-card');
        $metricCards.each(function() {
          var labelText = $(this).find('.saiap-metric-card__label').text().toLowerCase();
          if (labelText.indexOf('scheduled') !== -1) {
            $(this).find('.saiap-metric-card__label').after(html);
          }
        });
      }
    } else if ($hint.length) {
      $hint.remove();
    }
  }

  /**
   * Clear the setup flag via AJAX.
   */
  function clearSetupFlag() {
    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_clear_setup_flag',
        nonce: saiapAdmin.trialNonce
      }
    });
  }

  /**
   * Check if setup has timed out.
   */
  function checkSetupTimeout() {
    if (!setupStartTime) return;

    const elapsed = Date.now() - setupStartTime;
    if (elapsed >= SETUP_TIMEOUT_MS) {
      showSetupTimeout();
    }
  }

  /**
   * Show the timeout message.
   */
  function showSetupTimeout() {
    // Stop status cycling
    if (statusCycleInterval) {
      clearInterval(statusCycleInterval);
      statusCycleInterval = null;
    }

    // Hide content, show timeout message
    const $loader = $('.saiap-setup-loader');
    $loader.find('.saiap-setup-loader__content').hide();
    $loader.find('.saiap-setup-loader__timeout').show();

    // Track setup timeout event
    trackSetupEvent('setup_timeout');

    // Keep polling - it might still complete
  }

  /**
   * Handle setup retry button click.
   */
  function handleSetupRetry(e) {
    e.preventDefault();

    const $btn = $(e.currentTarget);
    const originalText = $btn.text();

    // Disable button and show loading state
    $btn.prop('disabled', true).text('Retrying...');

    // Track retry attempt
    trackSetupEvent('setup_retry_attempted');

    // Clear setup flag and re-trigger setup
    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_retry_setup',
        nonce: saiapAdmin.trialNonce
      },
      success: function (response) {
        if (response.success) {
          // Setup re-triggered, reload the page to show fresh progress
          window.location.reload();
        } else {
          // Show error
          $btn.prop('disabled', false).text(originalText);
          showNotice(response.data?.message || 'Failed to retry setup. Please try again.', 'error');
        }
      },
      error: function () {
        $btn.prop('disabled', false).text(originalText);
        showNotice('Network error. Please check your connection and try again.', 'error');
      }
    });
  }

  /**
   * Track setup-related analytics events.
   */
  function trackSetupEvent(eventName, params) {
    params = params || {};
    params.source = saiapAdmin.trialSource || 'unknown';

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_track_setup_event',
        nonce: saiapAdmin.trialNonce,
        event_name: eventName,
        event_params: JSON.stringify(params)
      }
    });
  }

  // ===========================================================================
  // Calendar Functions
  // ===========================================================================
  // Note: handleStartTrial is defined in admin.js to avoid duplicate handlers
  // when both scripts are loaded on the same page.

  /**
   * Initialize the SEO calendar.
   */
  function initCalendar() {
    currentMonth = new Date();
    loadCalendarData();
  }

  /**
   * Load calendar data from the API.
   */
  function loadCalendarData() {
    const $grid = $('.saiap-seo-calendar__grid');
    const $loading = $grid.find('.saiap-seo-calendar__loading');

    // Show loading
    $loading.show();

    // Calculate date range (current month view)
    const startDate = getCalendarStartDate(currentMonth);
    const endDate = getCalendarEndDate(currentMonth, startDate);

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_calendar_items',
        nonce: saiapAdmin.trialNonce,
        start_date: formatDate(startDate),
        end_date: formatDate(endDate)
      },
      success: function (response) {
        $loading.hide();

        if (response.success) {
          calendarData = response.data.items || [];
          // Update total planned count (all planned items across all months)
          if (typeof response.data.total_planned_count !== 'undefined') {
            totalPlannedCount = response.data.total_planned_count;
          }
          if (response.data.plan_cadence_cap) {
            saiapAdmin.planCadenceCap = response.data.plan_cadence_cap;
          }
          renderCalendar();
          updateScheduledCount();
          checkCalendarCapHint();
        } else {
          // Handle error - might be trial expired or no access
          calendarData = [];
          renderCalendar();
          updateScheduledCount();

          if (response.data && response.data.error_code === 'trial_expired') {
            // Show upgrade prompt
            showCalendarUpgradePrompt();
          }
        }
      },
      error: function () {
        $loading.hide();
        calendarData = [];
        renderCalendar();
      }
    });
  }

  /**
   * Update the expired trial CTA with published/pending counts.
   */
  function updateExpiredTrialCTA() {
    if (calendarVariant !== 'trial_expired') {
      return;
    }

    const $message = $('#saiap-expired-message');
    if (!$message.length) {
      return;
    }

    $message.text('Resume automated publishing to keep your content engine running.');
  }

  /**
   * Render the calendar grid.
   */
  function renderCalendar() {
    const $grid = $('.saiap-seo-calendar__grid');
    const $monthLabel = $('.saiap-calendar-month-label');

    // Update month label
    $monthLabel.text(formatMonthYear(currentMonth));

    // Update expired trial CTA with counts
    updateExpiredTrialCTA();

    // Get calendar range
    const startDate = getCalendarStartDate(currentMonth);
    const endDate = getCalendarEndDate(currentMonth, startDate);

    // Calculate trial start for locked days determination
    const trialStartStr = getTrialStartDate();
    const trialStart = trialStartStr ? new Date(trialStartStr) : null;

    // Clear grid (except loading)
    $grid.find('.saiap-calendar-day').remove();

    // Generate day cells
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    let currentDate = new Date(startDate);
    while (currentDate <= endDate) {
      const dateStr = formatDate(currentDate);
      const dayNumber = currentDate.getDate();
      const isToday = currentDate.getTime() === today.getTime();
      const isCurrentMonth = currentDate.getMonth() === currentMonth.getMonth();

      // Find all calendar items for this day (support multiple posts per day)
      const items = calendarData.filter(
        (i) => i.scheduled_for && i.scheduled_for.substring(0, 10) === dateStr
      );

      // Determine if any item is locked or published
      const hasLockedItem = items.some((item) => isDayLocked(currentDate, trialStart, item));
      const hasPublishedItem = items.some((item) => isItemPublished(item));

      // Build class list
      const classes = ['saiap-calendar-day'];
      if (!isCurrentMonth) classes.push('saiap-calendar-day--other-month');
      if (isToday) classes.push('saiap-calendar-day--today');
      if (items.length > 0) classes.push('saiap-calendar-day--has-item');
      if (hasLockedItem) classes.push('saiap-calendar-day--locked');
      if (hasPublishedItem) classes.push('saiap-calendar-day--published');

      // Build HTML
      let html = '<div class="' + classes.join(' ') + '" data-date="' + dateStr + '">';

      html += '<span class="saiap-calendar-day__number">' + dayNumber + '</span>';

      // Render each item as a card (up to 2 per day)
      if (items.length > 0) {
        html += '<div class="saiap-calendar-day__items">';
        items.slice(0, 2).forEach(function(item) {
          const keyword = item.keyword || {};
          const keywordText = keyword.keyword || item.title || 'Untitled';
          const volume = keyword.search_volume || 0;
          const difficulty = keyword.keyword_difficulty || 0;

          // Determine status class for card color
          // NOTE: Calendar item statuses are: planned, pending_review, queued, generating,
          // processing, completed, published, failed, skipped.
          // Do NOT confuse with WordPress post statuses (draft, publish, pending, future).
          let statusClass = 'scheduled'; // neutral/gray for 'planned' items
          if (item.status === 'pending_review') {
            statusClass = 'pending-review';
          } else if (item.status === 'published' || item.status === 'completed') {
            statusClass = 'published';
          }

          // Add orphan class if this is an orphaned post (no matching calendar item)
          var orphanClass = item.is_orphan ? ' saiap-calendar-item-card--orphan' : '';
          html += '<div class="saiap-calendar-item-card saiap-calendar-item-card--' + statusClass + orphanClass + '" data-item-id="' + item.id + '">';
          // Add status badge for completed/published/pending_review items
          if (item.status === 'published' || item.status === 'completed') {
            html += '<span class="saiap-calendar-item-card__status-badge saiap-calendar-item-card__status-badge--published">Published</span>';
          } else if (item.status === 'pending_review') {
            html += '<span class="saiap-calendar-item-card__status-badge saiap-calendar-item-card__status-badge--pending-review">Pending Review</span>';
          }
          // Show "Manual" badge for orphaned posts (generated outside calendar workflow)
          if (item.is_orphan) {
            html += '<span class="saiap-calendar-item-card__status-badge saiap-calendar-item-card__status-badge--orphan">Manual</span>';
          }
          html += '<span class="saiap-calendar-item-card__keyword">' + escapeHtml(keywordText) + '</span>';
          // Only show volume/difficulty for calendar items (not orphans)
          if (!item.is_orphan) {
            html += '<span class="saiap-calendar-item-card__volume">Volume: <strong>' + formatNumber(volume) + '</strong></span>';
            html += '<span class="saiap-calendar-item-card__difficulty">Difficulty: <strong>' + difficulty + '</strong></span>';
          }
          // Add View/Edit Post link for published and pending_review items
          if (item.status === 'published' || item.status === 'completed') {
            html += '<span class="saiap-calendar-item-card__view-link" data-wp-post-id="' + (item.wp_post_id || '') + '">View Post →</span>';
          } else if (item.status === 'pending_review' && item.wp_post_id) {
            html += '<span class="saiap-calendar-item-card__view-link saiap-calendar-item-card__view-link--edit" data-wp-post-id="' + item.wp_post_id + '">Edit Post →</span>';
          }
          html += '</div>';
        });
        html += '</div>';
      }

      if (hasLockedItem) {
        html += '<div class="saiap-calendar-day__lock"><span class="dashicons dashicons-lock"></span></div>';
      }

      html += '</div>';

      $grid.append(html);

      // Move to next day
      currentDate.setDate(currentDate.getDate() + 1);
    }
  }

  /**
   * Navigate to previous month.
   */
  function navigatePrevMonth(e) {
    e.preventDefault();
    currentMonth.setMonth(currentMonth.getMonth() - 1);
    loadCalendarData();
  }

  /**
   * Navigate to next month.
   */
  function navigateNextMonth(e) {
    e.preventDefault();
    currentMonth.setMonth(currentMonth.getMonth() + 1);
    loadCalendarData();
  }

  /**
   * Refresh calendar data.
   */
  function refreshCalendar(e) {
    e.preventDefault();
    loadCalendarData();
  }

  // Regenerate polling state
  let regeneratePollingInterval = null;
  let regeneratePollCount = 0;
  const REGENERATE_POLL_INTERVAL_MS = 15 * 1000; // 15 seconds
  const REGENERATE_MAX_POLLS = 40; // 10 minutes max (40 * 15s)

  // Status phrases for the regenerate pipeline
  const pipelineStatusPhrases = [
    'Archiving old keywords...',
    'Starting strategy session...',
    'Generating seed keywords...',
    'Expanding keywords via API...',
    'Classifying keywords...',
    'Analyzing content gaps...',
    'Balancing location coverage...',
    'Selecting calendar keywords...',
    'Building your new calendar...',
    'Almost done...'
  ];

  /**
   * Handle re-run pipeline button click.
   * Archives keywords, re-runs full strategy session + research + calendar.
   */
  function handleRerunPipeline(e) {
    e.preventDefault();

    var confirmMessage = 'Regenerate your content calendar?\n\n' +
      'This will:\n' +
      '• Re-run keyword research from scratch\n' +
      '• Rebuild your content calendar with fresh keywords\n' +
      '• Keep any posts that are already published or in progress\n\n' +
      'This takes 3-5 minutes. Continue?';

    if (!confirm(confirmMessage)) {
      return;
    }

    var $btn = $(e.currentTarget);
    $btn.prop('disabled', true).addClass('saiap-btn-disabled');

    // Show banner with pipeline-specific phrases
    showRegeneratingBanner(pipelineStatusPhrases[0]);

    let statusIndex = 0;
    const statusCycleInterval = setInterval(function () {
      statusIndex = (statusIndex + 1) % pipelineStatusPhrases.length;
      updateRegeneratingStatus(pipelineStatusPhrases[statusIndex]);
    }, 6000); // Slower cycling since pipeline takes longer

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      timeout: 600000, // 10 minute timeout
      data: {
        action: 'saiap_rerun_pipeline',
        nonce: saiapAdmin.trialNonce
      },
      success: function (response) {
        clearInterval(statusCycleInterval);
        $btn.prop('disabled', false).removeClass('saiap-btn-disabled');

        if (response.success) {
          var data = response.data;
          var message = data.total + ' posts created, ' + data.keywords_discovered + ' keywords discovered';

          showRegeneratingSuccess(message);

          // Refresh calendar from the GET endpoint which includes keyword join data
          // (the pipeline response items don't include nested keyword objects)
          currentMonth = new Date();
          loadCalendarData();
        } else {
          hideRegeneratingBanner();
          showNotice(response.data?.message || 'Failed to re-run pipeline', 'error');
        }
      },
      error: function (xhr, status) {
        clearInterval(statusCycleInterval);
        $btn.prop('disabled', false).removeClass('saiap-btn-disabled');
        hideRegeneratingBanner();

        if (status === 'timeout') {
          showNotice('Pipeline re-run is taking longer than expected. Please refresh the page to check status.', 'warning');
        } else {
          showNotice('Network error. Please try again.', 'error');
        }
      }
    });
  }

  /**
   * Show the regenerating banner with spinner.
   */
  function showRegeneratingBanner(initialText) {
    // Remove any existing banner
    $('.saiap-regenerating-banner').remove();

    var text = initialText || 'Starting...';
    var $banner = $('<div class="saiap-regenerating-banner">' +
      '<div class="saiap-regenerating-banner__content">' +
        '<span class="saiap-regenerating-banner__spinner"></span>' +
        '<span class="saiap-regenerating-banner__text">' + text + '</span>' +
      '</div>' +
    '</div>');

    // Insert after the calendar header
    $('.saiap-seo-calendar__header').after($banner);
    $banner.hide().fadeIn(300);
  }

  /**
   * Update the regenerating banner status text.
   */
  function updateRegeneratingStatus(newText) {
    var $text = $('.saiap-regenerating-banner__text');
    if (!$text.length) return;

    $text.addClass('saiap-status-fade');
    setTimeout(function () {
      $text.text(newText);
      $text.removeClass('saiap-status-fade');
    }, 300);
  }

  /**
   * Show success state in regenerating banner.
   */
  function showRegeneratingSuccess(message) {
    var $banner = $('.saiap-regenerating-banner');
    if (!$banner.length) return;

    $banner.removeClass('saiap-regenerating-banner--loading')
           .addClass('saiap-regenerating-banner--success');

    $banner.html(
      '<div class="saiap-regenerating-banner__content">' +
        '<span class="saiap-regenerating-banner__checkmark">&#10003;</span>' +
        '<span class="saiap-regenerating-banner__text">Done! ' + escapeHtml(message) + '</span>' +
      '</div>'
    );

    // Auto-hide after 5 seconds
    setTimeout(function () {
      $banner.fadeOut(300, function () {
        $(this).remove();
      });
    }, 5000);
  }

  /**
   * Hide the regenerating banner.
   */
  function hideRegeneratingBanner() {
    $('.saiap-regenerating-banner').fadeOut(200, function () {
      $(this).remove();
    });
  }

  /**
   * Handle click on a calendar item card.
   */
  function handleCardClick(e) {
    e.stopPropagation(); // Prevent event bubbling to day cell
    const $card = $(e.currentTarget);
    const itemId = $card.data('item-id');

    if (!itemId) return;

    const item = calendarData.find((i) => i.id === itemId);
    if (!item) return;

    showItemModal(item);
  }

  /**
   * Handle click on a locked calendar day - redirect to checkout.
   */
  function handleLockedDayClick(e) {
    e.preventDefault();

    // Get checkout URL from localized script data
    const checkoutUrl = saiapAdmin.checkoutUrl || 'https://buy.stripe.com/7sY6oIfwqgPu1Za7vC3AY06';

    // Open checkout in new tab
    window.open(checkoutUrl, '_blank');
  }

  /**
   * Handle click on View/Edit Post link.
   */
  function handleViewPostClick(e) {
    e.stopPropagation(); // Prevent card click from triggering

    const $link = $(e.currentTarget);
    const wpPostId = $link.data('wp-post-id');

    if (!wpPostId) return;

    // Check if this is an edit link (pending_review) or view link (published)
    const isEdit = $link.hasClass('saiap-calendar-item-card__view-link--edit');

    // Build URL - edit goes to edit screen, view goes to post permalink
    let url;
    if (isEdit) {
      // Edit screen URL
      url = saiapAdmin.adminUrl + 'post.php?post=' + wpPostId + '&action=edit';
    } else {
      // View URL - use permalink if available, otherwise edit screen
      url = saiapAdmin.adminUrl + 'post.php?post=' + wpPostId + '&action=edit';
    }

    window.open(url, '_blank');
  }

  /**
   * Show calendar item modal.
   */
  function showItemModal(item) {
    const $modal = $('#saiap-calendar-modal');
    const $body = $modal.find('.saiap-modal__body');

    const keyword = item.keyword || {};
    const difficultyClass = getDifficultyClass(keyword.keyword_difficulty || 50);
    // NOTE: 'pending_review' is the calendar item status (content generated, awaiting human review).
    // This is separate from WordPress post status ('draft', 'publish', etc.).
    const isPendingReview = item.status === 'pending_review';

    let html = '<div class="saiap-calendar-item-detail">';

    // Prominent review banner (yellow) for pending_review items
    if (isPendingReview) {
      html += '<div class="saiap-draft-review-banner">';
      html += '<div class="saiap-draft-review-banner__icon">&#9998;</div>';
      html += '<div class="saiap-draft-review-banner__content">';
      html += '<strong>Ready for Review!</strong>';
      html += '<p>We generated this post for you. Review it and publish when ready.</p>';
      html += '</div>';
      if (item.wp_post_id) {
        var editUrl = saiapAdmin.adminUrl + 'post.php?post=' + item.wp_post_id + '&action=edit';
        html += '<a href="' + editUrl + '" class="button button-primary saiap-draft-review-banner__cta" target="_blank">Review &amp; Edit Draft</a>';
      } else {
        html += '<a href="' + saiapAdmin.adminUrl + 'edit.php" class="button button-primary saiap-draft-review-banner__cta" target="_blank">View Posts</a>';
      }
      html += '</div>';
    }

    // Title
    html += '<h3 class="saiap-calendar-item-detail__title">' + escapeHtml(item.title) + '</h3>';

    // Meta row (simplified - no need for status badge since we have the banner)
    html += '<div class="saiap-calendar-item-detail__meta">';
    html += '<span class="saiap-calendar-item-detail__date">' + escapeHtml(item.scheduled_for) + '</span>';
    if (item.keyword && item.keyword.content_type) {
      html +=
        '<span class="saiap-calendar-item-detail__type saiap-calendar-item-detail__type--' +
        item.keyword.content_type +
        '">' +
        escapeHtml(item.keyword.content_type) +
        '</span>';
    }
    if (item.keyword && item.keyword.funnel_stage) {
      html +=
        '<span class="saiap-calendar-item-detail__funnel-stage saiap-calendar-item-detail__funnel-stage--' +
        item.keyword.funnel_stage +
        '">' +
        getFunnelStageLabel(item.keyword.funnel_stage) +
        '</span>';
    }
    if (!isPendingReview && item.status) {
      // Only show status badge if not showing the review banner
      html +=
        '<span class="saiap-calendar-item-detail__status saiap-calendar-item-detail__status--' +
        item.status +
        '">' +
        getStatusLabel(item.status) +
        '</span>';
    }
    html += '</div>';

    // Keyword info
    if (keyword.keyword) {
      html += '<div class="saiap-calendar-item-detail__keyword">';
      html += '<strong>Target Keyword:</strong> ' + escapeHtml(keyword.keyword);
      if (keyword.search_volume || keyword.keyword_difficulty !== undefined) {
        html += '<div class="saiap-kw-metrics">';
        if (keyword.search_volume) {
          html += '<span class="saiap-kw-volume">Volume: ' + keyword.search_volume.toLocaleString() + '</span>';
        }
        if (keyword.keyword_difficulty !== undefined) {
          html +=
            '<span class="saiap-kw-difficulty saiap-kw-difficulty--' +
            difficultyClass +
            '">Difficulty: ' +
            keyword.keyword_difficulty +
            '</span>';
        }
        html += '</div>';
      }
      html += '</div>';
    }

    // Post Type Map
    html += '<div class="saiap-calendar-item-detail__post-type">';
    html += '<strong>Post Type:</strong> <span class="saiap-post-type-badge">Auto-infer</span>';
    html += '</div>';

    // Prompt
    if (item.prompt) {
      html += '<div class="saiap-calendar-item-detail__section">';
      html += '<h4>Prompt</h4>';
      html += '<p>' + escapeHtml(item.prompt) + '</p>';
      html += '</div>';
    }

    // Content brief
    if (item.content_brief) {
      html += '<div class="saiap-calendar-item-detail__section">';
      html += '<h4>Content Brief</h4>';
      html += '<pre>' + escapeHtml(item.content_brief) + '</pre>';
      html += '</div>';
    }

    // Actions - different buttons based on status
    html += '<div class="saiap-calendar-item-detail__actions">';
    const isPublished = item.status === 'published' || item.status === 'completed';

    if (isPendingReview) {
      // Pending review - skip/secondary action only (primary CTA is in banner above)
      html += '<button type="button" class="button saiap-skip-item-btn" data-item-id="' + item.id + '">Skip This Post</button>';
    } else if (isPublished && item.wp_post_id) {
      // Published - show View Post
      var viewUrl = saiapAdmin.adminUrl + 'post.php?post=' + item.wp_post_id + '&action=edit';
      html += '<a href="' + viewUrl + '" class="button button-primary" target="_blank">View Post</a>';
    } else {
      // Not yet generated - show Generate Now
      html += '<button type="button" class="button button-primary saiap-generate-now-btn" data-item-id="' + item.id + '">Generate Now</button>';
      html += '<button type="button" class="button saiap-edit-item-btn" data-item-id="' + item.id + '">Edit</button>';
      html += '<button type="button" class="button saiap-change-date-btn" data-item-id="' + item.id + '" data-current-date="' + escapeHtml(item.scheduled_for) + '">Change Date</button>';
      html += '<button type="button" class="button saiap-skip-item-btn" data-item-id="' + item.id + '">Skip</button>';
    }
    html += '</div>';

    html += '</div>';

    $body.html(html);
    $modal.css('display', 'flex');
  }

  /**
   * Handle Generate Now button click.
   * Uses async generation with SSE for long-running Opus model generations.
   */
  function handleGenerateNow(e) {
    e.preventDefault();
    e.stopPropagation();

    var $btn = $(this);
    var itemId = $btn.data('item-id');

    if (!itemId) {
      showNotice('Missing calendar item ID', 'error');
      return;
    }

    // Find the calendar item in our cached data
    var item = calendarData.find(function (i) {
      return i.id === itemId;
    });

    if (!item) {
      showNotice('Calendar item not found', 'error');
      return;
    }

    // Disable button and show loading state
    var originalText = $btn.text();
    $btn.prop('disabled', true).text('Starting...');

    // Show generating progress message in modal
    var $detail = $('.saiap-calendar-item-detail');
    var $existingBanner = $detail.find('.saiap-generate-progress-banner, .saiap-generate-success-banner');
    $existingBanner.remove();

    var progressHtml = '<div class="saiap-generate-progress-banner">' +
      '<div class="saiap-generate-progress-banner__icon"><span class="spinner is-active"></span></div>' +
      '<div class="saiap-generate-progress-banner__content">' +
      '<strong>Starting generation...</strong>' +
      '<p>Connecting to AI service...</p>' +
      '</div>' +
      '</div>';
    $detail.prepend(progressHtml);

    // Step 1: Start async generation to get stream URL
    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      timeout: 30000, // 30 seconds to start the job
      data: {
        action: 'saiap_generate_from_calendar',
        nonce: saiapAdmin.trialNonce,
        item_id: itemId,
        prompt: item.prompt || '',
        content_brief: item.content_brief || '',
        title: item.title || ''
      },
      success: function (response) {
        if (response.success && response.data.stream_url) {
          $btn.text('Generating...');
          // Step 2: Connect to SSE stream for real-time updates
          connectToGenerateStream(
            response.data.stream_url,
            item,
            response.data.item_id,
            response.data.title || item.title || '',
            $btn,
            originalText
          );
        } else {
          handleGenerateError($btn, originalText, response.data?.message || 'Failed to start generation');
        }
      },
      error: function () {
        handleGenerateError($btn, originalText, 'Network error starting generation.');
      }
    });
  }

  /**
   * Connect to SSE stream for async generation status updates.
   */
  function connectToGenerateStream(streamUrl, item, itemId, title, $btn, originalText) {
    var $detail = $('.saiap-calendar-item-detail');
    var eventSource = new EventSource(streamUrl);
    var generatedContent = null;

    // Update progress banner with current state
    eventSource.addEventListener('status', function (e) {
      try {
        var data = JSON.parse(e.data);
        updateGenerateProgressBanner(data.state);
      } catch (err) {
        // Ignore parse errors
      }
    });

    // Generation complete - create the WordPress post
    eventSource.addEventListener('done', function (e) {
      eventSource.close();
      try {
        generatedContent = JSON.parse(e.data);
        updateGenerateProgressBanner('saving');
        // Step 3: Create the WordPress post with generated content
        createCalendarPost(generatedContent, item, itemId, title, $btn, originalText);
      } catch (err) {
        handleGenerateError($btn, originalText, 'Failed to parse generated content');
      }
    });

    // Generation failed
    eventSource.addEventListener('error', function (e) {
      eventSource.close();
      try {
        var data = JSON.parse(e.data);
        handleGenerateError($btn, originalText, data.message || 'Generation failed');
      } catch (err) {
        handleGenerateError($btn, originalText, 'Generation failed');
      }
    });

    // Connection error - EventSource auto-reconnects, but we handle persistent failures
    eventSource.onerror = function () {
      // EventSource will automatically try to reconnect
      // Only close after the connection is truly dead (readyState === CLOSED)
      if (eventSource.readyState === EventSource.CLOSED) {
        handleGenerateError($btn, originalText, 'Lost connection to generation service');
      }
    };

    // Safety timeout - close after 10 minutes
    setTimeout(function () {
      if (eventSource.readyState !== EventSource.CLOSED) {
        eventSource.close();
        handleGenerateError($btn, originalText, 'Generation timed out');
      }
    }, 600000);
  }

  /**
   * Update progress banner with generation state.
   */
  function updateGenerateProgressBanner(state) {
    var $banner = $('.saiap-generate-progress-banner__content');
    if (!$banner.length) return;

    var messages = {
      'queued': { title: 'Starting generation...', desc: 'Queued for processing...' },
      'analyzing': { title: 'Generating your post...', desc: 'AI is writing your content. This may take a few minutes.' },
      'saving': { title: 'Creating post...', desc: 'Saving to WordPress...' },
      'completed': { title: 'Done!', desc: 'Post created successfully.' }
    };

    var msg = messages[state] || { title: 'Generating your post...', desc: 'Please wait...' };
    $banner.html('<strong>' + msg.title + '</strong><p>' + msg.desc + '</p>');
  }

  /**
   * Create WordPress post from generated content.
   */
  function createCalendarPost(generatedContent, item, itemId, title, $btn, originalText) {
    var $detail = $('.saiap-calendar-item-detail');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      timeout: 60000, // 60 seconds to create post
      data: {
        action: 'saiap_create_calendar_post',
        nonce: saiapAdmin.trialNonce,
        item_id: itemId,
        title: title || generatedContent.title || '',
        content: generatedContent.content || '',
        link: generatedContent.link || '',
        linkTitle: generatedContent.linkTitle || '',
        inline_image_tasks: generatedContent.meta?.inline_image_tasks ? JSON.stringify(generatedContent.meta.inline_image_tasks) : '',
        session_id: generatedContent.session_id || ''
      },
      success: function (response) {
        // Remove progress banner
        $detail.find('.saiap-generate-progress-banner').remove();

        if (response.success) {
          var postId = response.data?.post_id;
          var postStatus = response.data?.post_status || 'draft';
          var isPublished = postStatus === 'publish';
          var editUrl = postId ? saiapAdmin.adminUrl + 'post.php?post=' + postId + '&action=edit' : null;

          // Show success banner in modal with appropriate messaging
          var bannerText = isPublished
            ? 'Your post has been published!'
            : 'Your post has been created as a draft. Review it and publish when ready.';
          var buttonText = isPublished ? 'View Post' : 'Review &amp; Edit Draft';

          var successHtml = '<div class="saiap-generate-success-banner">' +
            '<div class="saiap-generate-success-banner__icon">&#10003;</div>' +
            '<div class="saiap-generate-success-banner__content">' +
            '<strong>Post Generated Successfully!</strong>' +
            '<p>' + bannerText + '</p>' +
            '</div>' +
            (editUrl ? '<a href="' + editUrl + '" class="button button-primary saiap-generate-success-banner__cta" target="_blank">' + buttonText + '</a>' : '') +
            '</div>';
          $detail.prepend(successHtml);

          // Update the item in cache
          item.status = isPublished ? 'published' : 'pending_review';
          item.wp_post_id = postId;

          // Hide the actions section
          $detail.find('.saiap-calendar-item-detail__actions').hide();

          // Refresh calendar data in background
          loadCalendarData();
        } else {
          handleGenerateError($btn, originalText, response.data?.message || 'Failed to create post');
        }
      },
      error: function () {
        handleGenerateError($btn, originalText, 'Network error creating post.');
      }
    });
  }

  /**
   * Handle generation error - show error banner and reset button.
   */
  function handleGenerateError($btn, originalText, message) {
    var $detail = $('.saiap-calendar-item-detail');
    $detail.find('.saiap-generate-progress-banner').remove();
    $btn.prop('disabled', false).text(originalText);
    showModalError(message || 'Generation failed');
  }

  /**
   * Show an error message inside the modal.
   */
  function showModalError(message) {
    var $detail = $('.saiap-calendar-item-detail');
    var $existingError = $detail.find('.saiap-modal-error-banner');
    $existingError.remove();

    var errorHtml = '<div class="saiap-modal-error-banner">' +
      '<div class="saiap-modal-error-banner__icon">&#10007;</div>' +
      '<div class="saiap-modal-error-banner__content">' +
      '<strong>Error</strong>' +
      '<p>' + escapeHtml(message) + '</p>' +
      '</div>' +
      '</div>';
    $detail.prepend(errorHtml);
  }

  /**
   * Handle Change Date button click - show inline date picker.
   */
  function handleChangeDateClick(e) {
    e.preventDefault();
    e.stopPropagation();

    var $btn = $(this);
    var itemId = $btn.data('item-id');
    var currentDate = $btn.data('current-date') || '';

    // Extract just the date part (YYYY-MM-DD) if it includes time
    if (currentDate && currentDate.length > 10) {
      currentDate = currentDate.substring(0, 10);
    }

    // Hide the button and show inline date picker
    $btn.hide();

    var pickerHtml = '<div class="saiap-date-edit-inline" data-item-id="' + itemId + '">' +
      '<input type="date" class="saiap-date-edit-input" value="' + escapeHtml(currentDate) + '">' +
      '<button type="button" class="button button-primary saiap-change-date-save">Save</button>' +
      '<button type="button" class="button saiap-change-date-cancel">Cancel</button>' +
      '</div>';

    $btn.after(pickerHtml);
  }

  /**
   * Handle Change Date cancel - restore the button.
   */
  function handleChangeDateCancel(e) {
    e.preventDefault();
    e.stopPropagation();

    var $picker = $(this).closest('.saiap-date-edit-inline');
    var $btn = $picker.prev('.saiap-change-date-btn');

    $picker.remove();
    $btn.show();
  }

  /**
   * Handle Change Date save - update via AJAX.
   */
  function handleChangeDateSave(e) {
    e.preventDefault();
    e.stopPropagation();

    var $saveBtn = $(this);
    var $picker = $saveBtn.closest('.saiap-date-edit-inline');
    var itemId = $picker.data('item-id');
    var newDate = $picker.find('.saiap-date-edit-input').val();

    if (!newDate) {
      showNotice('Please select a date', 'error');
      return;
    }

    // Validate date format
    if (!/^\d{4}-\d{2}-\d{2}$/.test(newDate)) {
      showNotice('Invalid date format', 'error');
      return;
    }

    // Disable buttons during save
    $saveBtn.prop('disabled', true).text('Saving...');
    $picker.find('.saiap-change-date-cancel').prop('disabled', true);

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_update_calendar_item',
        nonce: saiapAdmin.trialNonce,
        item_id: itemId,
        scheduled_for: newDate
      },
      success: function (response) {
        if (response.success) {
          showNotice('Date updated successfully', 'success');
          closeModal();
          loadCalendarData();
        } else {
          showNotice(response.data?.message || 'Failed to update date', 'error');
          $saveBtn.prop('disabled', false).text('Save');
          $picker.find('.saiap-change-date-cancel').prop('disabled', false);
        }
      },
      error: function () {
        showNotice('Network error updating date', 'error');
        $saveBtn.prop('disabled', false).text('Save');
        $picker.find('.saiap-change-date-cancel').prop('disabled', false);
      }
    });
  }

  /**
   * Handle Skip button click - replace calendar item with different keyword.
   */
  function handleSkipItem(e) {
    e.preventDefault();
    e.stopPropagation();

    var $btn = $(this);
    var itemId = $btn.data('item-id');

    if (!itemId) {
      showNotice('Missing calendar item ID', 'error');
      return;
    }

    var originalText = $btn.text();
    $btn.prop('disabled', true).text('Finding replacement...');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      timeout: 90000, // AI generation can take time
      data: {
        action: 'saiap_replace_calendar_item',
        nonce: saiapAdmin.trialNonce,
        item_id: itemId
      },
      success: function (response) {
        if (response.success) {
          showNotice('Replaced with new topic: ' + (response.data?.item?.title || 'New item'), 'success');
          closeModal();
          loadCalendarData();
        } else {
          showNotice(response.data?.message || 'Failed to replace item', 'error');
          $btn.prop('disabled', false).text(originalText);
        }
      },
      error: function () {
        showNotice('Network error replacing item', 'error');
        $btn.prop('disabled', false).text(originalText);
      }
    });
  }

  function handleEditItem(e) {
    e.preventDefault();
    e.stopPropagation();

    var $btn = $(this);
    var itemId = $btn.data('item-id');
    var $detail = $btn.closest('.saiap-calendar-item-detail');
    var $titleEl = $detail.find('h3').first();
    var currentTitle = $titleEl.text();

    $titleEl.html(
      '<input type="text" class="saiap-edit-title-input" value="' + escapeHtml(currentTitle) + '" style="width:100%;font-size:inherit;padding:4px 8px;" />'
    );

    $btn.closest('.saiap-calendar-item-detail__actions').html(
      '<button type="button" class="button button-primary saiap-edit-save-btn" data-item-id="' + itemId + '">Save</button>' +
      '<button type="button" class="button saiap-edit-cancel-btn" data-original-title="' + escapeHtml(currentTitle) + '">Cancel</button>'
    );
  }

  function handleEditSave(e) {
    e.preventDefault();
    var $btn = $(this);
    var itemId = $btn.data('item-id');
    var $detail = $btn.closest('.saiap-calendar-item-detail');
    var newTitle = $detail.find('.saiap-edit-title-input').val().trim();

    if (!newTitle) {
      showNotice('Title cannot be empty', 'error');
      return;
    }

    $btn.prop('disabled', true).text('Saving...');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_update_calendar_item',
        nonce: saiapAdmin.trialNonce,
        item_id: itemId,
        title: newTitle
      },
      success: function (response) {
        if (response.success) {
          showNotice('Title updated', 'success');
          closeModal();
          loadCalendarData();
        } else {
          showNotice(response.data?.message || 'Failed to update title', 'error');
          $btn.prop('disabled', false).text('Save');
        }
      },
      error: function () {
        showNotice('Network error updating title', 'error');
        $btn.prop('disabled', false).text('Save');
      }
    });
  }

  function handleEditCancel(e) {
    e.preventDefault();
    var $btn = $(this);
    var originalTitle = $btn.data('original-title');
    var $detail = $btn.closest('.saiap-calendar-item-detail');
    $detail.find('h3').first().text(originalTitle);
    closeModal();
    loadCalendarData();
  }

  function closeModal(e) {
    if (e && e.currentTarget) {
      $(e.currentTarget).closest('.saiap-modal').css('display', 'none');
    } else {
      $('.saiap-modal:visible').css('display', 'none');
    }
  }

  /**
   * Show upgrade prompt for locked calendar.
   */
  function showCalendarUpgradePrompt() {
    // The overlay is already rendered by PHP for trial_expired variant
    // This function could show an additional modal if needed
  }

  // ===========================================================================
  // Autopilot Settings
  // ===========================================================================

  /**
   * Handle save autopilot settings.
   */
  function handleSaveAutopilotSettings(e) {
    e.preventDefault();

    const $btn = $(this);
    const originalText = $btn.text();

    $btn.prop('disabled', true).text('Saving...');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_save_autopilot_settings',
        nonce: saiapAdmin.trialNonce,
        autopilot_enabled: $('#saiap-autopilot-enabled').is(':checked') ? 'true' : 'false',
        posts_per_week: $('#saiap-posts-per-week').val(),
        review_before_publish: $('#saiap-review-before-publish').is(':checked') ? 'true' : 'false',
        model: $('#saiap-model-select').val()
      },
      success: function (response) {
        $btn.prop('disabled', false).text(originalText);

        if (response.success) {
          var cap = response.data?.plan_cadence_cap;
          var savedPpw = response.data?.settings?.posts_per_week;
          var requestedPpw = parseInt($('#saiap-posts-per-week').val(), 10);

          if (cap && savedPpw && savedPpw < requestedPpw) {
            $('#saiap-posts-per-week').val(savedPpw);
            showNotice('Your plan includes up to ' + cap + ' posts/week. Upgrade for more.', 'info');
          } else {
            showNotice('Settings saved successfully!', 'success');
          }

          if (cap) {
            var $sel = $('#saiap-posts-per-week');
            $sel.find('option').filter(function() { return parseInt($(this).val(), 10) > cap; }).remove();
          }
        } else {
          showNotice(response.data?.message || 'Failed to save settings', 'error');
        }
      },
      error: function () {
        $btn.prop('disabled', false).text(originalText);
        showNotice('Network error. Please try again.', 'error');
      }
    });
  }

  // ===========================================================================
  // Helper Functions
  // ===========================================================================

  /**
   * Get the start date for the calendar grid.
   * Always starts from Sunday of the week containing the 1st of the month.
   */
  function getCalendarStartDate(month) {
    const firstDay = new Date(month.getFullYear(), month.getMonth(), 1);
    const dayOfWeek = firstDay.getDay();
    // Sunday is 0, so subtract the day of week to get to Sunday
    firstDay.setDate(firstDay.getDate() - dayOfWeek);
    return firstDay;
  }

  /**
   * Get the end date for the calendar grid.
   * Always shows 5-6 weeks total, extending into next month if needed.
   */
  function getCalendarEndDate(month, startDate) {
    // Calculate how many days from start to end (5-6 weeks = 35-42 days)
    // We want to show at least 5 full weeks (35 days) to fill the calendar grid
    const weeksToShow = 5;
    const daysToShow = weeksToShow * 7 - 1; // -1 because we include start date
    
    const endDate = new Date(startDate);
    endDate.setDate(startDate.getDate() + daysToShow);
    return endDate;
  }

  /**
   * Format date as YYYY-MM-DD.
   */
  function formatDate(date) {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    return year + '-' + month + '-' + day;
  }

  /**
   * Format month and year label.
   */
  function formatMonthYear(date) {
    const months = [
      'January',
      'February',
      'March',
      'April',
      'May',
      'June',
      'July',
      'August',
      'September',
      'October',
      'November',
      'December'
    ];
    return months[date.getMonth()] + ' ' + date.getFullYear();
  }

  /**
   * Get trial start date from local storage or option.
   */
  function getTrialStartDate() {
    // This would be passed from PHP via wp_localize_script
    // For now, we check if there's a data attribute
    return null;
  }

  /**
   * Check if a calendar item should be locked based on its status.
   * For expired trials, only published items are accessible.
   *
   * @param {Object} item Calendar item with status field.
   * @returns {boolean} True if item should be locked.
   */
  function isItemLocked(item) {
    if (calendarVariant !== 'trial_expired') {
      return false;
    }

    // Published items are never locked (user accomplished these during trial)
    if (item && item.status === 'published') {
      return false;
    }

    // All other statuses are locked for expired trials
    return true;
  }

  /**
   * Check if a calendar item is published or completed.
   *
   * @param {Object} item Calendar item.
   * @returns {boolean} True if item is published or completed.
   */
  function isItemPublished(item) {
    return item && (item.status === 'published' || item.status === 'completed');
  }

  /**
   * Determine if a day is locked (for expired trial).
   * Now uses status-based locking instead of date-based.
   */
  function isDayLocked(date, trialStart, item) {
    if (calendarVariant !== 'trial_expired') {
      return false;
    }

    // Use status-based locking for items
    if (item) {
      return isItemLocked(item);
    }

    // Empty days in expired trial are not locked (nothing to lock)
    return false;
  }

  /**
   * Get difficulty class based on score.
   */
  function getDifficultyClass(difficulty) {
    if (difficulty < 30) return 'easy';
    if (difficulty < 60) return 'medium';
    return 'hard';
  }

  /**
   * Get human-readable status label.
   */
  function getStatusLabel(status) {
    const labels = {
      planned: 'Planned',
      pending_review: 'Pending Review',
      queued: 'Queued',
      generating: 'Generating',
      completed: 'Published',
      published: 'Published',
      skipped: 'Skipped'
    };
    return labels[status] || status;
  }

  /**
   * Get human-readable funnel stage label.
   */
  function getFunnelStageLabel(funnelStage) {
    const labels = {
      informational: 'Informational',
      consideration: 'Consideration',
      decision: 'Decision'
    };
    return labels[funnelStage] || funnelStage;
  }

  /**
   * Truncate string to max length.
   */
  function truncate(str, maxLength) {
    if (!str) return '';
    if (str.length <= maxLength) return str;
    return str.substring(0, maxLength - 3) + '...';
  }

  /**
   * Escape HTML special characters.
   */
  function escapeHtml(str) {
    if (!str) return '';
    return String(str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#039;');
  }

  /**
   * Format a number with K/M suffixes for large values.
   */
  function formatNumber(num) {
    if (!num || num === 0) return '0';
    if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
    if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
    return String(num);
  }

  /**
   * Show a temporary notice.
   */
  function showNotice(message, type) {
    const $notice = $(
      '<div class="notice notice-' + type + ' is-dismissible"><p>' + escapeHtml(message) + '</p></div>'
    );
    $('.saiap-dashboard').prepend($notice);

    // Auto-dismiss after 5 seconds
    setTimeout(function () {
      $notice.fadeOut(function () {
        $(this).remove();
      });
    }, 5000);
  }

  // ===========================================================================
  // Rank Tracker Functions
  // ===========================================================================

  /**
   * Initialize the rank tracker section.
   */
  function initRankTracker() {
    var $tracker = $('.saiap-rank-tracker');
    if (!$tracker.length) return;

    // Load tracked keywords
    loadTrackedKeywords();

    // Event handlers
    $(document).on('change', '.saiap-rank-keyword__track-toggle input', handleTrackingToggle);

    // Add Keyword modal handlers
    $(document).on('click', '.saiap-add-keyword-btn', openAddKeywordModal);
    $(document).on('click', '.saiap-add-keyword-modal__close, .saiap-add-keyword-modal__backdrop, .saiap-add-keyword-cancel', closeAddKeywordModal);
    $(document).on('click', '.saiap-add-keyword-tab', handleAddKeywordTabSwitch);
    $(document).on('click', '.saiap-add-keyword-submit', handleAddKeywordSubmit);
  }

  /**
   * Load tracked keywords from API.
   */
  function loadTrackedKeywords() {
    var $list = $('.saiap-rank-tracker__list');
    var $loading = $list.find('.saiap-rank-tracker__loading');
    var $empty = $('.saiap-rank-tracker__empty');

    $loading.show();
    $empty.hide();

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_tracked_keywords',
        nonce: saiapAdmin.trialNonce
      },
      success: function (response) {
        $loading.hide();

        if (response.success && response.data) {
          var keywords = response.data.keywords || [];
          var trackedCount = response.data.tracked_count || 0;

          // Update tracked count
          $('#saiap-tracked-count').text(trackedCount);

          if (keywords.length === 0) {
            $empty.show();
            $list.find('.saiap-rank-keyword').remove();
          } else {
            $empty.hide();
            renderTrackedKeywords(keywords);
          }
        } else {
          $empty.show();
        }
      },
      error: function () {
        $loading.hide();
        $empty.show();
      }
    });
  }

  /**
   * Render tracked keywords list.
   */
  function renderTrackedKeywords(keywords) {
    var $list = $('.saiap-rank-tracker__list');
    $list.find('.saiap-rank-keyword').remove();

    keywords.forEach(function (kw) {
      var html = buildTrackedKeywordHtml(kw);
      $list.append(html);

      // Load sparkline for this keyword if it has history
      if (kw.last_rank_check) {
        loadSparkline(kw.id);
      }
    });
  }

  /**
   * Build HTML for a single tracked keyword row.
   */
  function buildTrackedKeywordHtml(kw) {
    var rankBadge = getRankBadgeHtml(kw.current_rank, kw.last_rank_check);
    var changeHtml = getRankChangeHtml(kw.rank_change, kw.last_rank_check);
    var hasHistory = kw.history && kw.history.length > 1;

    // Format volume and difficulty
    var volumeStr = kw.search_volume ? formatNumber(kw.search_volume) : '—';
    var difficultyStr = kw.keyword_difficulty !== null && kw.keyword_difficulty !== undefined
      ? Math.round(kw.keyword_difficulty)
      : '—';
    var difficultyClass = kw.keyword_difficulty !== null && kw.keyword_difficulty !== undefined
      ? getDifficultyClass(kw.keyword_difficulty)
      : '';

    var html = '<div class="saiap-rank-keyword" data-keyword-id="' + kw.id + '">';
    html += '<span class="saiap-rank-keyword__name" title="' + escapeHtml(kw.keyword) + '">' + escapeHtml(kw.keyword) + '</span>';
    html += '<span class="saiap-rank-keyword__volume">' + volumeStr + '</span>';
    html += '<span class="saiap-rank-keyword__difficulty' + (difficultyClass ? ' saiap-rank-keyword__difficulty--' + difficultyClass : '') + '">' + difficultyStr + '</span>';
    html += rankBadge;
    html += changeHtml;

    // Sparkline or "no data" placeholder
    html += '<div class="saiap-rank-keyword__sparkline-wrap">';
    if (hasHistory) {
      html += '<canvas class="saiap-rank-keyword__sparkline" id="sparkline-' + kw.id + '"></canvas>';
    } else {
      html += '<span class="saiap-rank-keyword__no-data">No data yet</span>';
    }
    html += '</div>';

    // Styled checkbox toggle (reuses saiap-toggle-label from autopilot section)
    html += '<label class="saiap-rank-keyword__track-toggle saiap-toggle-label">';
    html += '<input type="checkbox" checked data-keyword-id="' + kw.id + '">';
    html += '</label>';
    html += '</div>';

    return html;
  }

  /**
   * Get rank badge HTML with appropriate styling.
   */
  function getRankBadgeHtml(rank, lastRankCheck) {
    if (rank === null || rank === undefined) {
      // Show different message based on whether we've checked yet
      var message = lastRankCheck ? '>100' : 'Pending';
      return '<span class="saiap-rank-keyword__badge saiap-rank-keyword__badge--unranked">' + message + '</span>';
    }

    var badgeClass = 'other';
    if (rank <= 10) {
      badgeClass = 'top10';
    } else if (rank <= 20) {
      badgeClass = 'top20';
    }

    return '<span class="saiap-rank-keyword__badge saiap-rank-keyword__badge--' + badgeClass + '">#' + rank + '</span>';
  }

  /**
   * Get rank change HTML with arrow and color.
   */
  function getRankChangeHtml(change, lastRankCheck) {
    if (change === null || change === undefined || change === 0) {
      // Show "—" only if we've checked, otherwise empty for pending state
      var display = lastRankCheck ? '—' : '';
      return '<span class="saiap-rank-keyword__change saiap-rank-keyword__change--none">' + display + '</span>';
    }

    if (change > 0) {
      return '<span class="saiap-rank-keyword__change saiap-rank-keyword__change--up">↑' + change + '</span>';
    } else {
      return '<span class="saiap-rank-keyword__change saiap-rank-keyword__change--down">↓' + Math.abs(change) + '</span>';
    }
  }

  /**
   * Load and render sparkline for a keyword.
   */
  function loadSparkline(keywordId) {
    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_rank_history',
        nonce: saiapAdmin.trialNonce,
        keyword_id: keywordId,
        days: 30
      },
      success: function (response) {
        if (response.success && response.data && response.data.history) {
          renderSparkline(keywordId, response.data.history);
        }
      }
    });
  }

  /**
   * Render sparkline using Canvas API (lightweight, no dependencies).
   */
  function renderSparkline(keywordId, history) {
    var canvas = document.getElementById('sparkline-' + keywordId);
    if (!canvas || !history || history.length < 2) return;

    var ctx = canvas.getContext('2d');
    var width = canvas.width;
    var height = canvas.height;

    // Extract rank positions (invert so lower rank = higher on chart)
    var values = history.map(function (h) {
      return h.rank_position !== null ? h.rank_position : 100;
    });

    // Find min/max for scaling
    var minVal = Math.min.apply(null, values);
    var maxVal = Math.max.apply(null, values);
    var range = maxVal - minVal || 1;

    // Calculate points
    var stepX = width / (values.length - 1);
    var points = values.map(function (val, i) {
      // Invert: lower rank = higher Y position
      var y = height - ((val - minVal) / range) * (height - 4) - 2;
      return { x: i * stepX, y: y };
    });

    // Clear canvas
    ctx.clearRect(0, 0, width, height);

    // Determine line color based on trend
    var firstRank = values[0];
    var lastRank = values[values.length - 1];
    var trendUp = lastRank < firstRank; // Lower rank is better

    ctx.strokeStyle = trendUp ? '#16a34a' : (lastRank > firstRank ? '#dc2626' : '#9ca3af');
    ctx.lineWidth = 1.5;
    ctx.lineJoin = 'round';
    ctx.lineCap = 'round';

    // Draw line
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    for (var i = 1; i < points.length; i++) {
      ctx.lineTo(points[i].x, points[i].y);
    }
    ctx.stroke();

    // Draw end dot
    ctx.fillStyle = ctx.strokeStyle;
    ctx.beginPath();
    ctx.arc(points[points.length - 1].x, points[points.length - 1].y, 2, 0, Math.PI * 2);
    ctx.fill();
  }

  /**
   * Handle manual rank check button click.
   */
  /**
   * Handle tracking toggle checkbox change (in rank tracker list).
   */
  function handleTrackingToggle(e) {
    var $checkbox = $(this);
    var keywordId = $checkbox.data('keyword-id');
    var enabled = $checkbox.is(':checked');

    $checkbox.prop('disabled', true);

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_toggle_keyword_tracking',
        nonce: saiapAdmin.trialNonce,
        keyword_id: keywordId,
        enabled: enabled ? 'true' : 'false'
      },
      success: function (response) {
        $checkbox.prop('disabled', false);

        if (response.success) {
          // Reload the list to update counts and potentially remove the row
          loadTrackedKeywords();
        } else {
          // Revert checkbox
          $checkbox.prop('checked', !enabled);
          showNotice(response.data?.message || 'Failed to update tracking', 'error');
        }
      },
      error: function () {
        $checkbox.prop('disabled', false);
        $checkbox.prop('checked', !enabled);
        showNotice('Network error updating tracking', 'error');
      }
    });
  }

  // ===========================================================================
  // Add Keyword Modal Functions
  // ===========================================================================

  /**
   * Open the Add Keyword modal.
   */
  function openAddKeywordModal() {
    var $modal = $('.saiap-add-keyword-modal');
    $modal.show();

    // Load untracked keywords for the dropdown
    loadUntrackedKeywords();

    // Reset to first tab
    $('.saiap-add-keyword-tab').removeClass('active').first().addClass('active');
    $('.saiap-add-keyword-panel').hide().first().show();
    $('#saiap-new-keyword-input').val('');
  }

  /**
   * Close the Add Keyword modal.
   */
  function closeAddKeywordModal() {
    $('.saiap-add-keyword-modal').hide();
  }

  /**
   * Handle tab switching in Add Keyword modal.
   */
  function handleAddKeywordTabSwitch(e) {
    var $tab = $(e.currentTarget);
    var tabName = $tab.data('tab');

    $('.saiap-add-keyword-tab').removeClass('active');
    $tab.addClass('active');

    $('.saiap-add-keyword-panel').hide();
    $('.saiap-add-keyword-panel[data-panel="' + tabName + '"]').show();
  }

  /**
   * Load untracked keywords for the dropdown.
   */
  function loadUntrackedKeywords() {
    var $select = $('#saiap-existing-keyword-select');
    $select.html('<option value="">Loading...</option>');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_get_untracked_keywords',
        nonce: saiapAdmin.trialNonce
      },
      success: function (response) {
        if (response.success && response.data && response.data.keywords) {
          var keywords = response.data.keywords;

          if (keywords.length === 0) {
            $select.html('<option value="">No untracked keywords available</option>');
          } else {
            var options = '<option value="">Select a keyword...</option>';
            keywords.forEach(function (kw) {
              var volumeStr = kw.search_volume ? ' (' + formatNumber(kw.search_volume) + ' vol)' : '';
              options += '<option value="' + kw.id + '">' + escapeHtml(kw.keyword) + volumeStr + '</option>';
            });
            $select.html(options);
          }
        } else {
          $select.html('<option value="">Failed to load keywords</option>');
        }
      },
      error: function () {
        $select.html('<option value="">Error loading keywords</option>');
      }
    });
  }

  /**
   * Handle Add Keyword form submission.
   */
  function handleAddKeywordSubmit() {
    var $btn = $('.saiap-add-keyword-submit');
    var activeTab = $('.saiap-add-keyword-tab.active').data('tab');
    var isNew = activeTab === 'new';
    var keywordId = null;
    var keywordText = null;

    if (isNew) {
      keywordText = $('#saiap-new-keyword-input').val().trim();
      if (!keywordText) {
        showNotice('Please enter a keyword', 'error');
        return;
      }
    } else {
      keywordId = $('#saiap-existing-keyword-select').val();
      if (!keywordId) {
        showNotice('Please select a keyword', 'error');
        return;
      }
    }

    $btn.prop('disabled', true).text('Adding...');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_add_tracked_keyword',
        nonce: saiapAdmin.trialNonce,
        is_new: isNew ? 'true' : 'false',
        keyword_id: keywordId,
        keyword_text: keywordText
      },
      success: function (response) {
        $btn.prop('disabled', false).text('Add & Track');

        if (response.success) {
          closeAddKeywordModal();
          loadTrackedKeywords();
          showNotice('Keyword added to tracking', 'success');
        } else {
          showNotice(response.data?.message || 'Failed to add keyword', 'error');
        }
      },
      error: function () {
        $btn.prop('disabled', false).text('Add & Track');
        showNotice('Network error adding keyword', 'error');
      }
    });
  }

  // ===========================================================================
  // Review Prompt — Two-Step Sentiment Gate
  // ===========================================================================

  function showReviewStep(stepId) {
    $('.saiap-review-step').hide();
    $(stepId).show();
  }

  function closeReviewModal() {
    $('#saiap-review-modal').css('display', 'none');
  }

  function reviewAjax(action, extraData) {
    var data = $.extend({
      action: action,
      nonce: saiapAdmin.reviewPromptNonce
    }, extraData || {});

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: data
    });
  }

  function handleReviewPositive(e) {
    e.preventDefault();
    reviewAjax('saiap_review_prompt_sentiment', { sentiment: 'positive' });
    showReviewStep('#saiap-review-step-2a');
  }

  function handleReviewNegative(e) {
    e.preventDefault();
    reviewAjax('saiap_review_prompt_sentiment', { sentiment: 'negative' });
    showReviewStep('#saiap-review-step-2b');
  }

  function handleReviewClick() {
    // Link opens in new tab via target="_blank". Track the click and close modal.
    reviewAjax('saiap_review_prompt_clicked');
    closeReviewModal();
    // Hide the persistent nudge too.
    $('.saiap-review-nudge').fadeOut();
  }

  function handleReviewDismissPermanent(e) {
    e.preventDefault();
    reviewAjax('saiap_review_prompt_dismiss', { dismiss_type: 'permanent' });
    closeReviewModal();
    $('.saiap-review-nudge').fadeOut();
  }

  function handleReviewDismissTemp(e) {
    e.preventDefault();
    reviewAjax('saiap_review_prompt_dismiss', { dismiss_type: 'temp' });
    closeReviewModal();
  }

  function handleReviewSendFeedback(e) {
    e.preventDefault();
    var feedback = $('#saiap-review-feedback-text').val().trim();
    if (!feedback) {
      $('#saiap-review-feedback-text').focus();
      return;
    }

    var $btn = $(e.target);
    $btn.prop('disabled', true).text('Sending...');

    $.ajax({
      url: saiapAdmin.ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_review_prompt_feedback',
        nonce: saiapAdmin.reviewPromptNonce,
        feedback: feedback
      },
      success: function () {
        showReviewStep('#saiap-review-step-thanks');
        $('.saiap-review-nudge').fadeOut();
      },
      error: function () {
        $btn.prop('disabled', false).text('Send Feedback');
      }
    });
  }

  function initReviewPrompt() {
    if (saiapAdmin.showReviewModal) {
      $('#saiap-review-modal').css('display', 'flex');
    }
  }

  // ===========================================================================
  // GSC Onboarding — Interstitial + Auto-Review
  // ===========================================================================

  /**
   * Trigger the deferred setup via AJAX.
   *
   * Called when user clicks Skip on the GSC interstitial, or when returning
   * from GSC OAuth as a new user (setup hasn't run yet).
   *
   * Idempotent: multiple code paths in initGscOnboarding can call this on the
   * same page load (Skip click, GSC-already-connected fetch, auto-trigger flag).
   * The module-local guard ensures only the first invocation fires the AJAX
   * call; the others run their callbacks directly so post-trigger UI still
   * advances. Without this, two AJAX calls race past the PHP/backend dedup
   * guards and produce duplicate setup runs (FK violations, 60-item calendars).
   */
  var saiapSetupTriggerFired = false;
  function triggerSetupAjax(callback) {
    if (saiapSetupTriggerFired) {
      if (typeof callback === 'function') callback();
      return;
    }
    saiapSetupTriggerFired = true;
    $.ajax({
      url: ajaxurl,
      type: 'POST',
      data: {
        action: 'saiap_trigger_setup',
        nonce: window.saiapTriggerSetupNonce || ''
      },
      success: function (response) {
        if (response.success) {
          if (typeof callback === 'function') callback();
        }
      }
    });
  }

  /**
   * Initialize GSC onboarding flows.
   *
   * Handles three scenarios:
   * 1. GSC interstitial (new user, setup pending) — Skip button triggers setup
   * 2. Auto-trigger setup (new user returning from GSC OAuth)
   * 3. Auto-review (existing user just connected GSC)
   */
  function initGscOnboarding() {
    // Clean up query params from URL to prevent re-trigger on refresh.
    var url = new URL(window.location.href);
    var paramsToRemove = ['gsc_connected', 'gsc_error', 'gsc_prompt'];
    var needsCleanup = false;
    paramsToRemove.forEach(function (p) {
      if (url.searchParams.has(p)) {
        url.searchParams.delete(p);
        needsCleanup = true;
      }
    });
    if (needsCleanup) {
      window.history.replaceState({}, '', url.toString());
    }

    // 1. GSC interstitial — handle Skip button.
    var $interstitial = $('#saiap-gsc-interstitial');
    var $skipBtn = $('#saiap-gsc-interstitial-skip');
    if ($skipBtn.length) {
      $skipBtn.on('click', function () {
        var $btn = $(this);
        $btn.prop('disabled', true).text('Starting setup...');

        triggerSetupAjax(function () {
          // Hide interstitial, show setup loader.
          $('#saiap-gsc-interstitial').slideUp(300, function () {
            $(this).remove();
          });

          // Re-initialize setup loader if calendar area exists.
          var $calendar = $('.saiap-seo-calendar');
          if ($calendar.length) {
            $calendar.data('setup-running', true);
            initSetupLoader();
          } else {
            // Reload page to show setup loader.
            window.location.reload();
          }
        });
      });

      // Countdown timer — shows remaining time before safety net auto-triggers.
      var remainingSeconds = parseInt($interstitial.data('remaining-seconds'), 10) || 0;
      if (remainingSeconds > 0) {
        var $countdown = $('#saiap-gsc-countdown');
        var $countdownTime = $('#saiap-gsc-countdown-time');

        function formatTime(secs) {
          var m = Math.floor(secs / 60);
          var s = secs % 60;
          return m + ':' + (s < 10 ? '0' : '') + s;
        }

        $countdownTime.text(formatTime(remainingSeconds));
        $countdown.show();

        var countdownInterval = setInterval(function () {
          remainingSeconds--;
          if (remainingSeconds <= 0) {
            clearInterval(countdownInterval);
            // Time's up — trigger setup (mirrors the PHP safety net).
            $skipBtn.prop('disabled', true).text('Starting setup...');
            triggerSetupAjax(function () {
              $interstitial.slideUp(300, function () { $(this).remove(); });
              var $calendar = $('.saiap-seo-calendar');
              if ($calendar.length) {
                $calendar.data('setup-running', true);
                initSetupLoader();
              } else {
                window.location.reload();
              }
            });
            return;
          }
          $countdownTime.text(formatTime(remainingSeconds));
        }, 1000);
      }

      // Check if GSC is already connected — if so, skip interstitial and trigger setup.
      var statusUrl = $interstitial.data('gsc-status-url');
      if (statusUrl) {
        fetch(statusUrl)
          .then(function (r) { return r.json(); })
          .then(function (data) {
            if (data.connected) {
              // GSC already connected — skip interstitial, trigger setup directly.
              $interstitial.slideUp(300, function () { $(this).remove(); });
              triggerSetupAjax(function () {
                window.location.reload();
              });
            }
          })
          .catch(function () { /* Ignore — show interstitial as normal */ });
      }
    }

    // 2. Auto-trigger setup (new user returning from GSC OAuth).
    if (window.saiapAutoTriggerSetup) {
      triggerSetupAjax(function () {
        // Hide interstitial if visible, reload to show setup loader.
        var $interstitial = $('#saiap-gsc-interstitial');
        if ($interstitial.length) {
          $interstitial.slideUp(300);
        }
        window.location.reload();
      });
    }

    // 3. Auto-review (existing user just connected GSC).
    if (window.saiapAutoGscReview) {
      // Wait for Clarence chat to mount, then send the review message.
      var reviewMessage = 'I just connected Google Search Console. Analyze my search performance data and give me a strategic review \u2014 what\u2019s working, what opportunities am I missing, and how should we adjust the content strategy?';

      var attempts = 0;
      var waitForChat = setInterval(function () {
        attempts++;
        if (typeof window.saiapSeoAgentSendMessage === 'function') {
          clearInterval(waitForChat);
          window.saiapSeoAgentSendMessage(reviewMessage);
        } else if (attempts > 20) {
          // Give up after ~10 seconds.
          clearInterval(waitForChat);
        }
      }, 500);
    }
  }

  // Initialize when DOM is ready
  $(document).ready(function () {
    initDashboard();
    initRankTracker();
    initReviewPrompt();
    initGscOnboarding();
  });
})(jQuery);
