<?php
/**
 * Plugin Name: Damalion Blog AI
 * Description: Automated blog post generation for Damalion. AI-powered satellite articles with SEO, featured images, WPML support, internal linking, inbound link scanning, and Google-compliant content validation. Includes PE/Capital Markets US news publishing module, LuxStrat Luxembourg structuring module, LATAM GeoBot for 15 Latin American countries, SEO Sniper competitive content engine, Anti-Orphan Center for site-wide orphan page detection and WPML-aware link injection, and Comfisc Luxembourg accounting & tax module (FR).
 * Version: 2.19.0
 * Author: Damalion
 * Text Domain: damalion-blog-ai
 */

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

define('DBA_VERSION', '2.19.0');
define('DBA_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('DBA_PLUGIN_URL', plugin_dir_url(__FILE__));
define('DBA_LOG_TABLE', 'dba_generation_logs');

/**
 * ─── SUBJECT DEDUPLICATION GUARD ───────────────────────────────────────────
 * Prevents more than 1 post per subject per day.
 * Avoids Google keyword-stuffing / spam penalties.
 *
 * Works at two levels:
 *   1. dba_subject_already_posted_today() — call before generating a post
 *   2. save_post hook — safety net that catches any duplicates that slip through
 * ────────────────────────────────────────────────────────────────────────────
 */

/**
 * Check whether a subject has already been published today (UTC or site timezone).
 *
 * @param  string $subject  The subject string (e.g. "Luxembourg holding company").
 * @return bool             True if a post with that subject already exists today.
 */
function dba_subject_already_posted_today( $subject ) {
    if ( empty( $subject ) ) {
        return false;
    }

    // Normalise for comparison
    $subject_lower = mb_strtolower( trim( $subject ) );

    // 1. Check in-memory batch tracker (transient — survives the same cron run)
    $batch_key  = 'dba_subjects_today_' . date( 'Y-m-d' );
    $used       = get_transient( $batch_key );
    if ( ! is_array( $used ) ) {
        $used = array();
    }
    if ( in_array( $subject_lower, $used, true ) ) {
        return true;
    }

    // 2. Check actual published/draft posts created today with the same _dba_subject
    $schedule_settings = get_option( 'dba_schedule_settings', array() );
    $tz_string         = ! empty( $schedule_settings['timezone'] ) ? $schedule_settings['timezone'] : 'Europe/Luxembourg';
    try {
        $tz = new DateTimeZone( $tz_string );
    } catch ( \Exception $e ) {
        $tz = new DateTimeZone( 'Europe/Luxembourg' );
    }
    $now        = new DateTime( 'now', $tz );
    $today_start = $now->format( 'Y-m-d' ) . ' 00:00:00';
    $today_end   = $now->format( 'Y-m-d' ) . ' 23:59:59';

    $existing = new WP_Query( array(
        'post_type'      => 'post',
        'post_status'    => array( 'publish', 'draft', 'pending', 'private' ),
        'posts_per_page' => 1,
        'fields'         => 'ids',
        'date_query'     => array(
            array(
                'after'     => $today_start,
                'before'    => $today_end,
                'inclusive' => true,
            ),
        ),
        'meta_query'     => array(
            array(
                'key'     => '_dba_subject',
                'value'   => $subject,          // exact match first
                'compare' => '=',
            ),
            array(
                'key'     => '_dba_auto_created',
                'compare' => 'EXISTS',
            ),
        ),
        'no_found_rows'          => true,
        'update_post_meta_cache' => false,
        'update_post_term_cache' => false,
    ) );

    return $existing->have_posts();
}

/**
 * Record a subject as "used today" in the fast in-memory transient.
 *
 * @param string $subject
 */
function dba_mark_subject_used_today( $subject ) {
    if ( empty( $subject ) ) {
        return;
    }
    $batch_key  = 'dba_subjects_today_' . date( 'Y-m-d' );
    $used       = get_transient( $batch_key );
    if ( ! is_array( $used ) ) {
        $used = array();
    }
    $subject_lower = mb_strtolower( trim( $subject ) );
    if ( ! in_array( $subject_lower, $used, true ) ) {
        $used[] = $subject_lower;
        set_transient( $batch_key, $used, DAY_IN_SECONDS );
    }
}

/**
 * Safety-net: when any DBA post is saved, auto-record its subject
 * so subsequent posts in the same batch won't duplicate it.
 */
function dba_track_subject_on_save( $post_id, $post, $update ) {
    // Only for new DBA auto-created posts
    if ( $update ) {
        return;
    }
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( $post->post_type !== 'post' ) {
        return;
    }

    $is_dba  = get_post_meta( $post_id, '_dba_auto_created', true );
    $subject = get_post_meta( $post_id, '_dba_subject', true );

    if ( $is_dba && ! empty( $subject ) ) {
        // Check if this is a duplicate — if so, trash it
        if ( dba_subject_already_posted_today( $subject ) ) {
            wp_update_post( array(
                'ID'          => $post_id,
                'post_status' => 'trash',
                'post_title'  => '[DUPLICATE SUBJECT] ' . $post->post_title,
            ) );
            error_log( 'DBA DEDUP (save_post): Trashed post #' . $post_id . ' — duplicate subject "' . $subject . '"' );
            return;
        }
        dba_mark_subject_used_today( $subject );
    }
}
add_action( 'wp_insert_post', 'dba_track_subject_on_save', 10, 3 );

/**
 * Catch _dba_subject added via add_post_meta() AFTER wp_insert_post.
 * If subject was already used today, trash the post immediately.
 */
function dba_catch_late_subject_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
    if ( $meta_key !== '_dba_subject' || empty( $meta_value ) ) {
        return;
    }
    // Only for DBA posts
    if ( ! get_post_meta( $post_id, '_dba_auto_created', true ) ) {
        return;
    }

    if ( dba_subject_already_posted_today( $meta_value ) ) {
        $post = get_post( $post_id );
        if ( $post && $post->post_status !== 'trash' ) {
            wp_update_post( array(
                'ID'          => $post_id,
                'post_status' => 'trash',
                'post_title'  => '[DUPLICATE SUBJECT] ' . $post->post_title,
            ) );
            error_log( 'DBA DEDUP (add_meta): Trashed post #' . $post_id . ' — duplicate subject "' . $meta_value . '"' );
        }
    } else {
        dba_mark_subject_used_today( $meta_value );
    }
}
add_action( 'added_post_meta', 'dba_catch_late_subject_meta', 10, 4 );

/**
 * Hard block: if a DBA post is about to be inserted and the subject
 * was already used today, force it to "trash" status and log a warning.
 * This is the absolute last line of defence.
 */
function dba_block_duplicate_subject_on_insert( $data, $postarr ) {
    // Only intercept new posts (not updates)
    if ( ! empty( $postarr['ID'] ) && $postarr['ID'] > 0 ) {
        return $data;
    }

    // Only if the meta flag is set via the DBA pipeline
    // (We check a custom flag that must be set by the post-creator class)
    if ( empty( $postarr['meta_input']['_dba_auto_created'] ) &&
         empty( $postarr['_dba_auto_created'] ) ) {
        return $data;
    }

    $subject = '';
    if ( ! empty( $postarr['meta_input']['_dba_subject'] ) ) {
        $subject = $postarr['meta_input']['_dba_subject'];
    }

    if ( ! empty( $subject ) && dba_subject_already_posted_today( $subject ) ) {
        // Block it: change status to trash and prepend title
        $data['post_status'] = 'trash';
        $data['post_title']  = '[DUPLICATE SUBJECT BLOCKED] ' . $data['post_title'];
        error_log( 'DBA DEDUP: Blocked duplicate subject "' . $subject . '" — title: ' . $postarr['post_title'] );
    }

    return $data;
}
add_filter( 'wp_insert_post_data', 'dba_block_duplicate_subject_on_insert', 99, 2 );

/**
 * ─── REAL-TIME REGISTRY UPDATE ──────────────────────────────────────────
 * Whenever ANY post/page is published, updated, or trashed, update the
 * internal page registry so the auto-linker always has fresh data.
 * ────────────────────────────────────────────────────────────────────────
 */
function dba_update_registry_on_save( $post_id, $post, $update ) {
    // Skip autosaves, revisions, and non-relevant post types
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
    if ( wp_is_post_revision( $post_id ) ) return;
    if ( ! in_array( $post->post_type, array( 'post', 'page' ) ) ) return;

    // Debounce: don't update registry if we just did it for this post
    $cache_key = 'dba_registry_updated_' . $post_id;
    if ( wp_cache_get( $cache_key ) ) return;
    wp_cache_set( $cache_key, true, '', 10 ); // 10 second cooldown

    try {
        if ( class_exists( 'DBA_Sitemap' ) ) {
            $sitemap = DBA_Sitemap::get_instance();
            $sitemap->update_single_post( $post_id );
        }
    } catch ( \Throwable $e ) {
        error_log( 'DBA Registry update failed for post #' . $post_id . ': ' . $e->getMessage() );
    }
}
add_action( 'wp_insert_post', 'dba_update_registry_on_save', 20, 3 );

/**
 * Also update registry when a post is trashed or deleted.
 */
function dba_update_registry_on_trash( $post_id ) {
    $post = get_post( $post_id );
    if ( ! $post || ! in_array( $post->post_type, array( 'post', 'page' ) ) ) return;

    try {
        if ( class_exists( 'DBA_Sitemap' ) ) {
            $sitemap = DBA_Sitemap::get_instance();
            $sitemap->update_single_post( $post_id );
        }
    } catch ( \Throwable $e ) {
        error_log( 'DBA Registry trash update failed for post #' . $post_id . ': ' . $e->getMessage() );
    }
}
add_action( 'wp_trash_post', 'dba_update_registry_on_trash', 20, 1 );
add_action( 'before_delete_post', 'dba_update_registry_on_trash', 20, 1 );

/**
 * Custom cron schedules
 */
function dba_custom_cron_schedules($schedules) {
    $schedules['dba_every_10_minutes'] = array(
        'interval' => 600,
        'display'  => 'Every 10 Minutes (DBA Enrichment)',
    );
    $schedules['weekly'] = array(
        'interval' => 604800,
        'display'  => 'Once Weekly',
    );
    return $schedules;
}
add_filter('cron_schedules', 'dba_custom_cron_schedules');

/**
 * ─── GLOBAL POST STAGGER ──────────────────────────────────────────────
 * Returns the stagger interval (in minutes) between posts in a batch.
 * Configurable from the Master Calendar admin page.
 * Default: 20 minutes. Range: 20–60 minutes.
 *
 * All bot schedulers call this to space out post_date timestamps,
 * making output look like a natural editorial cadence to Google.
 * ────────────────────────────────────────────────────────────────────────
 */
function dba_get_post_stagger_minutes() {
    return max( 5, min( 60, intval( get_option( 'dba_post_stagger_minutes', 20 ) ) ) );
}

/**
 * Initialize plugin
 */
