<?php
/**
 * DBA Comfisc Anti-Orphan Protection
 *
 * Ensures every Comfisc article is linked FROM the pillar page
 * and has cross-links to/from related theme articles.
 *
 * Mirrors DBA_GeoBot_Anti_Orphan architecture, adapted for
 * theme-based (not city-based) accounting/tax articles.
 *
 * Features:
 *   - Auto-append new posts to the pillar page topic list
 *   - Inject "Related Articles" section into existing posts
 *   - Inject backlinks FROM related posts TO the new post
 *   - Schedule verification cron to catch missed links
 *   - Weekly audit to find and fix orphaned posts
 *   - Language-scoped: each language managed independently
 *
 * ═══════════════════════════════════════════════════════════════════════
 *  ZERO CONFLICT: Uses _dba_comfisc_{lang}_ meta prefix.
 *  Completely separate from GeoBot, LuxStrat, Startup Hub Anti-Orphan.
 * ═══════════════════════════════════════════════════════════════════════
 *
 * @since 2.19.0
 * @package DamalionBlogAI
 * @subpackage Comfisc
 */

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

class DBA_Comfisc_Anti_Orphan {

    private static $instance = null;

    /** Marker comment used in pillar pages to locate the topic list. */
    const PILLAR_LIST_MARKER = '<!-- COMFISC_TOPIC_LIST -->';

    /** Cron hook for single-post verification. */
    const VERIFY_HOOK = 'dba_comfisc_verify_link';

    /** Cron hook for weekly audit. */
    const AUDIT_HOOK = 'dba_comfisc_weekly_orphan_audit';

    /** Max related articles to inject into a post. */
    const MAX_RELATED = 4;

    /** Max existing posts to receive a backlink to the new post. */
    const MAX_BACKLINKS = 3;

    /** CSS class for related block (avoids duplication). */
    const RELATED_CLASS = 'comfisc-related-articles';

    /** CSS class for backlink (avoids duplication). */
    const BACKLINK_CLASS = 'dba-cfi-link';

    public static function get_instance() {
        if ( null === self::$instance ) self::$instance = new self();
        return self::$instance;
    }

    private function __construct() {
        add_action( self::VERIFY_HOOK, array( $this, 'verify_single_post' ), 10, 2 );
        add_action( self::AUDIT_HOOK,  array( $this, 'run_all_language_audits' ) );

        // Schedule weekly audit if not already scheduled
        if ( ! wp_next_scheduled( self::AUDIT_HOOK ) ) {
            wp_schedule_event( time() + DAY_IN_SECONDS, 'weekly', self::AUDIT_HOOK );
        }
    }

    /* =================================================================
     *  FULL ANTI-ORPHAN PIPELINE — Call this after post creation
     *
     *  Static wrapper so the generator can call it without get_instance()
     * ================================================================= */

    /**
     * Run the complete anti-orphan pipeline for a newly created post.
     *
     * @param  string $lang        Language code (e.g. 'fr').
     * @param  int    $post_id     New post ID.
     * @param  string $theme_slug  Theme slug (e.g. 'soparfi_obligations').
     * @return array  Summary: { auth_links, related, backlinks }
     */
    public static function on_post_created( $lang, $post_id, $theme_slug ) {
        $self = self::get_instance();
        $result = array( 'auth_links' => 0, 'related' => 0, 'backlinks' => 0 );

        // 1. Link from pillar page
        $linked = $self->link_from_pillar_page( $lang, $post_id, $theme_slug );
        $result['auth_links'] = $linked ? 1 : 0;

        // 2. Inject backlinks INTO related posts (they now point to new post)
        $result['backlinks'] = $self->inject_backlinks_from_related( $lang, $post_id, $theme_slug );

        // 3. Append related articles block TO the new post (outbound links)
        $related = $self->append_related_to_new_post( $lang, $post_id, $theme_slug );
        $result['related'] = $related ? $related : 0;

        // 4. Schedule verification (30 min later)
        $self->schedule_verification( $lang, $post_id );

        return $result;
    }

