<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ -- NUCLEAR+++
// File: /home/ploi/automarketer-ai.com/public/wp-content/plugins/automarketer-ai/includes/class-blog-scheduler.php
// Description: Blog post scheduler -- Mon/Wed/Fri nudge system, auto-draft, autopilot publish, email and admin notice - NUCLEAR+++
// Last Edited: 2026-04-27
// ==================================================

namespace AutoMarketerAI;

defined('ABSPATH') || exit;

class BlogScheduler
{
    // Cron hook names
    public const HOOK_MORNING   = 'amai_blog_morning_check';
    public const HOOK_AFTERNOON = 'amai_blog_afternoon_check';
    public const HOOK_TOKEN     = 'amai_blog_title_pick';

    // Slot days (N = day of week, 1=Mon 3=Wed 5=Fri)
    private const SLOT_DAYS = [1, 3, 5];

    // Nudge hour (9am) and auto-draft hour (2pm) in site local time
    private const NUDGE_HOUR  = 9;
    private const DRAFT_HOUR  = 14;

    // How many recent published posts to inspect for category diversity
    private const DIVERSITY_LOOKBACK = 5;

    // --------------------------------------------------
    // Register hooks
    // --------------------------------------------------
    public static function register(): void
    {
        add_action(self::HOOK_MORNING,   [self::class, 'morningCheck']);
        add_action(self::HOOK_AFTERNOON, [self::class, 'afternoonCheck']);
        add_action('admin_notices',      [self::class, 'renderAdminNotice']);
        add_action('wp_ajax_amai_blog_nudge_pick',        [self::class, 'handleNudgePick']);
        add_action('wp_ajax_nopriv_amai_blog_token_pick', [self::class, 'handleTokenPick']);
        add_action('wp_ajax_amai_blog_nudge_dismiss',     [self::class, 'handleNudgeDismiss']);
        add_action('admin_enqueue_scripts',               [self::class, 'enqueueAssets']);
    }

    // --------------------------------------------------
    // Activation -- schedule both daily crons
    // --------------------------------------------------
    public static function scheduleCrons(): void
    {
        if (!wp_next_scheduled(self::HOOK_MORNING)) {
            $next = self::nextCronTimestamp(self::NUDGE_HOUR, 0);
            wp_schedule_event($next, 'daily', self::HOOK_MORNING);
        }
        if (!wp_next_scheduled(self::HOOK_AFTERNOON)) {
            $next = self::nextCronTimestamp(self::DRAFT_HOUR, 0);
            wp_schedule_event($next, 'daily', self::HOOK_AFTERNOON);
        }
    }

    // --------------------------------------------------
    // Deactivation -- clear crons
    // --------------------------------------------------
    public static function clearCrons(): void
    {
        $ts = wp_next_scheduled(self::HOOK_MORNING);
        if ($ts !== false) {
            wp_unschedule_event($ts, self::HOOK_MORNING);
        }
        $ts = wp_next_scheduled(self::HOOK_AFTERNOON);
        if ($ts !== false) {
            wp_unschedule_event($ts, self::HOOK_AFTERNOON);
        }
    }

    // --------------------------------------------------
    // Morning cron (9am) -- ensure slot exists, fire nudge or autopilot
    // --------------------------------------------------
    public static function morningCheck(): void
    {
        $today = self::localDate();
        if (!self::isSlotDay($today)) {
            return;
        }

        $slot = self::getOrCreateSlot($today);
        if ($slot === null) {
            return;
        }

        if ($slot['status'] !== 'pending') {
            return;
        }

        // Pre-generate titles and cache on slot
        $titles = self::generateTitles();
        if (!empty($titles)) {
            self::updateSlot((int) $slot['id'], [
                'titles_json' => json_encode($titles),
                'nudge_token' => bin2hex(random_bytes(32)),
            ]);
            $slot = self::getSlot($today);
        }

        $approval_mode = (get_option('amai_blog_approval_mode', 'review') === 'autopilot') ? 'autopilot' : 'review';

        if ($approval_mode === 'autopilot') {
            self::runAutopilot($slot);
        } else {
            self::fireNudge($slot);
        }
    }