function dba_init_plugin() {
    require_once DBA_PLUGIN_DIR . 'includes/class-logger.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-validator.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-sitemap.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-auto-linker.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-category-scorer.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-openai.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-image-generator.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-plagiarism.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-post-creator.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-scheduler.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-conflict-center.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-link-sanitizer.php';

    // Kaizen — Core learning engine + multilingual patterns + center dashboard
    if ( file_exists( DBA_PLUGIN_DIR . 'includes/class-kaizen.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'includes/class-kaizen.php';
    }
    if ( file_exists( DBA_PLUGIN_DIR . 'includes/class-kaizen-patterns-i18n.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'includes/class-kaizen-patterns-i18n.php';
    }
    if ( file_exists( DBA_PLUGIN_DIR . 'includes/class-kaizen-center.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'includes/class-kaizen-center.php';
    }
    require_once DBA_PLUGIN_DIR . 'includes/class-frontend.php';
    require_once DBA_PLUGIN_DIR . 'includes/class-ajax.php';

    // --- PE/Capital Markets NewsBot module (isolated) ---
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-dedupe.php';
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-validator.php';
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-generator.php';
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-scheduler.php';
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-readability.php';

    // --- GeoBot module — Scalable country investment blogs (isolated) ---
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-registry.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-dedupe.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-validator.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-generator.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-scheduler.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-linker.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-inbound.php';
    // GeoBot Kaizen + Anti-Orphan (v2.17.0)
    if ( file_exists( DBA_PLUGIN_DIR . 'geobot/class-geobot-kaizen.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-kaizen.php';
    }
    if ( file_exists( DBA_PLUGIN_DIR . 'geobot/class-geobot-anti-orphan.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-anti-orphan.php';
    }

    // --- LuxStrat module — Luxembourg structuring weekly articles (isolated) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-registry.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-dedupe.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-validator.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-generator.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-scheduler.php';
        // LuxStrat Kaizen + Anti-Orphan (v2.17.0)
        if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-kaizen.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-kaizen.php';
        }
        if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-anti-orphan.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-anti-orphan.php';
        }
        define( 'DBA_LUXSTRAT_LOADED', true );
    }

    // --- LuxStrat International — Scalable multilingual structuring articles (isolated) ---
    // Add new languages by dropping a config file into luxstrat-intl/languages/{lang}.php
    if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat-intl/class-luxstrat-intl-loader.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'luxstrat-intl/class-luxstrat-intl-loader.php';
        define( 'DBA_LUXSTRAT_INTL_LOADED', true );

        // Inbound Link Scanner — must load after loader
        if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat-intl/class-luxstrat-intl-inbound.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'luxstrat-intl/class-luxstrat-intl-inbound.php';
        }
    }

    // --- Comfisc module — Luxembourg accounting & tax articles (FR, isolated) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'comfisc/class-comfisc-loader.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'comfisc/class-comfisc-loader.php';
        define( 'DBA_COMFISC_LOADED', true );
    }

    // --- DALL-E Diagnostic Tool (TEMPORARY — remove after debugging) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'dalle-diagnostic.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'dalle-diagnostic.php';
    }

    // --- LATAM GeoBot module — Latin America investment blogs (isolated) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-cron.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-registry.php';
        require_once DBA_PLUGIN_DIR . 'latam/data/class-latam-cities.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-scheduler.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-shortcodes.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-calendar.php';
        // Kaizen learning engine + Anti-Orphan protection (v2.17.0)
        if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-kaizen.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'latam/class-latam-kaizen.php';
        }
        if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-anti-orphan.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'latam/class-latam-anti-orphan.php';
        }
        define( 'DBA_LATAM_LOADED', true );
    }

    // --- Eastern Europe GeoBot module — 12 CEE country investment blogs (isolated) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'eeurope/class-eeurope-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'eeurope/data/class-eeurope-cities.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-registry.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-validator.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-corrections.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-dedupe.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-cron.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-scheduler.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-shortcodes.php';
        DBA_EEurope_Cron::init();
        DBA_EEurope_Shortcodes::init();
        DBA_EEurope_Scheduler::get_instance();
        define( 'DBA_EEUROPE_LOADED', true );
    }

    // --- Startup Hub module — U.S. startup ecosystem articles (isolated) ---
    // Try both folder names (space-safe)
    $shub_dir = '';
    if ( file_exists( DBA_PLUGIN_DIR . 'Startups Hub/class-startup-hub-registry.php' ) ) {
        $shub_dir = 'Startups Hub/';
    } elseif ( file_exists( DBA_PLUGIN_DIR . 'startup-hub/class-startup-hub-registry.php' ) ) {
        $shub_dir = 'startup-hub/';
    } elseif ( file_exists( DBA_PLUGIN_DIR . 'Startups-Hub/class-startup-hub-registry.php' ) ) {
        $shub_dir = 'Startups-Hub/';
    } elseif ( file_exists( DBA_PLUGIN_DIR . 'startups-hub/class-startup-hub-registry.php' ) ) {
        $shub_dir = 'startups-hub/';
    }

    if ( $shub_dir ) {
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-registry.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-dedupe.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-validator.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-kaizen.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-anti-orphan.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-homepage-filter.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-generator.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-scheduler.php';
        require_once DBA_PLUGIN_DIR . $shub_dir . 'class-startup-hub-inbound.php';
        define( 'DBA_STARTUP_HUB_LOADED', true );
        define( 'DBA_STARTUP_HUB_DIR', DBA_PLUGIN_DIR . $shub_dir );
        error_log( 'DBA StartupHub: Loaded from ' . $shub_dir );
    } else {
        error_log( 'DBA StartupHub: Folder NOT FOUND. Tried: Startups Hub/, startup-hub/, Startups-Hub/, startups-hub/' );
    }

    // --- SEO Sniper module — competitive content engine (feeds intel into existing bots) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-crawler.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-crawler.php';
        require_once DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-analyzer.php';
        require_once DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-router.php';
        if ( file_exists( DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-rank-tracker.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-rank-tracker.php';
            DBA_SEO_Sniper_Rank_Tracker::get_instance();
            DBA_SEO_Sniper_Rank_Tracker::schedule();
        }
        define( 'DBA_SNIPER_LOADED', true );
        error_log( 'DBA SEO Sniper: Module loaded' );
    }

    // --- TIS module — Topic Intelligence Scanner (monthly site-wide content gap analysis) ---
    // SAFETY: Every require is inside try/catch + file_exists + class_exists
    // If ANY TIS file has an error, the rest of the plugin continues normally.
    if ( file_exists( DBA_PLUGIN_DIR . 'tis/class-tis-crawler.php' )
      && file_exists( DBA_PLUGIN_DIR . 'tis/class-tis-recommender.php' )
      && file_exists( DBA_PLUGIN_DIR . 'tis/class-tis-analyzer.php' )
      && file_exists( DBA_PLUGIN_DIR . 'tis/class-tis-scheduler.php' )
    ) {
        try {
            require_once DBA_PLUGIN_DIR . 'tis/class-tis-crawler.php';
            require_once DBA_PLUGIN_DIR . 'tis/class-tis-recommender.php';
            require_once DBA_PLUGIN_DIR . 'tis/class-tis-analyzer.php';
            require_once DBA_PLUGIN_DIR . 'tis/class-tis-scheduler.php';

            if ( class_exists( 'DBA_TIS_Crawler' )
              && class_exists( 'DBA_TIS_Recommender' )
              && class_exists( 'DBA_TIS_Analyzer' )
              && class_exists( 'DBA_TIS_Scheduler' )
            ) {
                DBA_TIS_Scheduler::get_instance();
                DBA_TIS_Scheduler::schedule();
                define( 'DBA_TIS_LOADED', true );
                error_log( 'DBA TIS: Module loaded successfully.' );
            } else {
                error_log( 'DBA TIS: One or more classes failed to load. Module disabled.' );
            }
        } catch ( \Throwable $e ) {
            error_log( 'DBA TIS LOAD ERROR (site is safe): ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() );
        }
    }

    // --- Anti-Orphan Center (site-wide orphan detection & WPML-aware link injection) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-scanner.php' )
      && file_exists( DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-injector.php' )
    ) {
        try {
            require_once DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-scanner.php';
            require_once DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-injector.php';
            define( 'DBA_ANTI_ORPHAN_LOADED', true );
            error_log( 'DBA Anti-Orphan Center: Module loaded.' );
        } catch ( \Throwable $e ) {
            error_log( 'DBA Anti-Orphan LOAD ERROR (site is safe): ' . $e->getMessage() );
        }
    }

    if (is_admin()) {
        require_once DBA_PLUGIN_DIR . 'includes/class-admin.php';
        require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-admin.php';
        require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-admin.php';
        require_once DBA_PLUGIN_DIR . 'includes/class-master-calendar.php';
        DBA_Admin::get_instance();
        DBA_NewsBot_Admin::get_instance();
        DBA_GeoBot_Admin::get_instance();
        DBA_Master_Calendar::get_instance();

        if ( defined( 'DBA_LUXSTRAT_LOADED' ) && DBA_LUXSTRAT_LOADED ) {
            require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-admin.php';
            DBA_LuxStrat_Admin::get_instance();
        }

        if ( defined( 'DBA_LATAM_LOADED' ) && DBA_LATAM_LOADED ) {
            require_once DBA_PLUGIN_DIR . 'latam/class-latam-admin.php';
            DBA_LATAM_Admin::get_instance();
            DBA_LATAM_Calendar::get_instance();
        }

        if ( defined( 'DBA_EEUROPE_LOADED' ) && DBA_EEUROPE_LOADED ) {
            require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-admin.php';
            DBA_EEurope_Admin::get_instance();
        }

        if ( defined( 'DBA_STARTUP_HUB_LOADED' ) && DBA_STARTUP_HUB_LOADED ) {
            require_once DBA_STARTUP_HUB_DIR . 'class-startup-hub-admin.php';
            DBA_Startup_Hub_Admin::get_instance();
        }

        if ( defined( 'DBA_SNIPER_LOADED' ) && DBA_SNIPER_LOADED ) {
            require_once DBA_PLUGIN_DIR . 'seo-sniper/class-seo-sniper-admin.php';
            DBA_SEO_Sniper_Admin::get_instance();
        }

        if ( defined( 'DBA_TIS_LOADED' ) && DBA_TIS_LOADED ) {
            try {
                require_once DBA_PLUGIN_DIR . 'tis/class-tis-admin.php';
                if ( class_exists( 'DBA_TIS_Admin' ) ) {
                    DBA_TIS_Admin::get_instance();
                }
            } catch ( \Throwable $e ) {
                error_log( 'DBA TIS Admin load error (admin is safe): ' . $e->getMessage() );
            }
        }

        // Anti-Orphan Center admin dashboard
        if ( defined( 'DBA_ANTI_ORPHAN_LOADED' ) && DBA_ANTI_ORPHAN_LOADED ) {
            require_once DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-admin.php';
            DBA_Anti_Orphan_Admin::get_instance();
        }

        // Anti-Orphan Cleanup + Third-Party Link Scanner
        if ( file_exists( DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-cleanup.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'anti-orphan/class-anti-orphan-cleanup.php';
        }

        // Comfisc admin (Comptabilité & Fiscalité)
        if ( defined( 'DBA_COMFISC_LOADED' ) && DBA_COMFISC_LOADED ) {
            if ( file_exists( DBA_PLUGIN_DIR . 'comfisc/class-comfisc-admin.php' ) ) {
                require_once DBA_PLUGIN_DIR . 'comfisc/class-comfisc-admin.php';
                DBA_Comfisc_Admin::get_instance();
            }
        }
    }

    DBA_Logger::get_instance();
    DBA_Validator::get_instance();
    DBA_Sitemap::get_instance();
    DBA_OpenAI::get_instance();
    DBA_Image_Generator::get_instance();
    DBA_Plagiarism::get_instance();
    DBA_Post_Creator::get_instance();
    DBA_Scheduler::get_instance();
    DBA_Frontend::get_instance();
    DBA_AJAX::get_instance();

    // --- PE/Capital Markets NewsBot instances ---
    DBA_NewsBot_Generator::get_instance();
    DBA_NewsBot_Scheduler::get_instance();

    // --- GeoBot instances (country investment blogs) ---
    DBA_GeoBot_Generator::get_instance();
    DBA_GeoBot_Scheduler::get_instance();
    DBA_GeoBot_Linker::get_instance();
    DBA_GeoBot_Inbound::get_instance();

    // --- LuxStrat instances (Luxembourg structuring weekly) ---
    if ( defined( 'DBA_LUXSTRAT_LOADED' ) && DBA_LUXSTRAT_LOADED ) {
        DBA_LuxStrat_Generator::get_instance();
        DBA_LuxStrat_Scheduler::get_instance();
    }

    // --- LATAM instances ---
    if ( defined( 'DBA_LATAM_LOADED' ) && DBA_LATAM_LOADED ) {
        DBA_LATAM_Scheduler::get_instance();
        DBA_LATAM_Shortcodes::get_instance();
    }

    // --- Startup Hub instances (U.S. startup ecosystem) ---
    if ( defined( 'DBA_STARTUP_HUB_LOADED' ) && DBA_STARTUP_HUB_LOADED ) {
        DBA_Startup_Hub_Generator::get_instance();
        DBA_Startup_Hub_Scheduler::get_instance();
        DBA_Startup_Hub_Anti_Orphan::get_instance();
        DBA_Startup_Hub_Homepage_Filter::get_instance();
        DBA_Startup_Hub_Inbound::get_instance();
    }
}
add_action('plugins_loaded', 'dba_init_plugin');

/* ═══════════════════════════════════════════════════════════════════════════
 *  GLOBAL LANGUAGE FILTER — Hide GeoBot posts on wrong-language pages.
 *
 *  Italy GeoBot posts ONLY appear on ?lang=it
 *  France GeoBot posts ONLY appear on ?lang=fr
 *  Spain  GeoBot posts ONLY appear on ?lang=es
 *  etc.
 *
 *  Applies to EVERY frontend query: homepage, archives, categories, search,
 *  widgets, RSS feeds, related posts — everything.
 * ═══════════════════════════════════════════════════════════════════════════ */
add_action( 'pre_get_posts', 'dba_geo_hide_foreign_posts' );
function dba_geo_hide_foreign_posts( $query ) {
    // Only filter frontend
    if ( is_admin() ) return;

    // Need the registry
    if ( ! class_exists( 'DBA_GeoBot_Registry' ) ) return;

    // Detect current WPML language (respects switch_lang() calls)
    $current_lang = '';
    if ( function_exists( 'icl_get_languages' ) ) {
        global $sitepress;
        if ( $sitepress ) {
            $current_lang = $sitepress->get_current_language();
        }
    }
    if ( empty( $current_lang ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
        $current_lang = ICL_LANGUAGE_CODE;
    }

    // No WPML or "show all languages" view → don't filter
    if ( empty( $current_lang ) || $current_lang === 'all' ) return;

    // Get all registered GeoBot countries
    $countries = DBA_GeoBot_Registry::get_countries();
    if ( empty( $countries ) ) return;

    // Build exclusion: for each country whose language ≠ current page language,
    // exclude posts that have that country's auto_created meta key.
    $existing_meta = $query->get( 'meta_query' );
    if ( ! is_array( $existing_meta ) ) $existing_meta = array();

    $hub_exclude_ids = array();
    $added           = false;

    foreach ( $countries as $key => $config ) {
        if ( ! isset( $config['wpml_code'], $config['meta_prefix'] ) ) continue;

        if ( $config['wpml_code'] === $current_lang ) continue; // This country matches → don't hide

        // Exclude city posts: they have {meta_prefix}auto_created
        $existing_meta[] = array(
            'key'     => $config['meta_prefix'] . 'auto_created',
            'compare' => 'NOT EXISTS',
        );
        $added = true;

        // Collect hub post IDs for this country (max 1 per country, very cheap)
        $hub_id = get_option( 'dba_geo_' . $key . '_hub_page_id', 0 );
        if ( $hub_id ) {
            $hub_exclude_ids[] = intval( $hub_id );
        }
    }

    if ( $added ) {
        // Ensure AND relation so all exclusions apply
        if ( ! isset( $existing_meta['relation'] ) ) {
            $existing_meta['relation'] = 'AND';
        }
        $query->set( 'meta_query', $existing_meta );
    }

    // Exclude hub posts via post__not_in (only a few IDs, very cheap)
    if ( ! empty( $hub_exclude_ids ) ) {
        $current_not_in = $query->get( 'post__not_in' );
        if ( ! is_array( $current_not_in ) ) $current_not_in = array();
        $query->set( 'post__not_in', array_merge( $current_not_in, $hub_exclude_ids ) );
    }
}

/**
 * Activation
 */
function dba_activate() {
    global $wpdb;

    // Create log table
    $table = $wpdb->prefix . DBA_LOG_TABLE;
    $charset = $wpdb->get_charset_collate();
    $sql = "CREATE TABLE IF NOT EXISTS {$table} (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        run_date DATE NOT NULL,
        run_time DATETIME NOT NULL,
        theme VARCHAR(100) DEFAULT '',
        title VARCHAR(255) DEFAULT '',
        slug VARCHAR(255) DEFAULT '',
        post_id BIGINT UNSIGNED DEFAULT 0,
        media_id BIGINT UNSIGNED DEFAULT 0,
        language VARCHAR(10) DEFAULT 'en',
        json_output LONGTEXT DEFAULT '',
        validation_result TEXT DEFAULT '',
        retry_attempts INT DEFAULT 0,
        status VARCHAR(20) DEFAULT 'pending',
        error_trace TEXT DEFAULT '',
        PRIMARY KEY (id),
        KEY run_date (run_date),
        KEY status (status)
    ) {$charset};";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);

    // Default settings
    if (!get_option('dba_settings')) {
        add_option('dba_settings', array(
            'openai_api_key'    => '',
            'xai_api_key'       => '',
            'image_provider'    => 'openai',  // 'openai' or 'xai'
            'openai_model'      => 'gpt-4o-mini',
            'image_model'       => 'dall-e-3',
            'posts_per_run'     => 5,
            'post_status'       => 'publish',
            'image_size'        => '950x534',
        ));
    } else {
        // Ensure new settings exist for existing installs
        $settings = get_option('dba_settings');
        $updated  = false;
        if ( ! isset( $settings['xai_api_key'] ) ) {
            $settings['xai_api_key'] = '';
            $updated = true;
        }
        if ( ! isset( $settings['image_provider'] ) ) {
            $settings['image_provider'] = 'openai';
            $updated = true;
        }
        if ( $updated ) {
            update_option( 'dba_settings', $settings );
        }
    }

    if (!get_option('dba_schedule_settings')) {
        add_option('dba_schedule_settings', array(
            'enabled'       => false,
            'posting_days'  => array('monday', 'wednesday', 'saturday'),
            'posting_times' => array('09:10', '11:20', '13:30', '15:40', '17:50'),
            'timezone'      => 'Europe/Luxembourg',
        ));
    }

    if (!get_option('dba_content_settings')) {
        add_option('dba_content_settings', array(
            'subjects'       => array(),
            'languages'      => array('en'),
            'pillar_url'     => 'https://www.damalion.com/blog/',
            'cta_html'       => '<p>Damalion supports investors, entrepreneurs, and family offices with compliant structuring, governance, and alignment of Luxembourg holding companies. Please <a href="https://www.damalion.com/lets-connect/" target="_blank" rel="noopener">contact your Damalion experts now</a>.</p>',
            'sitemap_url'    => 'https://www.damalion.com/sitemap.xml',
        ));
    }

    // Migration: purge old fake pillar URL
    $content_settings = get_option('dba_content_settings', array());
    if (isset($content_settings['pillar_url']) && strpos($content_settings['pillar_url'], 'core-structuring-tool') !== false) {
        $content_settings['pillar_url'] = 'https://www.damalion.com/blog/';
        update_option('dba_content_settings', $content_settings);
    }

    // Schedule daily generation
    require_once DBA_PLUGIN_DIR . 'includes/class-scheduler.php';
    DBA_Scheduler::activate();

    // --- PE/Capital Markets NewsBot cron (15:00 UTC = 10:00 AM New York — isolated from core 06:00 UTC) ---
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-scheduler.php';
    DBA_NewsBot_Scheduler::activate();

    // --- GeoBot module activation (country investment blogs) ---
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-registry.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-dedupe.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-scheduler.php';
    DBA_GeoBot_Scheduler::activate();

    // --- LuxStrat module activation (Luxembourg structuring weekly) ---
    if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-registry.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-dedupe.php';
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-scheduler.php';
        DBA_LuxStrat_Scheduler::activate();
    }

    // --- LATAM module activation ---
    if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-cron.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-registry.php';
        require_once DBA_PLUGIN_DIR . 'latam/data/class-latam-cities.php';
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-scheduler.php';
        if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-kaizen.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'latam/class-latam-kaizen.php';
        }
        if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-anti-orphan.php' ) ) {
            require_once DBA_PLUGIN_DIR . 'latam/class-latam-anti-orphan.php';
        }
        DBA_LATAM_Scheduler::activate();
        DBA_LATAM_Cities::load_all_cities();

        // Default LATAM settings
        if ( ! get_option( 'dba_latam_settings' ) ) {
            add_option( 'dba_latam_settings', array(
                'enabled'            => false,
                'post_status'        => 'publish',
                'author_id'          => 1,
                'category_id'        => 0,
                'word_count_min'     => 500,
                'word_count_max'     => 800,
                'enabled_countries'  => array(),
                'cta_text'           => 'Damalion supports international investors, entrepreneurs, and family offices establishing and structuring their business in {Country}.',
                'cta_link_text'      => 'Contact your Damalion experts now.',
                'cta_link_url'       => 'https://www.damalion.com/lets-connect/',
            ) );
        }

        // Default settings per LATAM country
        foreach ( DBA_LATAM_Registry::get_countries() as $key => $geo_config ) {
            if ( ! get_option( $geo_config['settings_option'] ) ) {
                add_option( $geo_config['settings_option'], array(
                    'enabled'          => false,
                    'posting_days'     => $geo_config['default_posting_days'],
                    'posts_per_run'    => $geo_config['default_posts_per_run'],
                    'post_status'      => 'publish',
                    'author_id'        => 1,
                    'active_communes'  => array(),
                    'cta_text'         => $geo_config['cta_text'],
                    'cta_link_text'    => $geo_config['cta_link_text'],
                    'cta_link_url'     => $geo_config['cta_link_url'],
                    'word_count_min'   => $geo_config['word_count_min'],
                    'word_count_max'   => $geo_config['word_count_max'],
                ) );
            }
        }

        error_log( 'DBA LATAM: Module activated — 15 countries, 75 cities loaded.' );
    }

    // --- Eastern Europe module activation ---
    if ( file_exists( DBA_PLUGIN_DIR . 'eeurope/class-eeurope-registry.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'eeurope/data/class-eeurope-cities.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-registry.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-validator.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-corrections.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-dedupe.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-cron.php';
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-scheduler.php';
        DBA_EEurope_Cron::init();
        DBA_EEurope_Scheduler::activate();

        // Default EEurope settings
        if ( ! get_option( 'dba_eeurope_settings' ) ) {
            add_option( 'dba_eeurope_settings', array(
                'enabled'            => false,
                'posts_per_run'      => 3,
                'cooldown_days'      => 5,
                'post_status'        => 'publish',
                'author_id'          => 1,
                'category_id'        => 0,
                'word_count_min'     => 1000,
                'word_count_max'     => 1800,
                'cron_hour'          => 9,
                'posting_days'       => array( 'thursday', 'friday' ),
                'enabled_countries'  => array(),
                'cta_text'           => 'Damalion supports international investors, entrepreneurs, and family offices establishing and structuring their business in {Country}.',
                'cta_link_text'      => 'Contact your Damalion experts now.',
                'cta_link_url'       => 'https://www.damalion.com/lets-connect/',
            ) );
        }

        // Default settings per EEurope country
        foreach ( DBA_EEurope_Registry::get_countries() as $key => $geo_config ) {
            if ( ! get_option( $geo_config['settings_option'] ) ) {
                add_option( $geo_config['settings_option'], array(
                    'enabled'          => false,
                    'active_communes'  => array(),
                ) );
            }
        }

        error_log( 'DBA EEurope: Module activated — 12 countries loaded.' );
    }

    // Default GeoBot settings per country
    foreach ( DBA_GeoBot_Registry::get_countries() as $key => $geo_config ) {
        if ( ! get_option( $geo_config['settings_option'] ) ) {
            add_option( $geo_config['settings_option'], array(
                'enabled'          => false,
                'posting_days'     => $geo_config['default_posting_days'],
                'posts_per_run'    => $geo_config['default_posts_per_run'],
                'post_status'      => 'publish',
                'author_id'        => 1,
                'active_communes'  => array(),
                'cta_text'         => $geo_config['cta_text'],
                'cta_link_text'    => $geo_config['cta_link_text'],
                'cta_link_url'     => $geo_config['cta_link_url'],
                'word_count_min'   => $geo_config['word_count_min'],
                'word_count_max'   => $geo_config['word_count_max'],
            ));
        }
    }

    // Load France communes from built-in data if not already loaded
    if ( empty( get_option( 'dba_geo_france_communes', array() ) ) ) {
        dba_load_france_communes();
    }

    // Load Italy communes from built-in data if not already loaded
    if ( empty( get_option( 'dba_geo_italy_communes', array() ) ) ) {
        dba_load_italy_communes();
    }

    // Default PE settings
    if (!get_option('dba_pe_settings')) {
        add_option('dba_pe_settings', array(
            'enabled'              => false,
            'posting_days'         => array('monday', 'wednesday', 'friday'),
            'posts_per_day'        => 6,
            'post_status'          => 'publish',
            'timezone'             => 'America/New_York',
            'author_id'            => 1,
            'active_geographies'   => array('New York', 'Florida', 'Colorado', 'Texas', 'California', 'New Hampshire', 'Arizona'),
            'custom_categories'    => array(),
            'word_count_min'       => 500,
            'word_count_max'       => 600,
            'require_subtitle'     => true,
            'require_company_link' => true,
            'require_gov_link'     => true,
            'cta_enabled'          => true,
            'cta_text'             => 'Damalion helps international family offices, entrepreneurs, private equity and venture capital firms identify opportunities to achieve their goals.',
            'cta_link_text'        => 'Contact your Damalion experts now.',
            'cta_link_url'         => 'https://www.damalion.com/lets-connect/',
        ));
    }

    // DO NOT scan on activation — shared hosting will timeout with 35k posts.
    // The admin will click "Scan Internal Pages" after activating.
    // Just log that we skipped it.

    // Default LuxStrat settings (Luxembourg structuring weekly articles)
    if (!get_option('dba_luxstrat_settings')) {
        add_option('dba_luxstrat_settings', array(
            'enabled'          => false,
            'active_themes'    => array('scsp', 'raif', 'depositary_bank', 'sicav_raif'),
            'post_status'      => 'publish',
            'author_id'        => 1,
            'category_id'      => 0,
            'word_count_min'   => 1000,
            'word_count_max'   => 1800,
            'wpml_language'    => 'en',
            'cta_text'         => 'Damalion supports institutional investors, fund managers, and family offices with compliant Luxembourg structuring solutions.',
            'cta_link_text'    => 'Contact your Damalion experts now.',
            'cta_link_url'     => 'https://www.damalion.com/lets-connect/',
        ));
    }

    error_log( 'DBA: Plugin activated. Use "Scan Internal Pages" button to build the registry.' );
}