    /* =================================================================
     *  LINK FROM PILLAR PAGE
     *
     *  Appends the new article to the pillar page's topic list.
     *  Pillar pages use <!-- COMFISC_TOPIC_LIST --> as insertion marker.
     * ================================================================= */

    /**
     * Link a new post from the pillar page.
     *
     * @param  string $lang        Language code.
     * @param  int    $post_id     New post ID.
     * @param  string $theme_slug  Theme slug.
     * @return bool                True if linked, false if skipped/failed.
     */
    public function link_from_pillar_page( $lang, $post_id, $theme_slug ) {

        $pillar_id = intval( get_option( 'dba_comfisc_' . $lang . '_pillar_page_id', 0 ) );

        if ( ! $pillar_id || ! get_post_status( $pillar_id ) || get_post_status( $pillar_id ) === 'trash' ) {
            error_log( "Comfisc Anti-Orphan [{$lang}]: No pillar page configured. Skipping pillar link for #{$post_id}" );
            return false;
        }

        $pillar_content = get_post_field( 'post_content', $pillar_id );
        $permalink      = get_permalink( $post_id );
        $title          = get_the_title( $post_id );

        // Already linked?
        if ( strpos( $pillar_content, $permalink ) !== false ) {
            error_log( "Comfisc Anti-Orphan [{$lang}]: Post #{$post_id} already linked from pillar." );
            return true;
        }

        // Build the link HTML
        $link_html = '<li><a href="' . esc_url( $permalink ) . '">' . esc_html( $title ) . '</a></li>';

        // Strategy 1: Insert at the COMFISC_TOPIC_LIST marker
        if ( strpos( $pillar_content, self::PILLAR_LIST_MARKER ) !== false ) {
            $marker_pos = strpos( $pillar_content, self::PILLAR_LIST_MARKER );
            $after      = substr( $pillar_content, $marker_pos + strlen( self::PILLAR_LIST_MARKER ) );

            $ul_open_pos = strpos( $after, '<ul>' );

            if ( $ul_open_pos === false || $ul_open_pos > 50 ) {
                // No <ul> right after marker — create one
                $new_content = substr( $pillar_content, 0, $marker_pos + strlen( self::PILLAR_LIST_MARKER ) )
                    . "\n<ul>\n" . $link_html . "\n</ul>"
                    . $after;
            } else {
                // Insert into existing <ul> (before closing </ul>)
                $ul_close_pos = strpos( $after, '</ul>' );
                if ( $ul_close_pos !== false ) {
                    $abs_pos = $marker_pos + strlen( self::PILLAR_LIST_MARKER ) + $ul_close_pos;
                    $new_content = substr( $pillar_content, 0, $abs_pos )
                        . $link_html . "\n"
                        . substr( $pillar_content, $abs_pos );
                } else {
                    $new_content = $pillar_content . "\n<ul>\n" . $link_html . "\n</ul>";
                }
            }
        }
        // Strategy 2: No marker — find the CTA and insert before it
        else {
            $cta_pos = stripos( $pillar_content, 'Damalion accompagne' );
            if ( ! $cta_pos ) $cta_pos = stripos( $pillar_content, 'damalion-cta' );
            if ( ! $cta_pos ) $cta_pos = stripos( $pillar_content, 'lets-connect' );

            if ( $cta_pos !== false ) {
                $tag_start = strrpos( substr( $pillar_content, 0, $cta_pos ), '<' );
                if ( $tag_start !== false ) {
                    $new_content = substr( $pillar_content, 0, $tag_start )
                        . "\n" . self::PILLAR_LIST_MARKER . "\n<ul>\n" . $link_html . "\n</ul>\n\n"
                        . substr( $pillar_content, $tag_start );
                } else {
                    $new_content = $pillar_content . "\n\n" . self::PILLAR_LIST_MARKER . "\n<ul>\n" . $link_html . "\n</ul>";
                }
            } else {
                $new_content = $pillar_content . "\n\n" . self::PILLAR_LIST_MARKER . "\n<ul>\n" . $link_html . "\n</ul>";
            }
        }

        wp_update_post( array(
            'ID'           => $pillar_id,
            'post_content' => $new_content,
        ) );

        error_log( "Comfisc Anti-Orphan [{$lang}]: Linked post #{$post_id} from pillar #{$pillar_id}" );
        return true;
    }