    // --------------------------------------------------
    // Afternoon cron (2pm) -- auto-draft if no action taken
    // --------------------------------------------------
    public static function afternoonCheck(): void
    {
        $today = self::localDate();
        if (!self::isSlotDay($today)) {
            return;
        }

        $slot = self::getSlot($today);
        if ($slot === null) {
            return;
        }

        if ($slot['status'] !== 'nudged') {
            return;
        }

        // User did not act -- auto-generate draft
        self::runAutoDraft($slot);
    }

    // --------------------------------------------------
    // Autopilot: generate content, publish, send brag notice + email
    // --------------------------------------------------
    private static function runAutopilot(array $slot): void
    {
        $titles = self::decodeTitles($slot);
        if (empty($titles)) {
            return;
        }

        $title   = self::pickDiverseTitle($titles);
        $post_id = self::generateAndSavePost($title, true);
        if ($post_id <= 0) {
            return;
        }

        self::updateSlot((int) $slot['id'], [
            'status'            => 'published',
            'published_post_id' => $post_id,
        ]);

        // Store brag notice for next admin pageload
        $notices   = (array) get_option('amai_blog_brag_notices', []);
        $notices[] = [
            'type'    => 'published',
            'title'   => $title,
            'post_id' => $post_id,
            'url'     => get_permalink($post_id),
        ];
        update_option('amai_blog_brag_notices', $notices);

        // Send brag email
        self::sendBragEmail($title, (int) $post_id);
    }

    // --------------------------------------------------
    // Fire nudge: mark slot nudged, store notice, send email
    // --------------------------------------------------
    private static function fireNudge(array $slot): void
    {
        self::updateSlot((int) $slot['id'], [
            'status'        => 'nudged',
            'nudge_sent_at' => gmdate('Y-m-d H:i:s'),
        ]);

        // Store nudge notice for next admin pageload
        $notices   = (array) get_option('amai_blog_nudge_notices', []);
        $notices[] = [
            'slot_id' => (int) $slot['id'],
            'titles'  => self::decodeTitles($slot),
            'token'   => (string) ($slot['nudge_token'] ?? ''),
        ];
        update_option('amai_blog_nudge_notices', $notices);

        // Send nudge email
        self::sendNudgeEmail($slot);
    }

    // --------------------------------------------------
    // Auto-draft: generate content, save as draft, send notice + email
    // --------------------------------------------------
    private static function runAutoDraft(array $slot): void
    {
        $titles = self::decodeTitles($slot);
        if (empty($titles)) {
            return;
        }

        $title   = self::pickDiverseTitle($titles);
        $post_id = self::generateAndSavePost($title, false);
        if ($post_id <= 0) {
            return;
        }

        self::updateSlot((int) $slot['id'], [
            'status'        => 'drafted',
            'draft_post_id' => $post_id,
            'auto_draft_at' => gmdate('Y-m-d H:i:s'),
        ]);

        // Store draft-ready notice
        $notices   = (array) get_option('amai_blog_draft_notices', []);
        $notices[] = [
            'title'    => $title,
            'post_id'  => $post_id,
            'edit_url' => get_edit_post_link($post_id, 'raw'),
        ];
        update_option('amai_blog_draft_notices', $notices);

        // Send draft-ready email
        self::sendDraftReadyEmail($title, (int) $post_id);
    }

    // --------------------------------------------------
    // Pick the title whose category has not appeared recently.
    // Falls back to first title if all categories are equally represented.
    // Titles may be strings (legacy) or {title, category} objects.
    // --------------------------------------------------
    private static function pickDiverseTitle(array $titles): string
    {
        // Resolve recent post categories from WP
        $recent_cats = self::recentPublishedCategories(self::DIVERSITY_LOOKBACK);

        // Score each candidate: lower score = less recently used category
        $best_score = PHP_INT_MAX;
        $best_title = '';

        foreach ($titles as $item) {
            if (is_array($item)) {
                $title    = (string) ($item['title'] ?? '');
                $category = (string) ($item['category'] ?? '');
            } else {
                $title    = (string) $item;
                $category = '';
            }

            if ($title === '') {
                continue;
            }

            // Count how many of the recent posts share this category
            $score = $category !== '' ? (int) ($recent_cats[$category] ?? 0) : 0;

            if ($best_title === '' || $score < $best_score) {
                $best_score = $score;
                $best_title = $title;
            }
        }

        return $best_title !== '' ? $best_title : (is_array($titles[0]) ? (string) ($titles[0]['title'] ?? '') : (string) $titles[0]);
    }