/**
 * Load France communes data into wp_options.
 * Data extracted from the COMMUNE_CITY_REGION.xlsx file.
 * Called on activation if option is empty.
 */
function dba_load_france_communes() {
    $data_file = DBA_PLUGIN_DIR . 'geobot/data/france-communes.php';
    if ( file_exists( $data_file ) ) {
        $communes = include $data_file;
        if ( is_array( $communes ) && ! empty( $communes ) ) {
            update_option( 'dba_geo_france_communes', $communes, false );
            error_log( 'DBA GeoBot: Loaded ' . count( $communes ) . ' France communes' );
        }
    } else {
        error_log( 'DBA GeoBot: France communes data file not found at ' . $data_file );
    }
}

/**
 * Load Italy communes data into wp_options.
 * 250 Italian cities (comuni) with provincia and regione.
 * Called on activation if option is empty.
 */
function dba_load_italy_communes() {
    $data_file = DBA_PLUGIN_DIR . 'geobot/data/italy-communes.php';
    if ( file_exists( $data_file ) ) {
        $communes = include $data_file;
        if ( is_array( $communes ) && ! empty( $communes ) ) {
            update_option( 'dba_geo_italy_communes', $communes, false );
            error_log( 'DBA GeoBot: Loaded ' . count( $communes ) . ' Italy communes' );
        }
    } else {
        error_log( 'DBA GeoBot: Italy communes data file not found at ' . $data_file );
    }
}

register_activation_hook(__FILE__, 'dba_activate');

/**
 * Deactivation
 */