    /* =================================================================
     *  CROSS-LINK: BACKLINKS FROM RELATED POSTS → NEW POST
     *
     *  Injects a contextual link from up to MAX_BACKLINKS existing
     *  Comfisc posts back to the newly created post.
     * ================================================================= */

    /**
     * Inject backlinks from related existing posts to the new post.
     *
     * @param  string $lang        Language code.
     * @param  int    $post_id     New post ID.
     * @param  string $theme_slug  Theme slug.
     * @return int    Number of backlinks injected.
     */
    public function inject_backlinks_from_related( $lang, $post_id, $theme_slug ) {
        $prefix    = DBA_Comfisc_Dedupe::meta_prefix( $lang );
        $permalink = get_permalink( $post_id );
        $title     = get_the_title( $post_id );

        if ( ! $permalink ) return 0;

        // Find related posts: same language, different theme preferred
        $related = get_posts( array(
            'post_type'      => 'post',
            'post_status'    => 'publish',
            'posts_per_page' => 10,
            'fields'         => 'ids',
            'exclude'        => array( $post_id ),
            'meta_query'     => array(
                'relation' => 'AND',
                array( 'key' => $prefix . 'auto_created', 'compare' => 'EXISTS' ),
            ),
            'orderby' => 'date',
            'order'   => 'DESC',
        ) );

        if ( empty( $related ) ) return 0;

        $injected = 0;
        $link_tag = '<a class="' . self::BACKLINK_CLASS . '" href="' . esc_url( $permalink ) . '">'
            . esc_html( $title ) . '</a>';

        // Localised "Read also" label
        $read_also = array(
            'fr' => 'Lire aussi',
            'de' => 'Lesen Sie auch',
            'it' => 'Leggi anche',
            'es' => 'Lea también',
        );
        $label = $read_also[ $lang ] ?? 'Read also';

        foreach ( $related as $rid ) {
            if ( $injected >= self::MAX_BACKLINKS ) break;

            $content = get_post_field( 'post_content', $rid );

            // Skip if already linked
            if ( strpos( $content, $permalink ) !== false ) continue;

            // Find last </p> before CTA to inject
            $inject_html = '<p><strong>' . esc_html( $label ) . ' :</strong> ' . $link_tag . '</p>';

            // Try to inject before the CTA
            $cta_pos = stripos( $content, 'Damalion accompagne' );
            if ( ! $cta_pos ) $cta_pos = stripos( $content, 'damalion-cta' );
            if ( ! $cta_pos ) $cta_pos = stripos( $content, 'lets-connect' );

            if ( $cta_pos !== false ) {
                $tag_start = strrpos( substr( $content, 0, $cta_pos ), '<' );
                if ( $tag_start !== false ) {
                    $updated = substr( $content, 0, $tag_start ) . $inject_html . "\n" . substr( $content, $tag_start );
                } else {
                    $updated = $content . "\n" . $inject_html;
                }
            } else {
                // No CTA found — append at end
                $updated = $content . "\n" . $inject_html;
            }

            wp_update_post( array( 'ID' => $rid, 'post_content' => $updated ) );
            $injected++;

            error_log( "Comfisc Anti-Orphan [{$lang}]: Injected backlink to #{$post_id} into existing post #{$rid}" );
        }

        return $injected;
    }