    // --------------------------------------------------
    // Return a category => count map for the last N published posts
    // using WP post categories (not amai custom categories).
    // Falls back gracefully if no posts exist.
    // --------------------------------------------------
    private static function recentPublishedCategories(int $limit): array
    {
        $posts = get_posts([
            'post_type'      => 'post',
            'post_status'    => 'publish',
            'posts_per_page' => $limit,
            'orderby'        => 'date',
            'order'          => 'DESC',
            'fields'         => 'ids',
        ]);

        $counts = [];
        foreach ($posts as $post_id) {
            $cats = get_the_category((int) $post_id);
            foreach ($cats as $cat) {
                $name = (string) $cat->name;
                $counts[$name] = ($counts[$name] ?? 0) + 1;
            }
        }

        return $counts;
    }

    // --------------------------------------------------
    // Generate post content and save via BlogGenerator
    // --------------------------------------------------
    private static function generateAndSavePost(string $title, bool $publish): int
    {
        $profile           = (new BusinessProfile())->get();
        $business_name     = get_bloginfo('name');
        $business_category = '';
        $context_parts     = [];

        if (is_array($profile)) {
            $cat_map           = BusinessProfile::BUSINESS_CATEGORIES;
            $cat_key           = (string) ($profile['business_category'] ?? '');
            $business_category = $cat_map[$cat_key] ?? $cat_key;
            if (!empty($profile['differentiator'])) {
                $context_parts[] = 'What makes us different: ' . $profile['differentiator'];
            }
            if (!empty($profile['values_and_causes'])) {
                $context_parts[] = 'Our values: ' . $profile['values_and_causes'];
            }
            if (!empty($profile['customer_descriptor'])) {
                $context_parts[] = 'Our customers: ' . $profile['customer_descriptor'];
            }
            if (($profile['website_status'] ?? '') === 'has_website' && !empty($profile['website_url'])) {
                $url      = esc_url_raw($profile['website_url']);
                $response = wp_remote_get($url, ['timeout' => 8, 'sslverify' => false]);
                if (!is_wp_error($response)) {
                    $body = wp_remote_retrieve_body($response);
                    $text = wp_strip_all_tags($body);
                    $text = preg_replace('/\s+/', ' ', $text) ?? '';
                    $text = trim(mb_substr($text, 0, 800));
                    if ($text !== '') {
                        $context_parts[] = 'Website content: ' . $text;
                    }
                }
            }
        }

        $context = implode("\n", $context_parts);
        $content = BlogGenerator::callCoreGenerateContentStatic($title, $business_name, $business_category, $context);
        if ($content === null) {
            return 0;
        }

        $primary_user_id = (int) get_option('amai_primary_user_id', 1);
        $post_id = wp_insert_post([
            'post_title'   => $title,
            'post_content' => $content,
            'post_status'  => $publish ? 'publish' : 'draft',
            'post_type'    => 'post',
            'post_author'  => $primary_user_id,
        ], true);

        if (is_wp_error($post_id)) {
            return 0;
        }

        // Fetch featured image -- source determined by amai_blog_image_source setting.
        // AI mode: try ImageProvider first, fall back to Unsplash on failure.
        // Unsplash mode: go straight to Unsplash.
        $image_source = (string) get_option('amai_blog_image_source', 'unsplash');
        $image_set    = false;

        if ($image_source === 'ai' && class_exists('\AutoMarketerAI\ImageProvider')) {
            $prompt    = 'A high-quality professional photograph for a blog post titled "' . $title . '"';
            $local_url = \AutoMarketerAI\ImageProvider::resolveForPost($prompt);
            if ($local_url !== null) {
                $attachment_id = (int) attachment_url_to_postid($local_url);
                if ($attachment_id > 0) {
                    set_post_thumbnail($post_id, $attachment_id);
                    $image_set = true;
                }
            }
        }

        if (!$image_set && class_exists('\AutoMarketerAI\Unsplash') && defined('AMAI_UNSPLASH_ACCESS_KEY') && AMAI_UNSPLASH_ACCESS_KEY !== '') {
            $keywords = Unsplash::extractKeywordsAi($title . ' ' . $business_category);
            if ($keywords !== '') {
                $photo = Unsplash::search($keywords);
                if ($photo !== null && !empty($photo['url']) && !empty($photo['photographer'])) {
                    $attachment_id = Unsplash::downloadToMedia($photo['url'], $photo['photographer'], $post_id, (string) ($photo['profile_url'] ?? ''));
                    if ($attachment_id > 0) {
                        set_post_thumbnail($post_id, $attachment_id);
                    }
                }
            }
        }

        // Schedule background content optimizer to run 90 seconds after post creation.
        wp_schedule_single_event(time() + 90, 'amai_bg_optimize_post', [$post_id]);

        return (int) $post_id;
    }