function dba_deactivate() {
    require_once DBA_PLUGIN_DIR . 'includes/class-scheduler.php';
    DBA_Scheduler::deactivate();

    // --- PE/Capital Markets NewsBot cron cleanup ---
    require_once DBA_PLUGIN_DIR . 'newsbot/class-newsbot-scheduler.php';
    DBA_NewsBot_Scheduler::deactivate();

    // --- GeoBot cron cleanup ---
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-registry.php';
    require_once DBA_PLUGIN_DIR . 'geobot/class-geobot-scheduler.php';
    DBA_GeoBot_Scheduler::deactivate();

    // --- LuxStrat cron cleanup ---
    if ( file_exists( DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-scheduler.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'luxstrat/class-luxstrat-scheduler.php';
        DBA_LuxStrat_Scheduler::deactivate();
    }

    // --- LATAM cron cleanup ---
    if ( file_exists( DBA_PLUGIN_DIR . 'latam/class-latam-scheduler.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'latam/class-latam-scheduler.php';
        DBA_LATAM_Scheduler::deactivate();
    }

    // --- Eastern Europe cron cleanup ---
    if ( file_exists( DBA_PLUGIN_DIR . 'eeurope/class-eeurope-scheduler.php' ) ) {
        require_once DBA_PLUGIN_DIR . 'eeurope/class-eeurope-scheduler.php';
        DBA_EEurope_Scheduler::deactivate();
    }

    // --- Comfisc cron cleanup ---
    $comfisc_hooks = array( 'dba_comfisc_fr_cron' );
    foreach ( $comfisc_hooks as $ch ) {
        $ts = wp_next_scheduled( $ch );
        if ( $ts ) wp_unschedule_event( $ts, $ch );
    }
}
register_deactivation_hook(__FILE__, 'dba_deactivate');

/**
 * One-time migration: purge old fake pillar URL from database
 */
function dba_cleanup_old_data() {
    try {
        $content = get_option('dba_content_settings', array());
        if (!is_array($content)) return;

        $dirty = false;

        // Remove the old fake pillar URL
        if (isset($content['pillar_url']) && strpos($content['pillar_url'], 'core-structuring-tool') !== false) {
            $content['pillar_url'] = 'https://www.damalion.com/blog/';
            $dirty = true;
        }

        if ($dirty) {
            update_option('dba_content_settings', $content);
            error_log('DBA: Cleaned old fake pillar URL from settings');
        }
    } catch (\Throwable $e) {
        error_log('DBA cleanup error: ' . $e->getMessage());
    }
}
add_action('plugins_loaded', 'dba_cleanup_old_data');

/**
 * Auto-load missing communes data on plugins_loaded.
 * Ensures Italy communes are loaded without requiring deactivation/reactivation.
 */
function dba_autoload_missing_communes() {
    if ( empty( get_option( 'dba_geo_italy_communes', array() ) ) ) {
        $data_file = ( defined( 'DBA_PLUGIN_DIR' ) ? DBA_PLUGIN_DIR : '' ) . 'geobot/data/italy-communes.php';
        if ( file_exists( $data_file ) ) {
            $communes = include $data_file;
            if ( is_array( $communes ) && ! empty( $communes ) ) {
                update_option( 'dba_geo_italy_communes', $communes, false );
                error_log( 'DBA GeoBot: Auto-loaded ' . count( $communes ) . ' Italy communes' );
            }
        }
    }
}
add_action('plugins_loaded', 'dba_autoload_missing_communes');

/* ═══════════════════════════════════════════════════════════════════════════
 *  ONE-TIME CLEANUP: Trash contaminated GeoBot hub posts (v2.13.0)
 *
 *  The old code auto-created hub posts with English categories
 *  ("Company formation/Business registration", "Investments").
 *  This runs ONCE, detects them, trashes them, deletes the options,
 *  and flushes all caches. The admin creates their own hub post.
 *
 *  Runs at priority 20 (AFTER dba_init_plugin at priority 10).
 *  Does NOT depend on DBA_GeoBot_Registry — hardcodes known country keys.
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_v213_cleanup_contaminated_hubs() {
    // v2.13.0b: delete stale flag from broken first deploy (class wasn't loaded at priority 5)
    // This ensures the cleanup actually runs this time.
    if ( ! get_option( 'dba_v213_hub_cleanup_v2_done' ) ) {
        delete_option( 'dba_v213_hub_cleanup_done' );
    }

    // Only run once (use v2 flag)
    if ( get_option( 'dba_v213_hub_cleanup_v2_done' ) ) return;

    // Hardcoded country keys — no dependency on registry class loading
    $country_keys = array( 'france', 'italy', 'spain', 'portugal' );

    $cleaned = array();

    foreach ( $country_keys as $key ) {
        $option_name = 'dba_geo_' . $key . '_hub_page_id';
        $hub_id      = intval( get_option( $option_name, 0 ) );

        if ( ! $hub_id ) continue;

        $hub_post = get_post( $hub_id );
        if ( ! $hub_post || $hub_post->post_status === 'trash' ) {
            // Already gone — just clean the option
            delete_option( $option_name );
            continue;
        }

        // Check if this hub has English categories that don't belong
        $categories = wp_get_post_categories( $hub_id, array( 'fields' => 'names' ) );
        $english_cats = array(
            'Company formation/Business registration',
            'Investments',
            'Uncategorized',
        );

        $has_english = false;
        foreach ( $categories as $cat_name ) {
            if ( in_array( $cat_name, $english_cats, true ) ) {
                $has_english = true;
                break;
            }
        }

        if ( $has_english ) {
            // Trash the contaminated hub
            wp_trash_post( $hub_id );
            delete_option( $option_name );
            $cleaned[] = "[{$key}] Trashed contaminated hub #{$hub_id} (had English categories: " . implode( ', ', $categories ) . ")";
            error_log( "DBA v2.13.0 CLEANUP: Trashed contaminated {$key} hub #{$hub_id} — English categories: " . implode( ', ', $categories ) );
        }
    }

    // Flush caches after cleanup
    if ( ! empty( $cleaned ) ) {
        wp_cache_flush();

        // WPML cache
        if ( function_exists( 'icl_cache_clear' ) ) {
            icl_cache_clear();
        }

        // Page cache plugins
        if ( function_exists( 'rocket_clean_domain' ) ) rocket_clean_domain();
        if ( function_exists( 'wp_cache_clear_cache' ) ) wp_cache_clear_cache();
        if ( function_exists( 'w3tc_flush_all' ) ) w3tc_flush_all();
        if ( has_action( 'litespeed_purge_all' ) ) do_action( 'litespeed_purge_all' );
        if ( function_exists( 'sg_cachepress_purge_cache' ) ) sg_cachepress_purge_cache();
        if ( function_exists( 'et_core_clear_wp_cache' ) ) et_core_clear_wp_cache();

        error_log( "DBA v2.13.0 CLEANUP COMPLETE: " . implode( ' | ', $cleaned ) );
    } else {
        error_log( "DBA v2.13.0 CLEANUP: No contaminated hubs found (or already cleaned)." );
    }

    // Mark as done — never run again
    update_option( 'dba_v213_hub_cleanup_v2_done', true, false );
}
add_action( 'plugins_loaded', 'dba_v213_cleanup_contaminated_hubs', 20 );

/* ═══════════════════════════════════════════════════════════════════════════
 *  v2.14.0 — PER-COUNTRY HUB TRASH (AJAX)
 *
 *  Adds an AJAX endpoint so the admin can trash any single country's
 *  hub post from the GeoBot admin panel without touching others.
 *  Trashes the post, deletes the option pointer, flushes caches.
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_ajax_trash_hub_post() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( 'Permission denied' );
    }

    $country = sanitize_key( $_POST['country'] ?? '' );
    if ( empty( $country ) ) {
        wp_send_json_error( 'Missing country parameter' );
    }

    $option_name = 'dba_geo_' . $country . '_hub_page_id';
    $hub_id      = intval( get_option( $option_name, 0 ) );

    if ( ! $hub_id ) {
        delete_option( $option_name );
        wp_send_json_error( 'No hub post found for ' . $country );
    }

    $hub_post = get_post( $hub_id );
    $title    = $hub_post ? $hub_post->post_title : '(unknown)';

    // Trash the post
    if ( $hub_post && $hub_post->post_status !== 'trash' ) {
        wp_trash_post( $hub_id );
    }

    // Delete the option pointer so admin can create a new one
    delete_option( $option_name );

    // Flush caches
    wp_cache_flush();
    if ( function_exists( 'icl_cache_clear' ) ) icl_cache_clear();
    if ( function_exists( 'rocket_clean_domain' ) ) rocket_clean_domain();
    if ( function_exists( 'wp_cache_clear_cache' ) ) wp_cache_clear_cache();
    if ( function_exists( 'w3tc_flush_all' ) ) w3tc_flush_all();
    if ( has_action( 'litespeed_purge_all' ) ) do_action( 'litespeed_purge_all' );
    if ( function_exists( 'sg_cachepress_purge_cache' ) ) sg_cachepress_purge_cache();
    if ( function_exists( 'et_core_clear_wp_cache' ) ) et_core_clear_wp_cache();

    error_log( "DBA v2.14: Admin trashed {$country} hub #{$hub_id} ({$title})" );

    wp_send_json_success( array(
        'message'  => ucfirst( $country ) . ' hub post trashed successfully.',
        'hub_id'   => $hub_id,
        'title'    => $title,
    ) );
}
add_action( 'wp_ajax_dba_trash_hub_post', 'dba_ajax_trash_hub_post' );

/**
 * Admin notice: Show trash buttons for ALL countries that have a hub post.
 * Appears at the top of any DBA/GeoBot admin page. Fully self-contained.
 */
function dba_hub_management_admin_notice() {
    $screen = get_current_screen();
    if ( ! $screen ) return;
    // Show on any DBA admin page
    if ( strpos( $screen->id, 'dba' ) === false && strpos( $screen->id, 'damalion' ) === false && strpos( $screen->id, 'geobot' ) === false ) return;
    if ( ! current_user_can( 'manage_options' ) ) return;

    $country_keys = array( 'france', 'italy', 'spain', 'portugal' );
    $hubs = array();

    foreach ( $country_keys as $key ) {
        $option_name = 'dba_geo_' . $key . '_hub_page_id';
        $hub_id      = intval( get_option( $option_name, 0 ) );
        if ( ! $hub_id ) continue;
        $hub_post = get_post( $hub_id );
        if ( ! $hub_post || $hub_post->post_status === 'trash' ) {
            delete_option( $option_name );
            continue;
        }
        $cats = wp_get_post_categories( $hub_id, array( 'fields' => 'names' ) );
        $hubs[ $key ] = array(
            'id'         => $hub_id,
            'title'      => $hub_post->post_title,
            'status'     => $hub_post->post_status,
            'categories' => implode( ', ', $cats ),
            'edit_url'   => get_edit_post_link( $hub_id, 'raw' ),
        );
    }

    if ( empty( $hubs ) ) return;
    ?>
    <div class="notice notice-warning" style="padding:12px 16px;border-left-color:#d63638;">
        <p><strong>🔗 GeoBot Hub Posts — Manage & Delete</strong></p>
        <table style="width:100%;border-collapse:collapse;margin:8px 0;">
            <thead>
                <tr style="text-align:left;border-bottom:2px solid #ccc;">
                    <th style="padding:6px 10px;">Country</th>
                    <th style="padding:6px 10px;">Hub Post</th>
                    <th style="padding:6px 10px;">Categories</th>
                    <th style="padding:6px 10px;">Status</th>
                    <th style="padding:6px 10px;">Actions</th>
                </tr>
            </thead>
            <tbody>
            <?php foreach ( $hubs as $key => $hub ) : ?>
                <tr style="border-bottom:1px solid #eee;" id="dba-hub-row-<?php echo esc_attr( $key ); ?>">
                    <td style="padding:8px 10px;font-weight:600;"><?php echo esc_html( strtoupper( $key ) ); ?></td>
                    <td style="padding:8px 10px;">
                        <a href="<?php echo esc_url( $hub['edit_url'] ); ?>" target="_blank"><?php echo esc_html( $hub['title'] ); ?></a>
                        <span style="color:#999;font-size:12px;">(#<?php echo $hub['id']; ?>)</span>
                    </td>
                    <td style="padding:8px 10px;color:<?php echo ( strpos( $hub['categories'], 'Investments' ) !== false || strpos( $hub['categories'], 'Company formation' ) !== false ) ? '#d63638;font-weight:700' : '#333'; ?>;">
                        <?php echo esc_html( $hub['categories'] ); ?>
                        <?php if ( strpos( $hub['categories'], 'Investments' ) !== false || strpos( $hub['categories'], 'Company formation' ) !== false ) : ?>
                            <span style="color:#d63638;"> ⚠ ENGLISH!</span>
                        <?php endif; ?>
                    </td>
                    <td style="padding:8px 10px;"><?php echo esc_html( $hub['status'] ); ?></td>
                    <td style="padding:8px 10px;">
                        <button type="button"
                            class="button dba-trash-hub-btn"
                            data-country="<?php echo esc_attr( $key ); ?>"
                            style="color:#b32d2e;border-color:#b32d2e;font-weight:600;"
                            onclick="dbaTrashHub('<?php echo esc_attr( $key ); ?>')">
                            🗑 Trash &amp; Reset
                        </button>
                    </td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <p style="color:#666;font-size:12px;margin:4px 0 0;">Trashing a hub deletes the post and clears the reference so you can create a new one with correct categories.</p>
    </div>
    <?php
}
add_action( 'admin_notices', 'dba_hub_management_admin_notice' );

/**
 * Inject the JS handler for trash-hub buttons into admin footer.
 * Also auto-injects trash buttons next to any hub post link found in the page DOM.
 */
function dba_trash_hub_inline_js() {
    $screen = get_current_screen();
    if ( ! $screen ) return;
    if ( strpos( $screen->id, 'dba' ) === false && strpos( $screen->id, 'damalion' ) === false && strpos( $screen->id, 'geobot' ) === false ) return;

    // Build a map of country → hub_id for DOM injection
    $country_keys = array( 'france', 'italy', 'spain', 'portugal' );
    $hub_map = array();
    foreach ( $country_keys as $key ) {
        $hub_id = intval( get_option( 'dba_geo_' . $key . '_hub_page_id', 0 ) );
        if ( $hub_id ) $hub_map[ $key ] = $hub_id;
    }
    ?>
    <script type="text/javascript">
    function dbaTrashHub(country) {
        if (!confirm('⚠️ TRASH the ' + country.toUpperCase() + ' hub post and clear the reference?\n\nThis will move it to trash and let you create a new one.')) return;
        var btns = document.querySelectorAll('.dba-trash-hub-btn[data-country="' + country + '"]');
        btns.forEach(function(b){ b.disabled = true; b.textContent = '⏳ Trashing…'; });
        jQuery.post(ajaxurl || dba_admin.ajaxurl, {
            action: 'dba_trash_hub_post',
            nonce: (typeof dba_admin !== 'undefined') ? dba_admin.nonce : '<?php echo wp_create_nonce("dba_admin_nonce"); ?>',
            country: country
        }, function(resp) {
            if (resp.success) {
                btns.forEach(function(b){ b.textContent = '✅ Trashed!'; b.style.color = '#46b450'; b.style.borderColor = '#46b450'; });
                var row = document.getElementById('dba-hub-row-' + country);
                if (row) row.style.opacity = '0.4';
                setTimeout(function(){ location.reload(); }, 800);
            } else {
                alert('❌ Error: ' + (resp.data || 'Unknown error'));
                btns.forEach(function(b){ b.disabled = false; b.textContent = '🗑 Trash & Reset'; });
            }
        }).fail(function() {
            alert('❌ AJAX request failed. Check your connection.');
            btns.forEach(function(b){ b.disabled = false; b.textContent = '🗑 Trash & Reset'; });
        });
    }
    /* Auto-inject inline trash buttons next to hub links in existing admin panels */
    jQuery(function($){
        var hubMap = <?php echo json_encode( $hub_map ); ?>;
        $.each(hubMap, function(country, hubId) {
            $('a[href*="post=' + hubId + '"], a[href*="p=' + hubId + '"]').each(function(){
                if ($(this).next('.dba-trash-hub-btn').length) return;
                var btn = $('<button type="button" class="button dba-trash-hub-btn" style="margin-left:8px;color:#b32d2e;border-color:#b32d2e;font-size:12px;padding:2px 8px;vertical-align:middle;cursor:pointer;" data-country="' + country + '">🗑 Trash Hub</button>');
                btn.on('click', function(e){ e.preventDefault(); dbaTrashHub(country); });
                $(this).after(btn);
            });
        });
    });
    </script>
    <?php
}
add_action( 'admin_footer', 'dba_trash_hub_inline_js' );

/* ═══════════════════════════════════════════════════════════════════════════
 *  WPML CATEGORY TRANSLATION HELPER
 *
 *  Returns the translated term_id for a given category in the target language.
 *  Falls back to the original ID if no translation exists or WPML is inactive.
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_get_translated_category_id( $category_id, $language_code ) {
    if ( empty( $category_id ) || empty( $language_code ) ) {
        return $category_id;
    }
    if ( has_filter( 'wpml_object_id' ) ) {
        $translated_id = apply_filters( 'wpml_object_id', $category_id, 'category', false, $language_code );
        if ( $translated_id ) return intval( $translated_id );
    }
    if ( function_exists( 'icl_object_id' ) ) {
        $translated_id = icl_object_id( $category_id, 'category', false, $language_code );
        if ( $translated_id ) return intval( $translated_id );
    }
    return $category_id;
}

function dba_translate_category_ids( $category_ids, $language_code ) {
    if ( empty( $category_ids ) || empty( $language_code ) || $language_code === 'en' ) {
        return $category_ids;
    }
    $translated = array();
    foreach ( (array) $category_ids as $cat_id ) {
        $translated[] = dba_get_translated_category_id( intval( $cat_id ), $language_code );
    }
    return array_unique( array_filter( $translated ) );
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  AUTO-FIX CATEGORIES ON GEOBOT / LATAM POST CREATION
 *
 *  When any GeoBot or LATAM post is saved, detect its target language
 *  from the country config and re-assign categories to the WPML translation.
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_fix_geobot_post_category_on_save( $post_id, $post, $update ) {
    if ( $update ) return;
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
    if ( $post->post_type !== 'post' ) return;
    static $fixing = array();
    if ( isset( $fixing[ $post_id ] ) ) return;
    if ( ! class_exists( 'DBA_GeoBot_Registry' ) ) return;

    $countries = DBA_GeoBot_Registry::get_countries();
    $target_lang = '';
    foreach ( $countries as $key => $config ) {
        if ( empty( $config['meta_prefix'] ) || empty( $config['wpml_code'] ) ) continue;
        if ( get_post_meta( $post_id, $config['meta_prefix'] . 'auto_created', true ) ) {
            $target_lang = $config['wpml_code'];
            break;
        }
    }
    if ( empty( $target_lang ) || $target_lang === 'en' ) return;

    $current_cats = wp_get_post_categories( $post_id );
    if ( empty( $current_cats ) ) return;
    $translated_cats = dba_translate_category_ids( $current_cats, $target_lang );
    if ( $translated_cats !== $current_cats ) {
        $fixing[ $post_id ] = true;
        wp_set_post_categories( $post_id, $translated_cats );
        unset( $fixing[ $post_id ] );
        error_log( "DBA CAT FIX: Post #{$post_id} cats [" . implode(',', $current_cats) . "] → [{$target_lang}] [" . implode(',', $translated_cats) . "]" );
    }
}
add_action( 'wp_insert_post', 'dba_fix_geobot_post_category_on_save', 50, 3 );

function dba_fix_geobot_category_on_late_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
    $target_lang = '';

    // Check GeoBot countries
    if ( class_exists( 'DBA_GeoBot_Registry' ) ) {
        foreach ( DBA_GeoBot_Registry::get_countries() as $key => $config ) {
            if ( ! empty( $config['meta_prefix'] ) && $meta_key === $config['meta_prefix'] . 'auto_created' ) {
                $target_lang = $config['wpml_code'] ?? '';
                break;
            }
        }
    }
    // Check LATAM countries
    if ( empty( $target_lang ) && class_exists( 'DBA_LATAM_Registry' ) ) {
        foreach ( DBA_LATAM_Registry::get_countries() as $key => $config ) {
            if ( ! empty( $config['meta_prefix'] ) && $meta_key === $config['meta_prefix'] . 'auto_created' ) {
                $target_lang = $config['wpml_code'] ?? '';
                break;
            }
        }
    }
    if ( empty( $target_lang ) || $target_lang === 'en' ) return;

    $current_cats = wp_get_post_categories( $post_id );
    if ( empty( $current_cats ) ) return;
    $translated_cats = dba_translate_category_ids( $current_cats, $target_lang );
    if ( $translated_cats !== $current_cats ) {
        wp_set_post_categories( $post_id, $translated_cats );
        error_log( "DBA CAT FIX (late meta): Post #{$post_id} → [{$target_lang}] [" . implode(',', $translated_cats) . "]" );
    }
}
add_action( 'added_post_meta', 'dba_fix_geobot_category_on_late_meta', 50, 4 );

/* ═══════════════════════════════════════════════════════════════════════════
 *  ONE-TIME MIGRATION: Fix categories on ALL existing GeoBot/LATAM posts
 *  Runs ONCE via admin_init when WPML is fully loaded.
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_v214_fix_existing_geobot_categories() {
    if ( get_option( 'dba_v214_category_fix_done' ) ) return;
    if ( ! is_admin() ) return;
    if ( ! has_filter( 'wpml_object_id' ) && ! function_exists( 'icl_object_id' ) ) return;

    $all_countries = array();
    if ( class_exists( 'DBA_GeoBot_Registry' ) ) {
        $all_countries = array_merge( $all_countries, DBA_GeoBot_Registry::get_countries() );
    }
    if ( class_exists( 'DBA_LATAM_Registry' ) ) {
        $all_countries = array_merge( $all_countries, DBA_LATAM_Registry::get_countries() );
    }
    if ( empty( $all_countries ) ) return;

    $fixed = 0;
    foreach ( $all_countries as $key => $config ) {
        if ( empty( $config['meta_prefix'] ) || empty( $config['wpml_code'] ) ) continue;
        if ( $config['wpml_code'] === 'en' ) continue;
        $target_lang = $config['wpml_code'];

        $posts = get_posts( array(
            'post_type'        => 'post',
            'post_status'      => array( 'publish', 'draft', 'pending', 'private' ),
            'posts_per_page'   => -1,
            'fields'           => 'ids',
            'meta_query'       => array( array( 'key' => $config['meta_prefix'] . 'auto_created', 'compare' => 'EXISTS' ) ),
            'suppress_filters' => true,
        ) );
        if ( empty( $posts ) ) continue;

        foreach ( $posts as $pid ) {
            $cats = wp_get_post_categories( $pid );
            if ( empty( $cats ) ) continue;
            $translated = dba_translate_category_ids( $cats, $target_lang );
            if ( $translated !== $cats && ! empty( $translated ) ) {
                wp_set_post_categories( $pid, $translated );
                $fixed++;
            }
        }
    }

    if ( $fixed > 0 ) {
        wp_cache_flush();
        if ( function_exists( 'rocket_clean_domain' ) ) rocket_clean_domain();
        if ( function_exists( 'et_core_clear_wp_cache' ) ) et_core_clear_wp_cache();
        error_log( "DBA v2.14 CATEGORY MIGRATION: Fixed {$fixed} posts." );
    }
    update_option( 'dba_v214_category_fix_done', true, false );
}
add_action( 'admin_init', 'dba_v214_fix_existing_geobot_categories', 30 );

/**
 * Admin assets
 */
function dba_admin_assets($hook) {
    if (strpos($hook, 'dba-settings') !== false || strpos($hook, 'dba-logs') !== false || strpos($hook, 'damalion') !== false || strpos($hook, 'dba') !== false) {
        wp_enqueue_style('dba-admin', DBA_PLUGIN_URL . 'assets/css/admin.css', array(), DBA_VERSION);
        wp_enqueue_script('dba-admin', DBA_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), DBA_VERSION, true);
        wp_localize_script('dba-admin', 'dba_admin', array(
            'nonce'   => wp_create_nonce('dba_admin_nonce'),
            'ajaxurl' => admin_url('admin-ajax.php'),
        ));
    }
}
add_action('admin_enqueue_scripts', 'dba_admin_assets');

/* ================================================================
 *  PAGE SCANNER — AJAX Handlers + Hooks
 *
 *  1. dba_scan_pages_start   — Initiates full scan, returns total count
 *  2. dba_scan_pages_batch   — Processes next batch, returns progress
 *  3. save_post hook         — Updates single entry in real-time
 *  4. wp_trash_post hook     — Removes entry when trashed
 * ================================================================ */

/**
 * AJAX: Start full page scan
 */
function dba_ajax_scan_start() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( 'Permission denied' );
    }

    $sitemap = DBA_Sitemap::get_instance();
    $total   = $sitemap->scan_start();

    wp_send_json_success( array( 'total' => $total ) );
}
add_action( 'wp_ajax_dba_scan_pages_start', 'dba_ajax_scan_start' );