    /* =================================================================
     *  CROSS-LINK: RELATED BLOCK ON THE NEW POST (outbound)
     *
     *  Appends a "Related Articles" section to the new post
     *  pointing to up to MAX_RELATED existing Comfisc articles.
     * ================================================================= */

    /**
     * Append a "Related Articles" block to the new post.
     *
     * @param  string $lang        Language code.
     * @param  int    $post_id     New post ID.
     * @param  string $theme_slug  Theme slug.
     * @return int|false           Number of related links added, or false.
     */
    public function append_related_to_new_post( $lang, $post_id, $theme_slug ) {
        $prefix = DBA_Comfisc_Dedupe::meta_prefix( $lang );

        $content = get_post_field( 'post_content', $post_id );

        // Don't duplicate
        if ( strpos( $content, self::RELATED_CLASS ) !== false ) return false;

        // Find related: same language, exclude self, prioritise recent
        $related = get_posts( array(
            'post_type'      => 'post',
            'post_status'    => 'publish',
            'posts_per_page' => self::MAX_RELATED + 2, // extra for filtering
            'fields'         => 'ids',
            'exclude'        => array( $post_id ),
            'meta_query'     => array(
                array( 'key' => $prefix . 'auto_created', 'compare' => 'EXISTS' ),
            ),
            'orderby' => 'date',
            'order'   => 'DESC',
        ) );

        if ( empty( $related ) ) return false;

        // Take up to MAX_RELATED
        $picks = array_slice( $related, 0, self::MAX_RELATED );

        // Localised heading
        $headings = array(
            'fr' => 'Articles connexes',
            'de' => 'Verwandte Artikel',
            'it' => 'Articoli correlati',
            'es' => 'Artículos relacionados',
        );
        $heading = $headings[ $lang ] ?? 'Related Articles';

        $links_html = '';
        foreach ( $picks as $rid ) {
            $links_html .= '<li><a href="' . esc_url( get_permalink( $rid ) ) . '">'
                . esc_html( get_the_title( $rid ) ) . '</a></li>';
        }

        $block = "\n\n" . '<div class="' . self::RELATED_CLASS . '" style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:16px 20px;margin:24px 0;">'
            . '<h3 style="margin:0 0 10px;font-size:1em;">' . esc_html( $heading ) . '</h3>'
            . '<ul style="margin:0;padding-left:20px;">' . $links_html . '</ul>'
            . '</div>';

        // Insert before CTA if present
        $cta_pos = stripos( $content, 'Damalion accompagne' );
        if ( ! $cta_pos ) $cta_pos = stripos( $content, 'damalion-cta' );
        if ( ! $cta_pos ) $cta_pos = stripos( $content, 'lets-connect' );

        if ( $cta_pos !== false ) {
            $tag_start = strrpos( substr( $content, 0, $cta_pos ), '<' );
            if ( $tag_start !== false ) {
                $updated = substr( $content, 0, $tag_start ) . $block . "\n" . substr( $content, $tag_start );
            } else {
                $updated = $content . $block;
            }
        } else {
            $updated = $content . $block;
        }

        wp_update_post( array( 'ID' => $post_id, 'post_content' => $updated ) );

        error_log( "Comfisc Anti-Orphan [{$lang}]: Appended " . count( $picks ) . " related article links to new post #{$post_id}" );
        return count( $picks );
    }

    /* =================================================================
     *  VERIFICATION SCHEDULING
     * ================================================================= */

    /**
     * Schedule a one-time verification for a post (30 min after publish).
     */
    public function schedule_verification( $lang, $post_id ) {
        $timestamp = time() + ( 30 * MINUTE_IN_SECONDS );
        wp_schedule_single_event( $timestamp, self::VERIFY_HOOK, array( $lang, $post_id ) );
    }