    // --------------------------------------------------
    // Generate 5 titles via BlogGenerator
    // --------------------------------------------------
    private static function generateTitles(): array
    {

        // Check gap topic queue first -- if topics are waiting, use one instead of calling Core
        if (class_exists('\\AutoMarketerAI\\GapTopicQueue') && GapTopicQueue::count() > 0) {
            $queued_topic = GapTopicQueue::pop();
            if ($queued_topic !== null && $queued_topic !== '') {
                return [[ 'title' => $queued_topic, 'category' => '' ]];
            }
        }
        $profile           = (new BusinessProfile())->get();
        $business_name     = get_bloginfo('name');
        $business_category = '';
        $context_parts     = [];

        if (is_array($profile)) {
            $cat_map           = BusinessProfile::BUSINESS_CATEGORIES;
            $cat_key           = (string) ($profile['business_category'] ?? '');
            $business_category = $cat_map[$cat_key] ?? $cat_key;
            if (!empty($profile['differentiator'])) {
                $context_parts[] = 'What makes us different: ' . $profile['differentiator'];
            }
            if (!empty($profile['customer_descriptor'])) {
                $context_parts[] = 'Our customers: ' . $profile['customer_descriptor'];
            }
        }

        global $wpdb;
        $table = $wpdb->prefix . 'amai_scheduled_posts';
        $posts = $wpdb->get_col(
            "SELECT post_content FROM `{$table}` WHERE status = 'approved' ORDER BY created_at DESC LIMIT 5"
        );
        if (!empty($posts)) {
            $context_parts[] = 'Recent social posts: ' . implode(' | ', array_map('wp_strip_all_tags', $posts));
        }

        $context = implode("\n", $context_parts);
        $titles  = BlogGenerator::callCoreGenerateTitlesStatic($business_name, $business_category, $context);
        return is_array($titles) ? $titles : [];
    }

    // --------------------------------------------------
    // Admin notice rendering
    // --------------------------------------------------
    public static function renderAdminNotice(): void
    {
        if (!current_user_can('edit_posts')) {
            return;
        }

        // Brag notices (autopilot published)
        $brags = (array) get_option('amai_blog_brag_notices', []);
        if (!empty($brags)) {
            delete_option('amai_blog_brag_notices');
            foreach ($brags as $brag) {
                $title = esc_html((string) ($brag['title'] ?? ''));
                $url   = esc_url((string) ($brag['url'] ?? ''));
                echo '<div class="notice notice-success is-dismissible amai-blog-notice">'
                    . '<p><strong>AutoMarketer AI just published a new blog post for you.</strong> '
                    . '"' . $title . '" is live. '
                    . '<a href="' . $url . '" target="_blank">View post</a></p>'
                    . '</div>';
            }
        }

        // Draft-ready notices (auto-draft after no action)
        $drafts = (array) get_option('amai_blog_draft_notices', []);
        if (!empty($drafts)) {
            delete_option('amai_blog_draft_notices');
            foreach ($drafts as $draft) {
                $title    = esc_html((string) ($draft['title'] ?? ''));
                $edit_url = esc_url((string) ($draft['edit_url'] ?? ''));
                echo '<div class="notice notice-info is-dismissible amai-blog-notice">'
                    . '<p><strong>We wrote a blog post for you.</strong> '
                    . '"' . $title . '" is ready for your review. '
                    . '<a href="' . $edit_url . '">Review and approve</a></p>'
                    . '</div>';
            }
        }

        // Nudge notices (title picker)
        $nudges = (array) get_option('amai_blog_nudge_notices', []);
        if (empty($nudges)) {
            return;
        }

        delete_option('amai_blog_nudge_notices');
        $nudge   = $nudges[0];
        $titles  = (array) ($nudge['titles'] ?? []);
        $slot_id = (int) ($nudge['slot_id'] ?? 0);
        if (empty($titles) || $slot_id <= 0) {
            return;
        }

        $nonce = wp_create_nonce('amai_blog_nudge');
        echo '<div class="amai-blog-nudge-notice" id="amai-blog-nudge-notice" data-slot-id="' . $slot_id . '" data-nonce="' . esc_attr($nonce) . '">';
        echo '<div class="amai-blog-nudge-header">';
        echo '<div class="amai-blog-nudge-eyebrow">AutoMarketer AI</div>';
        echo '<div class="amai-blog-nudge-title">Time to write a new blog post</div>';
        echo '<div class="amai-blog-nudge-sub">Pick a title and we\'ll write the full post, add an image, and optimize it automatically.</div>';
        echo '</div>';
        echo '<div class="amai-blog-nudge-titles">';
        foreach ($titles as $i => $item) {
            $display = is_array($item) ? (string) ($item['title'] ?? '') : (string) $item;
            if ($display === '') {
                continue;
            }
            echo '<button type="button" class="amai-blog-nudge-title-btn" data-title="' . esc_attr($display) . '" data-slot-id="' . $slot_id . '">'
                . esc_html($display)
                . '</button>';
        }
        echo '</div>';
        echo '<div class="amai-blog-nudge-footer">';
        echo '<button type="button" class="amai-blog-nudge-dismiss" id="amai-blog-nudge-dismiss">Not today</button>';
        echo '</div>';
        echo '</div>';
    }