/**
 * AJAX: Process one batch of the scan
 */
function dba_ajax_scan_batch() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( 'Permission denied' );
    }

    $sitemap = DBA_Sitemap::get_instance();
    $result  = $sitemap->scan_batch();

    wp_send_json_success( $result );
}
add_action( 'wp_ajax_dba_scan_pages_batch', 'dba_ajax_scan_batch' );

/**
 * AJAX: Get current scan metadata (for sidebar display)
 */
function dba_ajax_scan_meta() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( 'Permission denied' );
    }

    $sitemap = DBA_Sitemap::get_instance();
    wp_send_json_success( $sitemap->get_scan_meta() );
}
add_action( 'wp_ajax_dba_scan_meta', 'dba_ajax_scan_meta' );

/**
 * Hook: Update registry when any post/page is published or updated
 */
function dba_on_save_post_update_registry( $post_id, $post, $update ) {
    // Skip revisions
    if ( wp_is_post_revision( $post_id ) ) return;

    $sitemap = DBA_Sitemap::get_instance();

    if ( $post->post_status === 'publish' ) {
        $sitemap->update_single_entry( $post_id );
    } else {
        // Post unpublished/drafted — remove from registry
        $sitemap->remove_single_entry( $post_id );
    }
}
add_action( 'save_post', 'dba_on_save_post_update_registry', 20, 3 );

/**
 * Hook: Remove from registry when trashed
 */
function dba_on_trash_post_remove_registry( $post_id ) {
    $sitemap = DBA_Sitemap::get_instance();
    $sitemap->remove_single_entry( $post_id );
}
add_action( 'wp_trash_post', 'dba_on_trash_post_remove_registry' );

/**
 * Frontend assets
 */
function dba_frontend_assets() {
    if (is_single() && get_post_type() === 'post') {
        $post_id = get_the_ID();
        $is_dba = get_post_meta($post_id, '_dba_auto_created', true);
        if ($is_dba) {
            wp_enqueue_style('dba-frontend', DBA_PLUGIN_URL . 'assets/css/frontend.css', array(), DBA_VERSION);
        }
    }
}
add_action('wp_enqueue_scripts', 'dba_frontend_assets');

/* ═══════════════════════════════════════════════════════════════════════════
 *  SHORTCODE i18n — Translated UI strings for non-English pages.
 *
 *  Detects current WPML language and returns the correct string.
 *  Used by all shortcodes (GeoBot, Blog, PE News, LuxStrat).
 * ═══════════════════════════════════════════════════════════════════════════ */
function dba_get_current_wpml_lang() {
    if ( defined( 'ICL_LANGUAGE_CODE' ) ) return ICL_LANGUAGE_CODE;
    if ( function_exists( 'wpml_get_current_language' ) ) return wpml_get_current_language();
    if ( function_exists( 'icl_get_languages' ) ) {
        global $sitepress;
        return $sitepress ? $sitepress->get_current_language() : 'en';
    }
    return 'en';
}

function dba_i18n_strings( $lang = '' ) {
    if ( empty( $lang ) ) $lang = dba_get_current_wpml_lang();

    $strings = array(
        'en' => array(
            'read_more'       => 'Read more →',
            'no_posts'        => 'No blog posts available yet.',
            'no_geo_posts'    => 'No %s investment posts available yet.',
            'no_luxstrat'     => 'No Luxembourg structuring articles available yet.',
            'no_pe_news'      => 'No PE/Capital Markets news available yet.',
        ),
        'it' => array(
            'read_more'       => 'Leggi di più →',
            'no_posts'        => 'Nessun articolo disponibile al momento.',
            'no_geo_posts'    => 'Nessun articolo sugli investimenti in %s disponibile al momento.',
            'no_luxstrat'     => 'Nessun articolo sulla strutturazione lussemburghese disponibile al momento.',
            'no_pe_news'      => 'Nessuna notizia PE/Mercati dei Capitali disponibile al momento.',
        ),
        'fr' => array(
            'read_more'       => 'Lire la suite →',
            'no_posts'        => 'Aucun article disponible pour le moment.',
            'no_geo_posts'    => 'Aucun article sur l\'investissement en %s disponible pour le moment.',
            'no_luxstrat'     => 'Aucun article sur la structuration luxembourgeoise disponible pour le moment.',
            'no_pe_news'      => 'Aucune actualité PE/Marchés des Capitaux disponible pour le moment.',
        ),
        'es' => array(
            'read_more'       => 'Leer más →',
            'no_posts'        => 'No hay artículos disponibles por el momento.',
            'no_geo_posts'    => 'No hay artículos sobre inversión en %s disponibles por el momento.',
            'no_luxstrat'     => 'No hay artículos sobre estructuración luxemburguesa disponibles por el momento.',
            'no_pe_news'      => 'No hay noticias de PE/Mercados de Capitales disponibles por el momento.',
        ),
        'pt' => array(
            'read_more'       => 'Ler mais →',
            'no_posts'        => 'Nenhum artigo disponível no momento.',
            'no_geo_posts'    => 'Nenhum artigo sobre investimento em %s disponível no momento.',
            'no_luxstrat'     => 'Nenhum artigo sobre estruturação luxemburguesa disponível no momento.',
            'no_pe_news'      => 'Nenhuma notícia de PE/Mercados de Capitais disponível no momento.',
        ),
    );

    return $strings[ $lang ] ?? $strings['en'];
}

/* ── Translated country names for "No X investment posts" messages ── */
function dba_country_name_translated( $country_key, $lang = '' ) {
    if ( empty( $lang ) ) $lang = dba_get_current_wpml_lang();

    $names = array(
        'italy'    => array( 'en' => 'Italy', 'it' => 'Italia', 'fr' => 'Italie', 'es' => 'Italia', 'pt' => 'Itália' ),
        'france'   => array( 'en' => 'France', 'it' => 'Francia', 'fr' => 'France', 'es' => 'Francia', 'pt' => 'França' ),
        'spain'    => array( 'en' => 'Spain', 'it' => 'Spagna', 'fr' => 'Espagne', 'es' => 'España', 'pt' => 'Espanha' ),
        'portugal' => array( 'en' => 'Portugal', 'it' => 'Portogallo', 'fr' => 'Portugal', 'es' => 'Portugal', 'pt' => 'Portugal' ),
    );

    $country = $names[ $country_key ] ?? array();
    return $country[ $lang ] ?? ( $country['en'] ?? ucfirst( $country_key ) );
}

