<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™
// File: /home/ploi/automarketer-ai.com/public/wp-content/plugins/automarketer-ai/includes/class-link-preserver.php
// Description: Preserves internal links across post content rewrites - NUCLEAR+++
// Last Edited: 2026-05-25
// ==================================================

namespace AutoMarketerAI;

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

class LinkPreserver {

    // Returns true if the post is a member of any content cluster.
    public static function isClusterPost(int $post_id): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_cluster_posts';
        $count = (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM `{$table}` WHERE post_id = %d",
                $post_id
            )
        );
        return $count > 0;
    }

    // Extracts all internal <a href> links from HTML content.
    // Returns array of [ 'href' => string, 'html' => string ] -- full anchor tag preserved.
    private static function extractInternalLinks(string $content): array {
        if ($content === '') {
            return [];
        }
        $home  = home_url('/');
        $links = [];
        $count = preg_match_all('/<a\s[^>]*href=["\']([^"\']+)["\'][^>]*>.*?<\/a>/is', $content, $matches, PREG_SET_ORDER);
        if ($count === false || $count === 0) {
            return [];
        }
        foreach ($matches as $match) {
            $href = $match[1];
            // Internal = starts with home URL or is a root-relative path.
            if (str_starts_with($href, $home) || str_starts_with($href, '/')) {
                $links[] = [
                    'href' => $href,
                    'html' => $match[0],
                ];
            }
        }
        return $links;
    }

    // Returns true if $href already appears in $content (as a link).
    private static function hrefInContent(string $href, string $content): bool {
        // Normalise trailing slash for comparison.
        $href_norm = rtrim($href, '/');
        return (bool) preg_match('/<a\s[^>]*href=["\']' . preg_quote($href_norm, '/') . '\/?["\'][^>]*>/i', $content);
    }

    // Appends a single "Also read:" line for a missing link at the end of content.
    private static function appendLink(string $content, string $link_html, string $href): string {
        // Try to extract the link text from the anchor tag for a clean label.
        $label = $link_html;
        if (preg_match('/<a[^>]*>(.*?)<\/a>/is', $link_html, $m)) {
            $label = strip_tags($m[1]);
        }
        $also_read = '<p>Also read: <a href="' . esc_url($href) . '">' . esc_html($label) . '</a>.</p>';

        // Prefer injecting before the last </p> block rather than appending raw.
        if (str_contains($content, '</p>')) {
            $pos = strrpos($content, '</p>');
            return substr($content, 0, $pos + 4) . "\n" . $also_read . substr($content, $pos + 4);
        }
        return $content . "\n" . $also_read;
    }

    // Core method. Call this wrapping every wp_update_post that writes post_content.
    //
    // Usage:
    //   $new_content = LinkPreserver::preserve($post_id, $new_content);
    //   wp_update_post(['ID' => $post_id, 'post_content' => $new_content]);
    //
    // Behaviour:
    // - Loads current DB content.
    // - Extracts all internal links from current content.
    // - For each link missing from $new_content, re-injects it.
    // - Cluster posts: ALL missing links are re-injected (no cap).
    // - Non-cluster posts: up to 2 missing links re-injected (matches injection cap elsewhere).
    // - Returns $new_content unchanged if no links are missing.
    public static function preserve(int $post_id, string $new_content): string {
        $post = get_post($post_id);
        if (!($post instanceof \WP_Post)) {
            return $new_content;
        }

        $existing_links = self::extractInternalLinks($post->post_content);
        if ($existing_links === []) {
            return $new_content;
        }

        $is_cluster = self::isClusterPost($post_id);
        $injected   = 0;

        foreach ($existing_links as $link) {
            if (self::hrefInContent($link['href'], $new_content)) {
                continue;
            }
            // Non-cluster posts: cap at 2 re-injections to avoid flooding content.
            if (!$is_cluster && $injected >= 2) {
                break;
            }
            $new_content = self::appendLink($new_content, $link['html'], $link['href']);
            $injected++;
        }

        return $new_content;
    }
}