    // --------------------------------------------------
    // Enqueue nudge notice assets
    // --------------------------------------------------
    public static function enqueueAssets(string $hook): void
    {
        $nudges = (array) get_option('amai_blog_nudge_notices', []);
        if (empty($nudges)) {
            return;
        }
        wp_enqueue_style(
            'amai-blog-nudge',
            AMAI_PLUGIN_URL . 'assets/css/blog-nudge.css',
            [],
            (string) filemtime(AMAI_PLUGIN_DIR . 'assets/css/blog-nudge.css')
        );
        wp_enqueue_script(
            'amai-blog-nudge',
            AMAI_PLUGIN_URL . 'assets/js/amai-blog-nudge.js',
            [],
            (string) filemtime(AMAI_PLUGIN_DIR . 'assets/js/amai-blog-nudge.js'),
            true
        );
        wp_localize_script('amai-blog-nudge', 'amaiBlogNudge', [
            'ajaxurl' => admin_url('admin-ajax.php'),
            'nonce'   => wp_create_nonce('amai_blog_nudge'),
        ]);
    }

    // --------------------------------------------------
    // AJAX -- user picked a title from nudge notice
    // --------------------------------------------------
    public static function handleNudgePick(): void
    {
        check_ajax_referer('amai_blog_nudge', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $title   = sanitize_text_field(wp_unslash((string) ($_POST['title'] ?? '')));
        $slot_id = (int) ($_POST['slot_id'] ?? 0);

        if ($title === '' || $slot_id <= 0) {
            wp_send_json_error(['message' => 'Invalid request.'], 400);
        }

        self::updateSlotById($slot_id, ['status' => 'acted']);

        $post_id = wp_insert_post([
            'post_title'  => $title,
            'post_status' => 'draft',
            'post_type'   => 'post',
            'post_author' => get_current_user_id(),
        ], true);

        if (is_wp_error($post_id)) {
            wp_send_json_error(['message' => 'Failed to create post.'], 500);
        }

        $edit_url = add_query_arg('amai_gen', '1', get_edit_post_link($post_id, 'raw'));
        wp_send_json_success(['edit_url' => $edit_url]);
    }

    // --------------------------------------------------
    // AJAX (no-priv) -- email title-click token handler
    // --------------------------------------------------
    public static function handleTokenPick(): void
    {
        $token   = sanitize_text_field(wp_unslash((string) ($_GET['token'] ?? '')));
        $title   = sanitize_text_field(wp_unslash((string) ($_GET['title'] ?? '')));
        $slot_id = (int) ($_GET['slot_id'] ?? 0);

        if ($token === '' || $title === '' || $slot_id <= 0) {
            wp_die('Invalid link.', 'Invalid', ['response' => 400]);
        }

        // Validate token against slot
        global $wpdb;
        $table = $wpdb->prefix . 'amai_blog_schedule';
        $row   = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE id = %d", $slot_id),
            ARRAY_A
        );

        if (!is_array($row) || $row['nudge_token'] !== $token) {
            wp_die('Invalid or expired link.', 'Invalid', ['response' => 403]);
        }

        if (!in_array($row['status'], ['nudged', 'pending'], true)) {
            wp_die('This slot has already been handled.', 'Already done', ['response' => 410]);
        }

        self::updateSlotById($slot_id, ['status' => 'acted']);

        $primary_user_id = (int) get_option('amai_primary_user_id', 1);
        $post_id = wp_insert_post([
            'post_title'  => $title,
            'post_status' => 'draft',
            'post_type'   => 'post',
            'post_author' => $primary_user_id,
        ], true);

        if (is_wp_error($post_id)) {
            wp_die('Failed to create post.', 'Error', ['response' => 500]);
        }

        $edit_url = add_query_arg('amai_gen', '1', get_edit_post_link($post_id, 'raw'));
        wp_safe_redirect($edit_url);
        exit;
    }