/**
 * Shortcode to display Damalion blog posts
 */
function dba_blog_shortcode($atts) {
    $atts = shortcode_atts(array(
        'limit'    => 10,
        'subject'  => '',
        'language' => '',
    ), $atts);

    $meta_query = array(
        array('key' => '_dba_auto_created', 'compare' => 'EXISTS'),
        // Exclude PE/Capital Markets news posts — they have their own display
        array('key' => '_dba_pe_auto_created', 'compare' => 'NOT EXISTS'),
        // Exclude LuxStrat posts — they have their own display
        array('key' => '_dba_luxstrat_auto_created', 'compare' => 'NOT EXISTS'),
    );

    if (!empty($atts['subject'])) {
        $meta_query[] = array('key' => '_dba_subject', 'value' => sanitize_text_field($atts['subject']));
    }

    $args = array(
        'post_type'      => 'post',
        'posts_per_page' => intval($atts['limit']),
        'post_status'    => 'publish',
        'meta_query'     => $meta_query,
        'orderby'        => 'date',
        'order'          => 'DESC',
    );

    // WPML: filter by language if specified
    if (!empty($atts['language']) && function_exists('icl_get_languages')) {
        global $sitepress;
        if ($sitepress) {
            $sitepress->switch_lang($atts['language']);
        }
    }

    $query = new WP_Query($args);

    $i18n = dba_i18n_strings();

    if (!$query->have_posts()) {
        return '<p style="text-align:center;color:#666;padding:40px;">' . esc_html( $i18n['no_posts'] ) . '</p>';
    }

    ob_start();
    ?>
    <div class="dba-blog-grid" style="display:grid;gap:24px;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));margin:20px 0;">
        <?php while ($query->have_posts()): $query->the_post();
            $theme = get_post_meta(get_the_ID(), '_dba_theme', true);
            $subject = get_post_meta(get_the_ID(), '_dba_subject', true);
        ?>
        <article class="dba-card" style="background:#fff;border:1px solid #e0e0e0;border-radius:10px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08);transition:transform .2s;">
            <?php if (has_post_thumbnail()): ?>
            <div style="height:220px;overflow:hidden;">
                <?php the_post_thumbnail('medium_large', array('style' => 'width:100%;height:220px;object-fit:cover;')); ?>
            </div>
            <?php endif; ?>
            <div style="padding:20px;">
                <?php if ($theme): ?>
                <span style="display:inline-block;background:#1a365d;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:10px;"><?php echo esc_html($theme); ?></span>
                <?php endif; ?>
                <h3 style="margin:0 0 10px;line-height:1.3;">
                    <a href="<?php the_permalink(); ?>" style="text-decoration:none;color:#1a202c;"><?php the_title(); ?></a>
                </h3>
                <p style="color:#666;font-size:14px;line-height:1.5;margin:0 0 15px;"><?php echo wp_trim_words(get_the_excerpt(), 25); ?></p>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                    <span style="color:#999;font-size:12px;"><?php echo get_the_date(); ?></span>
                    <a href="<?php the_permalink(); ?>" style="color:#1a365d;font-weight:600;text-decoration:none;font-size:14px;"><?php echo esc_html( $i18n['read_more'] ); ?></a>
                </div>
            </div>
        </article>
        <?php endwhile; wp_reset_postdata(); ?>
    </div>
    <?php
    return ob_get_clean();
}
add_shortcode('damalion_blog', 'dba_blog_shortcode');

/**
 * Shortcode to display PE/Capital Markets news posts
 */
function dba_pe_news_shortcode($atts) {
    $atts = shortcode_atts(array(
        'limit'    => 10,
        'geo'      => '',
        'category' => '',
    ), $atts);

    $meta_query = array(
        array('key' => '_dba_pe_auto_created', 'compare' => 'EXISTS'),
    );

    if (!empty($atts['geo'])) {
        $meta_query[] = array('key' => '_dba_pe_geography', 'value' => sanitize_text_field($atts['geo']));
    }

    if (!empty($atts['category'])) {
        $meta_query[] = array('key' => '_dba_pe_category', 'value' => sanitize_text_field($atts['category']));
    }

    // Exclude roundup hubs from the grid
    $meta_query[] = array('key' => '_dba_pe_is_roundup', 'compare' => 'NOT EXISTS');

    $args = array(
        'post_type'      => 'post',
        'posts_per_page' => intval($atts['limit']),
        'post_status'    => 'publish',
        'meta_query'     => $meta_query,
        'orderby'        => 'date',
        'order'          => 'DESC',
    );

    $query = new WP_Query($args);

    $i18n = dba_i18n_strings();

    if (!$query->have_posts()) {
        return '<p style="text-align:center;color:#666;padding:40px;">' . esc_html( $i18n['no_pe_news'] ) . '</p>';
    }

    ob_start();
    ?>
    <div class="dba-pe-news-grid" style="display:grid;gap:24px;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));margin:20px 0;">
        <?php while ($query->have_posts()): $query->the_post();
            $geo = get_post_meta(get_the_ID(), '_dba_pe_geography', true);
            $company = get_post_meta(get_the_ID(), '_dba_pe_company', true);
            $category = get_post_meta(get_the_ID(), '_dba_pe_category', true);
        ?>
        <article class="dba-pe-card" style="background:#fff;border:1px solid #e0e0e0;border-radius:10px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08);transition:transform .2s;">
            <div style="padding:20px;">
                <?php if ($category): ?>
                <span style="display:inline-block;background:#0d3b66;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:8px;"><?php echo esc_html($category); ?></span>
                <?php endif; ?>
                <?php if ($geo): ?>
                <span style="display:inline-block;background:#4a7c59;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:8px;margin-left:6px;"><?php echo esc_html($geo); ?></span>
                <?php endif; ?>
                <h3 style="margin:10px 0;line-height:1.3;">
                    <a href="<?php the_permalink(); ?>" style="text-decoration:none;color:#1a202c;"><?php the_title(); ?></a>
                </h3>
                <?php if ($company): ?>
                <p style="color:#555;font-size:13px;margin:0 0 8px;"><strong><?php echo esc_html($company); ?></strong></p>
                <?php endif; ?>
                <p style="color:#666;font-size:14px;line-height:1.5;margin:0 0 15px;"><?php echo wp_trim_words(get_the_excerpt(), 25); ?></p>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                    <span style="color:#999;font-size:12px;"><?php echo get_the_date(); ?></span>
                    <a href="<?php the_permalink(); ?>" style="color:#0d3b66;font-weight:600;text-decoration:none;font-size:14px;"><?php echo esc_html( $i18n['read_more'] ); ?></a>
                </div>
            </div>
        </article>
        <?php endwhile; wp_reset_postdata(); ?>
    </div>
    <?php
    return ob_get_clean();
}
add_shortcode('damalion_pe_news', 'dba_pe_news_shortcode');

/**
 * Shortcode to display GeoBot investment posts by country
 * Usage: [damalion_geo_blog country="france" limit="10"]
 */
function dba_geo_blog_shortcode($atts) {
    $atts = shortcode_atts(array(
        'limit'    => 10,
        'country'  => 'france',
        'commune'  => '',
    ), $atts);

    $country_key = sanitize_key( $atts['country'] );
    $config = DBA_GeoBot_Registry::get_country( $country_key );
    if ( ! $config ) {
        return '<p>Unknown country: ' . esc_html( $country_key ) . '</p>';
    }

    // ── WPML LANGUAGE GATE ──
    // Only display posts when the current site language matches the country's language.
    // Italy posts (wpml_code='it') only show on ?lang=it, France on ?lang=fr, etc.
    if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
        $current_lang = ICL_LANGUAGE_CODE;
    } elseif ( function_exists( 'wpml_get_current_language' ) ) {
        $current_lang = wpml_get_current_language();
    } elseif ( function_exists( 'icl_get_languages' ) ) {
        global $sitepress;
        $current_lang = $sitepress ? $sitepress->get_current_language() : '';
    } else {
        $current_lang = '';
    }

    // If WPML is active and current language does NOT match this country → show nothing
    if ( ! empty( $current_lang ) && $current_lang !== 'all' && $current_lang !== $config['wpml_code'] ) {
        return ''; // Silent — don't render anything on wrong language
    }

    $meta_query = array(
        array( 'key' => $config['meta_prefix'] . 'auto_created', 'compare' => 'EXISTS' ),
    );

    if ( ! empty( $atts['commune'] ) ) {
        $meta_query[] = array( 'key' => $config['meta_prefix'] . 'commune', 'value' => sanitize_text_field( $atts['commune'] ) );
    }

    // WPML: switch language context for query
    $switched_lang = false;
    if ( function_exists( 'icl_get_languages' ) ) {
        global $sitepress;
        if ( $sitepress ) {
            $sitepress->switch_lang( $config['wpml_code'] );
            $switched_lang = true;
        }
    }

    $query = new WP_Query( array(
        'post_type'        => 'post',
        'posts_per_page'   => intval( $atts['limit'] ),
        'post_status'      => 'publish',
        'meta_query'       => $meta_query,
        'orderby'          => 'date',
        'order'            => 'DESC',
        'suppress_filters' => false, // Let WPML filter by language
    ) );

    if ( ! $query->have_posts() ) {
        // Restore language context before returning
        if ( $switched_lang && $sitepress ) {
            $sitepress->switch_lang( $current_lang );
        }
        $i18n = dba_i18n_strings( $current_lang );
        $country_display = dba_country_name_translated( $country_key, $current_lang );
        return '<p style="text-align:center;color:#666;padding:40px;">' . esc_html( sprintf( $i18n['no_geo_posts'], $country_display ) ) . '</p>';
    }

    $i18n = dba_i18n_strings( $current_lang );

    ob_start();
    ?>
    <div class="dba-geo-grid" style="display:grid;gap:24px;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));margin:20px 0;">
        <?php while ( $query->have_posts() ) : $query->the_post();
            $commune = get_post_meta( get_the_ID(), $config['meta_prefix'] . 'commune', true );
            $region  = get_post_meta( get_the_ID(), $config['meta_prefix'] . 'region', true );
        ?>
        <article class="dba-geo-card" style="background:#fff;border:1px solid #e0e0e0;border-radius:10px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08);transition:transform .2s;">
            <?php if ( has_post_thumbnail() ) : ?>
            <div style="height:220px;overflow:hidden;">
                <?php the_post_thumbnail( 'medium_large', array( 'style' => 'width:100%;height:220px;object-fit:cover;' ) ); ?>
            </div>
            <?php endif; ?>
            <div style="padding:20px;">
                <?php if ( $commune ) : ?>
                <span style="display:inline-block;background:#1a365d;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:8px;"><?php echo esc_html( $commune ); ?></span>
                <?php endif; ?>
                <?php if ( $region ) : ?>
                <span style="display:inline-block;background:#4a7c59;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:8px;margin-left:6px;"><?php echo esc_html( $region ); ?></span>
                <?php endif; ?>
                <h3 style="margin:10px 0;line-height:1.3;">
                    <a href="<?php the_permalink(); ?>" style="text-decoration:none;color:#1a202c;"><?php the_title(); ?></a>
                </h3>
                <p style="color:#666;font-size:14px;line-height:1.5;margin:0 0 15px;"><?php echo wp_trim_words( get_the_excerpt(), 25 ); ?></p>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                    <span style="color:#999;font-size:12px;"><?php echo get_the_date(); ?></span>
                    <a href="<?php the_permalink(); ?>" style="color:#1a365d;font-weight:600;text-decoration:none;font-size:14px;"><?php echo esc_html( $i18n['read_more'] ); ?></a>
                </div>
            </div>
        </article>
        <?php endwhile; wp_reset_postdata(); ?>
    </div>
    <?php

    // Restore WPML language context
    if ( $switched_lang && isset( $sitepress ) && $sitepress ) {
        $sitepress->switch_lang( $current_lang );
    }

    return ob_get_clean();
}
add_shortcode('damalion_geo_blog', 'dba_geo_blog_shortcode');

/**
 * Shortcode to display LuxStrat Luxembourg structuring posts
 * Usage: [damalion_luxstrat limit="10" theme="raif"]
 */
function dba_luxstrat_shortcode($atts) {
    $atts = shortcode_atts(array(
        'limit' => 10,
        'theme' => '',
    ), $atts);

    $meta_query = array(
        array('key' => '_dba_luxstrat_auto_created', 'compare' => 'EXISTS'),
    );

    if (!empty($atts['theme'])) {
        $meta_query[] = array('key' => '_dba_luxstrat_theme', 'value' => sanitize_key($atts['theme']));
    }

    $args = array(
        'post_type'      => 'post',
        'posts_per_page' => intval($atts['limit']),
        'post_status'    => 'publish',
        'meta_query'     => $meta_query,
        'orderby'        => 'date',
        'order'          => 'DESC',
    );

    $query = new WP_Query($args);

    $i18n = dba_i18n_strings();

    if (!$query->have_posts()) {
        return '<p style="text-align:center;color:#666;padding:40px;">' . esc_html( $i18n['no_luxstrat'] ) . '</p>';
    }

    ob_start();
    ?>
    <div class="dba-luxstrat-grid" style="display:grid;gap:24px;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));margin:20px 0;">
        <?php while ($query->have_posts()): $query->the_post();
            $theme_slug  = get_post_meta(get_the_ID(), '_dba_luxstrat_theme', true);
            $theme_label = get_post_meta(get_the_ID(), '_dba_luxstrat_theme_label', true);
            $theme_config = class_exists('DBA_LuxStrat_Registry') ? DBA_LuxStrat_Registry::get_theme($theme_slug) : null;
            $color = $theme_config ? $theme_config['color'] : '#1a365d';
        ?>
        <article class="dba-luxstrat-card" style="background:#fff;border:1px solid #e0e0e0;border-radius:10px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08);transition:transform .2s;">
            <?php if (has_post_thumbnail()): ?>
            <div style="height:220px;overflow:hidden;">
                <?php the_post_thumbnail('medium_large', array('style' => 'width:100%;height:220px;object-fit:cover;')); ?>
            </div>
            <?php endif; ?>
            <div style="padding:20px;">
                <?php if ($theme_label): ?>
                <span style="display:inline-block;background:<?php echo esc_attr($color); ?>;color:#fff;padding:3px 10px;border-radius:4px;font-size:11px;text-transform:uppercase;margin-bottom:10px;"><?php echo esc_html($theme_label); ?></span>
                <?php endif; ?>
                <h3 style="margin:0 0 10px;line-height:1.3;">
                    <a href="<?php the_permalink(); ?>" style="text-decoration:none;color:#1a202c;"><?php the_title(); ?></a>
                </h3>
                <p style="color:#666;font-size:14px;line-height:1.5;margin:0 0 15px;"><?php echo wp_trim_words(get_the_excerpt(), 25); ?></p>
                <div style="display:flex;justify-content:space-between;align-items:center;">
                    <span style="color:#999;font-size:12px;"><?php echo get_the_date(); ?></span>
                    <a href="<?php the_permalink(); ?>" style="color:<?php echo esc_attr($color); ?>;font-weight:600;text-decoration:none;font-size:14px;"><?php echo esc_html( $i18n['read_more'] ); ?></a>
                </div>
            </div>
        </article>
        <?php endwhile; wp_reset_postdata(); ?>
    </div>
    <?php
    return ob_get_clean();
}
add_shortcode('damalion_luxstrat', 'dba_luxstrat_shortcode');