    /**
     * Verify that a published post is linked from the pillar page.
     * If not, re-run the pillar link process.
     */
    public function verify_single_post( $lang, $post_id ) {
        $pillar_id = intval( get_option( 'dba_comfisc_' . $lang . '_pillar_page_id', 0 ) );
        if ( ! $pillar_id ) return;

        $pillar_content = get_post_field( 'post_content', $pillar_id );
        $permalink      = get_permalink( $post_id );

        if ( $permalink && strpos( $pillar_content, $permalink ) === false ) {
            $prefix     = DBA_Comfisc_Dedupe::meta_prefix( $lang );
            $theme_slug = get_post_meta( $post_id, $prefix . 'theme', true );

            $this->link_from_pillar_page( $lang, $post_id, $theme_slug ?: 'unknown' );
            error_log( "Comfisc Anti-Orphan [{$lang}]: Verification fixed orphan #{$post_id}" );
        }
    }

    /* =================================================================
     *  WEEKLY AUDIT — Scan ALL languages for orphaned posts
     * ================================================================= */

    /**
     * Run audit for all loaded Comfisc languages (called by cron).
     */
    public function run_all_language_audits() {
        if ( ! class_exists( 'DBA_Comfisc_Loader' ) ) return;

        $total_results = array();
        $languages = DBA_Comfisc_Loader::get_languages();

        foreach ( $languages as $lang => $config ) {
            $result = $this->audit_language( $lang );
            $total_results[ $lang ] = $result;
        }

        error_log( 'Comfisc Anti-Orphan: Weekly audit complete — ' . wp_json_encode( $total_results ) );
        return $total_results;
    }

    /**
     * Scan all Comfisc posts for a single language and fix orphans.
     *
     * @param  string $lang
     * @return array  { 'total' => int, 'orphaned' => int, 'fixed' => int }
     */
    public function audit_language( $lang ) {
        $prefix    = DBA_Comfisc_Dedupe::meta_prefix( $lang );
        $result    = array( 'total' => 0, 'orphaned' => 0, 'fixed' => 0 );
        $pillar_id = intval( get_option( 'dba_comfisc_' . $lang . '_pillar_page_id', 0 ) );

        if ( ! $pillar_id || ! get_post_status( $pillar_id ) || get_post_status( $pillar_id ) === 'trash' ) {
            error_log( "Comfisc Anti-Orphan Audit [{$lang}]: No pillar page — aborting." );
            return $result;
        }

        $pillar_content = get_post_field( 'post_content', $pillar_id );

        $all_posts = get_posts( array(
            'post_type'      => 'post',
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'meta_query'     => array(
                array( 'key' => $prefix . 'auto_created', 'compare' => 'EXISTS' ),
            ),
        ) );

        $result['total'] = count( $all_posts );

        foreach ( $all_posts as $pid ) {
            if ( $pid == $pillar_id ) continue;

            $permalink = get_permalink( $pid );
            if ( $permalink && strpos( $pillar_content, $permalink ) === false ) {
                $result['orphaned']++;

                $theme_slug = get_post_meta( $pid, $prefix . 'theme', true ) ?: 'unknown';

                if ( $this->link_from_pillar_page( $lang, $pid, $theme_slug ) ) {
                    $result['fixed']++;
                    // Refresh pillar content for next iteration
                    $pillar_content = get_post_field( 'post_content', $pillar_id );
                }
            }
        }

        error_log( "Comfisc Anti-Orphan Audit [{$lang}]: total={$result['total']}, orphaned={$result['orphaned']}, fixed={$result['fixed']}" );
        return $result;
    }

    /* =================================================================
     *  ADMIN HELPERS — used by class-comfisc-admin.php panels
     *  Mirrors DBA_LATAM_Anti_Orphan::get_orphan_stats()
     * ================================================================= */