    // --------------------------------------------------
    // AJAX -- dismiss nudge notice
    // --------------------------------------------------
    public static function handleNudgeDismiss(): void
    {
        check_ajax_referer('amai_blog_nudge', 'nonce');
        $slot_id = (int) ($_POST['slot_id'] ?? 0);
        if ($slot_id > 0) {
            self::updateSlotById($slot_id, ['status' => 'skipped']);
        }
        wp_send_json_success();
    }

    // --------------------------------------------------
    // Email -- nudge with 5 title links
    // --------------------------------------------------
    private static function sendNudgeEmail(array $slot): void
    {
        $user_id = (int) get_option('amai_primary_user_id', 1);
        $user    = get_userdata($user_id);
        if (!$user instanceof \WP_User) {
            return;
        }

        $titles    = self::decodeTitles($slot);
        $token     = (string) ($slot['nudge_token'] ?? '');
        $slot_id   = (int) $slot['id'];
        $site_name = get_bloginfo('name');
        $first     = esc_html($user->first_name ?: $user->display_name);

        $title_items = [];
        foreach ($titles as $item) {
            $display = is_array($item) ? (string) ($item['title'] ?? '') : (string) $item;
            if ($display === '') {
                continue;
            }
            $title_items[] = [
                'title' => $display,
                'url'   => add_query_arg([
                    'action'  => 'amai_blog_token_pick',
                    'token'   => $token,
                    'slot_id' => $slot_id,
                    'title'   => urlencode($display),
                ], admin_url('admin-ajax.php')),
            ];
        }
        $subject = 'Time to write a new blog post for ' . $site_name;
        $message = MarketingSite::buildNudgeEmailHtml($first, $site_name, $title_items);
        wp_mail($user->user_email, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
    }

    // --------------------------------------------------
    // Email -- auto-draft ready for review
    // --------------------------------------------------
    private static function sendDraftReadyEmail(string $title, int $post_id): void
    {
        $user_id = (int) get_option('amai_primary_user_id', 1);
        $user    = get_userdata($user_id);
        if (!$user instanceof \WP_User) {
            return;
        }

        $first    = esc_html($user->first_name ?: $user->display_name);
        $edit_url = (string) get_edit_post_link($post_id, 'raw');
        $subject  = 'We wrote a blog post for you - go take a look';
        $message  = MarketingSite::buildDraftReadyEmailHtml($first, $title, $edit_url);
        wp_mail($user->user_email, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
    }

    // --------------------------------------------------
    // Email -- blog published test (called from settings)
    // --------------------------------------------------
    public static function sendTestBragEmail(string $first, string $title, int $post_id, string $to): void
    {
        $message = self::buildBragEmailHtml($first, $title, $post_id);
        $subject = 'AutoMarketer AI just published a new blog post for you';
        wp_mail($to, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
    }

    // --------------------------------------------------
    // Email -- autopilot brag
    // --------------------------------------------------
    private static function sendBragEmail(string $title, int $post_id): void
    {
        $user_id = (int) get_option('amai_primary_user_id', 1);
        $user    = get_userdata($user_id);
        if (!$user instanceof \WP_User) {
            return;
        }

        $first   = esc_html($user->first_name ?: $user->display_name);
        $subject = 'AutoMarketer AI just published a new blog post for you';
        $message = self::buildBragEmailHtml($first, $title, $post_id);

        wp_mail($user->user_email, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
    }

    // --------------------------------------------------
    // Email -- blog published -- score + issues
    // --------------------------------------------------
    private static function buildBragEmailHtml(string $first, string $title, int $post_id): string
    {
        return MarketingSite::buildBlogPublishedEmailHtml($first, $title, $post_id);
    }

    // --------------------------------------------------
    // Email wrapper -- consistent branded template
    // --------------------------------------------------
    private static function wrapEmail(
        string $first_name,
        string $headline,
        string $body_text,
        string $body_html,
        string $footer_text
    ): string {
        $site_name = get_bloginfo('name');
        return '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body style="margin:0;padding:0;background:#F9F6F3;font-family:DM Sans,Arial,sans-serif;">'
            . '<table width="100%" cellpadding="0" cellspacing="0" style="background:#F9F6F3;padding:40px 0;">'
            . '<tr><td align="center">'
            . '<table width="600" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 4px 24px rgba(0,0,0,0.07);">'
            . '<tr><td style="background:#D97757;padding:28px 40px;">'
            . '<div style="font-family:Nunito,Arial,sans-serif;font-size:22px;font-weight:800;color:#fff;">' . esc_html($site_name) . '</div>'
            . '<div style="font-size:12px;color:rgba(255,255,255,0.8);margin-top:2px;">Powered by AutoMarketer AI</div>'
            . '</td></tr>'
            . '<tr><td style="padding:36px 40px;">'
            . '<p style="margin:0 0 6px;font-size:13px;color:#A8A29E;">Hi ' . $first_name . ',</p>'
            . '<h1 style="margin:0 0 20px;font-family:Nunito,Arial,sans-serif;font-size:24px;font-weight:800;color:#1C1917;">' . esc_html($headline) . '</h1>'
            . '<p style="margin:0 0 24px;font-size:15px;color:#44403C;line-height:1.6;">' . esc_html($body_text) . '</p>'
            . $body_html
            . '</td></tr>'
            . '<tr><td style="padding:20px 40px 36px;border-top:1px solid #F0EAE5;">'
            . '<p style="margin:0;font-size:13px;color:#A8A29E;line-height:1.5;">' . esc_html($footer_text) . '</p>'
            . '</td></tr>'
            . '</table>'
            . '</td></tr></table>'
            . '</body></html>';
    }

    // --------------------------------------------------
    // DB helpers
    // --------------------------------------------------
    private static function getOrCreateSlot(string $date): ?array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_blog_schedule';
        $row   = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE slot_date = %s", $date),
            ARRAY_A
        );
        if (is_array($row)) {
            return $row;
        }
        $wpdb->insert($table, [
            'slot_date'  => $date,
            'slot_time'  => '09:00:00',
            'status'     => 'pending',
            'created_at' => gmdate('Y-m-d H:i:s'),
            'updated_at' => gmdate('Y-m-d H:i:s'),
        ]);
        if ($wpdb->insert_id <= 0) {
            return null;
        }
        return $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE slot_date = %s", $date),
            ARRAY_A
        );
    }

    private static function getSlot(string $date): ?array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_blog_schedule';
        $row   = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE slot_date = %s", $date),
            ARRAY_A
        );
        return is_array($row) ? $row : null;
    }

    private static function updateSlot(int $id, array $data): void
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_blog_schedule';
        $data['updated_at'] = gmdate('Y-m-d H:i:s');
        $wpdb->update($table, $data, ['id' => $id]);
    }

    private static function updateSlotById(int $id, array $data): void
    {
        self::updateSlot($id, $data);
    }

    private static function decodeTitles(array $slot): array
    {
        $json = (string) ($slot['titles_json'] ?? '');
        if ($json === '') {
            return [];
        }
        $decoded = json_decode($json, true);
        return is_array($decoded) ? $decoded : [];
    }

    // --------------------------------------------------
    // Date/time helpers
    // --------------------------------------------------
    private static function localDate(): string
    {
        $tz = wp_timezone();
        $dt = new \DateTime('now', $tz);
        return $dt->format('Y-m-d');
    }

    private static function isSlotDay(string $date): bool
    {
        $dt  = new \DateTime($date);
        $dow = (int) $dt->format('N');
        return in_array($dow, self::SLOT_DAYS, true);
    }

    private static function nextCronTimestamp(int $hour, int $minute): int
    {
        $tz = wp_timezone();
        $dt = new \DateTime('now', $tz);
        $dt->setTime($hour, $minute, 0);
        if ($dt->getTimestamp() <= time()) {
            $dt->modify('+1 day');
        }
        return $dt->getTimestamp();
    }
}