/**
 * Settings link on plugins page
 */
function dba_plugin_action_links($links) {
    $settings_link = '<a href="' . admin_url('admin.php?page=dba-settings') . '">Settings</a>';
    array_unshift($links, $settings_link);
    return $links;
}
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'dba_plugin_action_links');

/* ═══════════════════════════════════════════════════════════════════════════
 *  ITALIAN HOMEPAGE — DYNAMIC BLOG OVERRIDE (v2.14.3)
 *
 *  Problem: The Italian homepage (?lang=it) uses 3 Divi Blog modules
 *  (et_pb_blog_0, et_pb_blog_1, et_pb_blog_2) each hardcoded to show
 *  1 English post (IDs 502711, 502628, 502588). These cannot be changed
 *  via Visual Builder.
 *
 *  Solution: Output buffer intercepts the rendered HTML, finds the 3
 *  <article> blocks inside those modules, and replaces them with
 *  the 1-3 most recent posts in the current language — EXCLUDING
 *  blacklisted slugs that should never appear on the homepage.
 *
 *  Also works for FR, ES, PT homepages — any language where the Divi
 *  Blog modules are hardcoded to English posts.
 *
 *  Safe: Only activates on the front page (?lang=XX). Does nothing
 *  on admin, single posts, archives, or the English homepage.
 * ═══════════════════════════════════════════════════════════════════════════ */

/**
 * ── HOMEPAGE EXCLUSION LIST ──
 * Slugs listed here will NEVER appear in the homepage "Featured news" section.
 * Add any slug to this array to permanently blacklist it from all homepages.
 * Works across ALL languages (it, fr, es, pt, etc.).
 *
 * You can also manage this from wp-admin via the option 'dba_homepage_excluded_slugs'.
 */
function dba_get_homepage_excluded_slugs() {
    // ── HARDCODED EXCLUSIONS (cannot be removed from admin) ──
    $hardcoded = array(
        'perche-la-puntata-defi-e-cosi-importante-per-il-business-delle-criptovalute',
    );

    // ── ADMIN-CONFIGURABLE EXCLUSIONS (stored in wp_options) ──
    $db_slugs = get_option( 'dba_homepage_excluded_slugs', array() );
    if ( ! is_array( $db_slugs ) ) {
        $db_slugs = array_filter( array_map( 'trim', explode( "\n", $db_slugs ) ) );
    }

    return array_unique( array_merge( $hardcoded, $db_slugs ) );
}

/**
 * Convert excluded slugs to post IDs for use in WP_Query post__not_in.
 * Caches the result for 1 hour to avoid repeated DB lookups.
 */
function dba_get_homepage_excluded_ids() {
    $cached = get_transient( 'dba_homepage_excluded_ids' );
    if ( is_array( $cached ) && ! empty( $cached ) ) {
        return $cached;
    }

    $slugs = dba_get_homepage_excluded_slugs();
    if ( empty( $slugs ) ) return array();

    $ids = array();
    foreach ( $slugs as $slug ) {
        $slug = sanitize_title( trim( $slug ) );
        if ( empty( $slug ) ) continue;

        // Look up by slug across all languages (suppress WPML filters)
        $found = get_posts( array(
            'name'             => $slug,
            'post_type'        => 'post',
            'post_status'      => array( 'publish', 'draft', 'pending', 'private' ),
            'posts_per_page'   => 5,
            'fields'           => 'ids',
            'suppress_filters' => true, // Bypass WPML to catch all translations
        ) );
        if ( ! empty( $found ) ) {
            $ids = array_merge( $ids, $found );
        }
    }

    // Also exclude the 3 hardcoded English posts from Divi
    $ids = array_merge( $ids, array( 502711, 502628, 502588 ) );
    $ids = array_unique( array_map( 'intval', array_filter( $ids ) ) );

    set_transient( 'dba_homepage_excluded_ids', $ids, HOUR_IN_SECONDS );

    return $ids;
}

/**
 * Start output buffering on non-English homepages so we can replace
 * the hardcoded Divi Blog module posts with language-appropriate ones.
 *
 * IMPORTANT: All WP_Query and WPML calls happen HERE, BEFORE ob_start().
 * Running queries inside an output-buffer callback is fragile and can
 * blank the entire page if anything throws a fatal error.
 */
function dba_homepage_blog_override_start() {
    // Only on frontend
    if ( is_admin() ) return;

    // Detect current WPML language
    $lang = '';
    if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
        $lang = ICL_LANGUAGE_CODE;
    } elseif ( function_exists( 'wpml_get_current_language' ) ) {
        $lang = wpml_get_current_language();
    } elseif ( isset( $_GET['lang'] ) ) {
        $lang = sanitize_key( $_GET['lang'] );
    }

    // Only override on non-English homepages
    if ( empty( $lang ) || $lang === 'en' || $lang === 'all' ) return;

    // Only on the front page / homepage
    if ( ! is_front_page() ) return;

    // ── PRE-FETCH all data now, while it's safe to run DB queries ──
    try {
        $replacement_posts = dba_get_latest_posts_for_lang( $lang, 3 );
        $excluded_slugs    = dba_get_homepage_excluded_slugs();
    } catch ( \Throwable $e ) {
        error_log( 'DBA Homepage Override: Failed to pre-fetch data for lang=' . $lang . ': ' . $e->getMessage() );
        return; // Don't start the buffer at all — page renders normally
    }

    // Store everything the buffer callback needs (NO queries inside the callback)
    $GLOBALS['_dba_homepage_override_lang']  = $lang;
    $GLOBALS['_dba_homepage_override_posts'] = $replacement_posts;
    $GLOBALS['_dba_homepage_override_slugs'] = $excluded_slugs;

    ob_start( 'dba_homepage_blog_override_buffer' );
}
add_action( 'template_redirect', 'dba_homepage_blog_override_start', 1 );

/**
 * Output buffer callback: find the 3 hardcoded Divi Blog <article> blocks
 * and replace them with up to 3 latest posts in the current language.
 *
 * SAFETY: This function MUST always return valid HTML. If anything fails,
 * it returns the original $html untouched. No WP_Query or WPML calls
 * happen here — all data was pre-fetched in dba_homepage_blog_override_start().
 */
function dba_homepage_blog_override_buffer( $html ) {
    // Absolute safety net: if ANYTHING fails, return original HTML
    if ( empty( $html ) ) return $html;

    try {
        $lang = $GLOBALS['_dba_homepage_override_lang'] ?? '';
        if ( empty( $lang ) ) return $html;

        // Use pre-fetched data (NO database queries in this callback!)
        $replacement_posts = $GLOBALS['_dba_homepage_override_posts'] ?? array();
        $excluded_slugs    = $GLOBALS['_dba_homepage_override_slugs'] ?? array();

        if ( empty( $replacement_posts ) ) {
            return $html; // Nothing to replace — pass through
        }

        // ── Hardcoded English post IDs to neutralize ──
        $hardcoded_ids = array( 502711, 502628, 502588 );

        // ── Check if the HTML actually contains these posts ──
        $found_any = false;
        foreach ( $hardcoded_ids as $hid ) {
            if ( strpos( $html, 'id="post-' . $hid . '"' ) !== false ) {
                $found_any = true;
                break;
            }
        }

        // ── Also check for excluded slugs that snuck through ──
        foreach ( $excluded_slugs as $slug ) {
            if ( strpos( $html, '/' . $slug . '/' ) !== false ) {
                $found_any = true;
                break;
            }
        }

        if ( ! $found_any ) return $html; // Clean page — pass through

        // ── Temporarily increase PCRE limits for large HTML ──
        $old_backtrack = @ini_set( 'pcre.backtrack_limit', '2000000' );
        $old_recursion = @ini_set( 'pcre.recursion_limit', '500000' );

        // ── STEP 1: Replace hardcoded English articles ──
        for ( $i = 0; $i < 3; $i++ ) {
            $old_id = $hardcoded_ids[ $i ];

            if ( isset( $replacement_posts[ $i ] ) ) {
                $new_article = dba_build_divi_blog_article( $replacement_posts[ $i ], $i );
                $pattern = '/<article\s+id="post-' . $old_id . '"[^>]*>.*?<\/article>/s';
                $result = preg_replace( $pattern, $new_article, $html, 1 );
                if ( $result !== null ) {
                    $html = $result;
                } else {
                    error_log( 'DBA Homepage Override: preg_replace returned NULL for post #' . $old_id . ' — skipping (page preserved).' );
                }
            }
        }

        // ── STEP 2: Remove any excluded slugs still in the HTML ──
        foreach ( $excluded_slugs as $slug ) {
            if ( strpos( $html, $slug ) === false ) {
                continue;
            }

            $safe_pattern = '/<article\s[^>]*>.*?<\/article>/s';
            $match_result = preg_match_all( $safe_pattern, $html, $matches );
            if ( $match_result && ! empty( $matches[0] ) ) {
                foreach ( $matches[0] as $article_block ) {
                    if ( strpos( $article_block, $slug ) !== false ) {
                        $html = str_replace( $article_block, '', $html );
                    }
                }
            }
        }

        // ── Restore PCRE limits ──
        if ( $old_backtrack !== false ) @ini_set( 'pcre.backtrack_limit', $old_backtrack );
        if ( $old_recursion !== false ) @ini_set( 'pcre.recursion_limit', $old_recursion );

        return $html;

    } catch ( \Throwable $e ) {
        // LAST RESORT: if anything at all went wrong, return the original page
        error_log( 'DBA Homepage Override FATAL: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() . ' — returning original HTML.' );
        return $html;
    }
}

/**
 * Query the N most recent published posts for a given WPML language.
 * Excludes all blacklisted homepage slugs and the hardcoded English posts.
 *
 * @param string $lang  WPML language code (e.g. 'it', 'fr', 'es')
 * @param int    $count Number of posts to retrieve
 * @return array Array of WP_Post objects (0-indexed)
 */
function dba_get_latest_posts_for_lang( $lang, $count = 3 ) {
    $posts = array();

    // ── Get exclusion IDs ──
    $exclude_ids = dba_get_homepage_excluded_ids();

    // ── WPML switch_lang ──
    $switched = false;
    $original_lang = '';
    if ( function_exists( 'icl_get_languages' ) ) {
        global $sitepress;
        if ( $sitepress ) {
            $original_lang = $sitepress->get_current_language();
            $sitepress->switch_lang( $lang );
            $switched = true;
        }
    }

    $args = array(
        'post_type'        => 'post',
        'post_status'      => 'publish',
        'posts_per_page'   => $count,
        'orderby'          => 'date',
        'order'            => 'DESC',
        'suppress_filters' => false, // Let WPML filter by language
    );

    // Apply exclusions
    if ( ! empty( $exclude_ids ) ) {
        $args['post__not_in'] = $exclude_ids;
    }

    $query = new WP_Query( $args );
    if ( $query->have_posts() ) {
        $posts = $query->posts;
    }

    // Restore language
    if ( $switched && isset( $sitepress ) && $sitepress ) {
        $sitepress->switch_lang( $original_lang );
    }

    wp_reset_postdata();

    // ── Log what we found (debug) ──
    $slugs_found = array_map( function( $p ) { return $p->post_name; }, $posts );
    error_log( 'DBA Homepage Override [' . $lang . ']: Found ' . count( $posts ) . ' posts — ' . implode( ', ', $slugs_found ) . ' | Excluded IDs: ' . implode( ',', $exclude_ids ) );

    return $posts;
}

/**
 * Build a Divi Blog module <article> block matching the exact HTML structure
 * that Divi's et_pb_blog module renders. Preserves CSS classes, hover effects,
 * overlays, and grid layout.
 *
 * @param WP_Post $post       The post object
 * @param int     $slot_index Which blog module slot (0, 1, or 2)
 * @return string HTML for the <article> block
 */
function dba_build_divi_blog_article( $post, $slot_index = 0 ) {
    $post_id    = $post->ID;
    $permalink  = get_permalink( $post_id );
    $title      = get_the_title( $post_id );
    $excerpt    = wp_trim_words( get_the_excerpt( $post_id ), 20, '...' );

    // Category — use the FIRST category assigned to this post
    $categories = get_the_category( $post_id );
    $cat_html   = '';
    $cat_slug   = 'uncategorized';
    if ( ! empty( $categories ) ) {
        $cat      = $categories[0];
        $cat_link = get_category_link( $cat->term_id );
        $cat_name = esc_html( $cat->name );
        $cat_slug = esc_attr( $cat->slug );
        $cat_html = '<p class="post-meta"><a href="' . esc_url( $cat_link ) . '" rel="tag">' . $cat_name . '</a></p> ';
    }

    // Featured image
    $image_html = '';
    if ( has_post_thumbnail( $post_id ) ) {
        $thumb_id  = get_post_thumbnail_id( $post_id );
        $thumb_url = get_the_post_thumbnail_url( $post_id, 'medium' );
        $thumb_alt = esc_attr( get_post_meta( $thumb_id, '_wp_attachment_image_alt', true ) );
        if ( empty( $thumb_alt ) ) $thumb_alt = esc_attr( $title );

        $thumb_srcset = wp_get_attachment_image_srcset( $thumb_id, 'medium' );
        $thumb_sizes  = '(max-width:479px) 479px, 100vw';

        $image_html = '<div class="et_pb_image_container">'
            . '<a href="' . esc_url( $permalink ) . '" class="entry-featured-image-url">'
            . '<img decoding="async" loading="lazy" src="' . esc_url( $thumb_url ) . '"'
            . ' alt="' . $thumb_alt . '"'
            . ( $thumb_srcset ? ' srcset="' . esc_attr( $thumb_srcset ) . '"' : '' )
            . ' sizes="' . esc_attr( $thumb_sizes ) . '"'
            . ' width="400" height="250" class="" />'
            . '<span class="et_overlay et_pb_inline_icon" data-icon="$"></span>'
            . '</a></div>';
    }

    // Build article — exact Divi Blog module structure
    $article = '<article id="post-' . $post_id . '" class="et_pb_post clearfix et_pb_has_overlay et_pb_blog_item_' . $slot_index . '_0 post-' . $post_id . ' post type-post status-publish format-standard' . ( has_post_thumbnail( $post_id ) ? ' has-post-thumbnail' : '' ) . ' hentry category-' . $cat_slug . '">' . "\n"
        . $image_html . "\n"
        . '<h2 class="entry-title">' . "\n"
        . '<a href="' . esc_url( $permalink ) . '">' . esc_html( $title ) . '</a>' . "\n"
        . '</h2>' . "\n"
        . $cat_html
        . '<div class="post-content"><div class="post-content-inner"><p>' . esc_html( $excerpt ) . '</p> ' . "\n"
        . '</div><a href="' . esc_url( $permalink ) . '" class="more-link">read more</a></div>'
        . '</article>';

    return $article;
}

/**
 * Safety: close the output buffer on shutdown.
 */
function dba_homepage_blog_override_end() {
    if ( isset( $GLOBALS['_dba_homepage_override_lang'] ) ) {
        if ( ob_get_level() > 0 ) {
            ob_end_flush();
        }
        unset( $GLOBALS['_dba_homepage_override_lang'] );
        unset( $GLOBALS['_dba_homepage_override_posts'] );
        unset( $GLOBALS['_dba_homepage_override_slugs'] );
    }
}
add_action( 'shutdown', 'dba_homepage_blog_override_end', 999 );

/**
 * ── ADMIN: Manage homepage exclusions ──
 * Adds a small admin panel section to add/remove slugs from the homepage blacklist.
 * Accessible via AJAX from any DBA admin page.
 */
function dba_ajax_add_homepage_exclusion() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) wp_send_json_error( 'Permission denied' );

    $slug = sanitize_title( trim( $_POST['slug'] ?? '' ) );
    if ( empty( $slug ) ) wp_send_json_error( 'Empty slug' );

    $slugs = get_option( 'dba_homepage_excluded_slugs', array() );
    if ( ! is_array( $slugs ) ) $slugs = array();
    if ( ! in_array( $slug, $slugs, true ) ) {
        $slugs[] = $slug;
        update_option( 'dba_homepage_excluded_slugs', $slugs, false );
    }

    // Clear cached exclusion IDs
    delete_transient( 'dba_homepage_excluded_ids' );

    // Flush page caches
    if ( function_exists( 'rocket_clean_domain' ) ) rocket_clean_domain();
    if ( function_exists( 'et_core_clear_wp_cache' ) ) et_core_clear_wp_cache();
    wp_cache_flush();

    wp_send_json_success( array( 'slug' => $slug, 'all_slugs' => $slugs ) );
}
add_action( 'wp_ajax_dba_add_homepage_exclusion', 'dba_ajax_add_homepage_exclusion' );