    /**
     * Scan ALL Comfisc posts and return orphan statistics.
     * An "orphan" = a post missing the related-articles block.
     *
     * @param  string $lang  Language code.
     * @return array  { total, linked, orphan_count, pillar_linked, orphans[] }
     */
    public static function get_orphan_stats( $lang = 'fr' ) {
        global $wpdb;
        $prefix = DBA_Comfisc_Dedupe::meta_prefix( $lang );

        $all = $wpdb->get_col(
            "SELECT pm.post_id FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
             WHERE pm.meta_key = '{$prefix}auto_created' AND p.post_status = 'publish'
             ORDER BY p.post_date DESC"
        );

        $total         = count( $all );
        $linked        = 0;
        $pillar_linked = 0;
        $orphans       = array();

        // Pillar page content for hub-link check
        $pillar_id      = intval( get_option( 'dba_comfisc_' . $lang . '_pillar_page_id', 0 ) );
        $pillar_content = $pillar_id ? get_post_field( 'post_content', $pillar_id ) : '';

        foreach ( $all as $pid ) {
            $content      = get_post_field( 'post_content', $pid );
            $has_related  = strpos( $content, self::RELATED_CLASS ) !== false;
            $has_backlink = strpos( $content, self::BACKLINK_CLASS ) !== false;
            $permalink    = get_permalink( $pid );

            // Count as "linked" if it has the related block
            if ( $has_related ) {
                $linked++;
            } else {
                if ( count( $orphans ) < 15 ) {
                    $orphans[] = array(
                        'id'    => (int) $pid,
                        'title' => get_the_title( $pid ),
                        'date'  => get_the_date( 'Y-m-d', $pid ),
                        'theme' => get_post_meta( $pid, $prefix . 'theme', true ),
                    );
                }
            }

            // Pillar hub link check
            if ( $pillar_content && $permalink && strpos( $pillar_content, $permalink ) !== false ) {
                $pillar_linked++;
            }
        }

        return array(
            'total'          => $total,
            'linked'         => $linked,
            'pillar_linked'  => $pillar_linked,
            'orphan_count'   => $total - $linked,
            'pillar_orphans' => $total - $pillar_linked,
            'orphans'        => $orphans,
        );
    }

    /**
     * Fix ALL orphan posts for a language.
     * 1. Append related-articles block to posts missing it
     * 2. Inject backlinks from recent posts
     * 3. Link from pillar page
     *
     * @param  string $lang
     * @return array  { scanned, fixed_related, fixed_pillar, fixed_backlinks }
     */
    public static function fix_all_orphans( $lang = 'fr' ) {
        $self   = self::get_instance();
        $prefix = DBA_Comfisc_Dedupe::meta_prefix( $lang );
        $result = array( 'scanned' => 0, 'fixed_related' => 0, 'fixed_pillar' => 0, 'fixed_backlinks' => 0 );

        global $wpdb;
        $all = $wpdb->get_col(
            "SELECT pm.post_id FROM {$wpdb->postmeta} pm
             INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
             WHERE pm.meta_key = '{$prefix}auto_created' AND p.post_status = 'publish'
             ORDER BY p.post_date DESC"
        );

        $result['scanned'] = count( $all );

        foreach ( $all as $pid ) {
            $content    = get_post_field( 'post_content', $pid );
            $theme_slug = get_post_meta( $pid, $prefix . 'theme', true ) ?: 'unknown';

            // 1. Fix missing related block
            if ( strpos( $content, self::RELATED_CLASS ) === false ) {
                $added = $self->append_related_to_new_post( $lang, (int) $pid, $theme_slug );
                if ( $added ) $result['fixed_related']++;
            }

            // 2. Fix missing pillar link
            $linked = $self->link_from_pillar_page( $lang, (int) $pid, $theme_slug );
            if ( $linked ) $result['fixed_pillar']++;

            // 3. Inject backlinks from related posts → this post
            $bl = $self->inject_backlinks_from_related( $lang, (int) $pid, $theme_slug );
            $result['fixed_backlinks'] += $bl;
        }

        error_log( 'Comfisc Anti-Orphan fix_all_orphans [' . $lang . ']: ' . wp_json_encode( $result ) );
        return $result;
    }
}