function dba_ajax_remove_homepage_exclusion() {
    check_ajax_referer( 'dba_admin_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) wp_send_json_error( 'Permission denied' );

    $slug = sanitize_title( trim( $_POST['slug'] ?? '' ) );
    $slugs = get_option( 'dba_homepage_excluded_slugs', array() );
    if ( ! is_array( $slugs ) ) $slugs = array();
    $slugs = array_values( array_diff( $slugs, array( $slug ) ) );
    update_option( 'dba_homepage_excluded_slugs', $slugs, false );
    delete_transient( 'dba_homepage_excluded_ids' );

    if ( function_exists( 'rocket_clean_domain' ) ) rocket_clean_domain();
    if ( function_exists( 'et_core_clear_wp_cache' ) ) et_core_clear_wp_cache();
    wp_cache_flush();

    wp_send_json_success( array( 'slug' => $slug, 'all_slugs' => $slugs ) );
}
add_action( 'wp_ajax_dba_remove_homepage_exclusion', 'dba_ajax_remove_homepage_exclusion' );

/**
 * ═══════════════════════════════════════════════════════════════════════
 *  CONFLICT CENTER — Admin Submenu Page (v2.15.0)
 * ═══════════════════════════════════════════════════════════════════════
 */
function dba_conflict_center_admin_menu() {
    add_submenu_page(
        'dba-settings',
        'Conflict Center',
        '🚦 Conflict Center',
        'manage_options',
        'dba-conflict-center',
        'dba_conflict_center_render_page'
    );
}
add_action( 'admin_menu', 'dba_conflict_center_admin_menu', 13 );

/** AJAX: Force-release lock */
function dba_conflict_center_force_release() {
    check_ajax_referer( 'dba_conflict_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) wp_send_json_error( 'Permission denied' );
    if ( ! class_exists( 'DBA_Conflict_Center' ) ) wp_send_json_error( 'Conflict Center not loaded' );
    $old = DBA_Conflict_Center::force_release();
    wp_send_json_success( array( 'released' => $old ? ( $old['label'] ?? $old['module'] ) : 'none' ) );
}
add_action( 'wp_ajax_dba_conflict_force_release', 'dba_conflict_center_force_release' );

/** AJAX: Clear history */
function dba_conflict_center_clear_history() {
    check_ajax_referer( 'dba_conflict_nonce', 'nonce' );
    if ( ! current_user_can( 'manage_options' ) ) wp_send_json_error( 'Permission denied' );
    if ( ! class_exists( 'DBA_Conflict_Center' ) ) wp_send_json_error( 'Conflict Center not loaded' );
    DBA_Conflict_Center::clear_history();
    wp_send_json_success();
}
add_action( 'wp_ajax_dba_conflict_clear_history', 'dba_conflict_center_clear_history' );

/** Render the Conflict Center page */
function dba_conflict_center_render_page() {
    if ( ! class_exists( 'DBA_Conflict_Center' ) ) {
        echo '<div class="wrap"><h1>🚦 Conflict Center</h1><div class="notice notice-error"><p>class-conflict-center.php not loaded. Upload it to <code>includes/</code> folder.</p></div></div>';
        return;
    }

    $data       = DBA_Conflict_Center::get_dashboard_data();
    $status     = $data['status'];
    $collisions = $data['collisions'];
    $history    = $data['history'];
    $map        = $data['timing_map'];
    $nonce      = wp_create_nonce( 'dba_conflict_nonce' );

    ?>
    <div class="wrap">
    <h1>🚦 Conflict Center <small style="font-size:13px;color:#666;">v2.15.0</small></h1>
    <p>Prevents simultaneous OpenAI API calls across all modules. Only one module generates at a time.</p>

    <!-- ══ CURRENT STATUS ══ -->
    <div class="postbox" style="padding:15px;margin-bottom:20px;">
        <h2 style="margin-top:0;">📡 Live Status</h2>
        <?php if ( $status['locked'] ) : ?>
            <div style="background:#fef0f0;border-left:4px solid #dc3232;padding:12px;margin:10px 0;">
                <strong>🔴 LOCKED</strong> — <strong><?php echo esc_html( $status['label'] ?? 'Unknown' ); ?></strong>
                is running (<?php echo esc_html( $status['duration'] ); ?>s)
                &nbsp;
                <button class="button" id="dba-force-release" style="color:#dc3232;border-color:#dc3232;">
                    🔓 Force Release
                </button>
            </div>
        <?php else : ?>
            <div style="background:#f0fef0;border-left:4px solid #46b450;padding:12px;margin:10px 0;">
                <strong>✅ All clear</strong> — No module is currently generating. Lock is free.
            </div>
        <?php endif; ?>
    </div>

    <!-- ══ COLLISION WARNINGS ══ -->
    <?php if ( ! empty( $collisions ) ) : ?>
    <div class="postbox" style="padding:15px;margin-bottom:20px;">
        <h2 style="margin-top:0;">⚠️ Timing Collisions Detected</h2>
        <p>These modules are scheduled too close together. The Conflict Center <strong>will handle this automatically</strong> (blocked module retries +10 min), but you may want to adjust the schedule.</p>
        <table class="widefat striped">
            <thead><tr><th>Day</th><th>Severity</th><th>Issue</th></tr></thead>
            <tbody>
            <?php foreach ( $collisions as $w ) :
                $color = ( $w['severity'] ?? '' ) === 'critical' ? '#dc3232' : ( ( $w['severity'] ?? '' ) === 'warning' ? '#ffb900' : '#0073aa' );
            ?>
                <tr>
                    <td><strong><?php echo esc_html( $w['day'] ?? '' ); ?></strong></td>
                    <td style="color:<?php echo $color; ?>;"><strong><?php echo esc_html( strtoupper( $w['severity'] ?? '' ) ); ?></strong></td>
                    <td><?php echo esc_html( $w['message'] ?? '' ); ?></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
    </div>
    <?php endif; ?>

    <!-- ══ TIMING MAP ══ -->
    <div class="postbox" style="padding:15px;margin-bottom:20px;">
        <h2 style="margin-top:0;">📅 Module Timing Map (UTC)</h2>
        <table class="widefat striped">
            <thead>
                <tr>
                    <th>Module</th>
                    <th>UTC Time</th>
                    <th>Days</th>
                    <th>Posts/Run</th>
                    <th>API Calls</th>
                    <th>Status</th>
                    <th>Protected?</th>
                </tr>
            </thead>
            <tbody>
            <?php foreach ( $map as $id => $info ) :
                $status_icon = $info['enabled'] ? '✅ Active' : '⏸ Disabled';
                $lock_icon   = $info['has_lock'] ? '🔒 Yes' : '❌ No';
                $row_style   = $info['enabled'] ? '' : 'opacity:0.5;';
            ?>
                <tr style="<?php echo $row_style; ?>">
                    <td><strong><?php echo esc_html( $info['label'] ); ?></strong></td>
                    <td><code><?php echo esc_html( $info['cron_utc'] ); ?></code></td>
                    <td><?php echo esc_html( implode( ', ', array_map( function($d) { return substr(ucfirst($d),0,3); }, $info['days'] ) ) ); ?></td>
                    <td style="text-align:center;"><?php echo esc_html( $info['posts'] ); ?></td>
                    <td style="text-align:center;"><?php echo esc_html( $info['api_calls'] ); ?></td>
                    <td><?php echo $status_icon; ?></td>
                    <td><?php echo $lock_icon; ?></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <p style="color:#666;margin-top:10px;">
            <strong>Protected?</strong> = Module checks the global lock before calling OpenAI.
            🔒 Yes = safe. ❌ No = runs independently (Live Sports runs every 5 min with 1 small call — low risk).
        </p>
    </div>

    <!-- ══ LOCK HISTORY ══ -->
    <div class="postbox" style="padding:15px;margin-bottom:20px;">
        <h2 style="margin-top:0;">
            📜 Recent Lock Events
            <button class="button button-small" id="dba-clear-history" style="margin-left:10px;">Clear History</button>
        </h2>
        <?php if ( empty( $history ) ) : ?>
            <p style="color:#666;">No lock events yet. Events will appear here after the next scheduled generation.</p>
        <?php else : ?>
            <table class="widefat striped">
                <thead><tr><th>Time (UTC)</th><th>Module</th><th>Event</th><th>Detail</th></tr></thead>
                <tbody>
                <?php foreach ( array_slice( $history, 0, 25 ) as $h ) :
                    $event = $h['event'] ?? '';
                    $color = '#333';
                    if ( $event === 'blocked' ) $color = '#dc3232';
                    elseif ( $event === 'acquired' ) $color = '#46b450';
                    elseif ( $event === 'released' ) $color = '#0073aa';
                    elseif ( $event === 'stale_cleared' ) $color = '#ffb900';
                ?>
                    <tr>
                        <td><code><?php echo esc_html( $h['time'] ?? '' ); ?></code></td>
                        <td><?php echo esc_html( $h['label'] ?? $h['module'] ?? '' ); ?></td>
                        <td style="color:<?php echo $color; ?>;font-weight:bold;"><?php echo esc_html( strtoupper( $event ) ); ?></td>
                        <td><?php echo esc_html( $h['detail'] ?? '' ); ?></td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        <?php endif; ?>
    </div>

    <!-- ══ HOW IT WORKS ══ -->
    <div class="postbox" style="padding:15px;margin-bottom:20px;">
        <h2 style="margin-top:0;">ℹ️ How It Works</h2>
        <ol style="margin-left:20px;">
            <li><strong>Module A</strong> starts → acquires the global lock</li>
            <li><strong>Module B</strong> tries to start → sees lock is taken → <strong>automatically reschedules itself +10 minutes</strong></li>
            <li><strong>Module A</strong> finishes → releases the lock</li>
            <li><strong>Module B</strong> runs 10 minutes later → lock is free → proceeds normally</li>
        </ol>
        <p>If a module crashes, the lock auto-expires after <strong>15 minutes</strong>. Nothing gets stuck.</p>
    </div>

    </div><!-- .wrap -->

    <script>
    jQuery(function($){
        $('#dba-force-release').on('click', function(){
            if (!confirm('Force-release the lock? Only do this if you are SURE no generation is running.')) return;
            var btn = $(this);
            btn.prop('disabled', true).text('Releasing...');
            $.post(ajaxurl, { action: 'dba_conflict_force_release', nonce: '<?php echo $nonce; ?>' }, function(r){
                if (r.success) {
                    alert('Lock released: ' + r.data.released);
                    location.reload();
                } else {
                    alert('Error: ' + (r.data || 'Unknown'));
                    btn.prop('disabled', false).text('🔓 Force Release');
                }
            });
        });
        $('#dba-clear-history').on('click', function(){
            if (!confirm('Clear all lock event history?')) return;
            $.post(ajaxurl, { action: 'dba_conflict_clear_history', nonce: '<?php echo $nonce; ?>' }, function(r){
                if (r.success) location.reload();
            });
        });
    });
    </script>
    <?php
}

/**
 * Security: index.php files
 */
// Silence is golden
