<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ — NUCLEAR+++
// File: /home/ploi/automarketer-ai.com/public/wp-content/plugins/automarketer-ai/includes/class-outbound-links.php
// Description: Per-post outbound link generation via AI, plus curated library management - NUCLEAR+++
// Last Edited: 2026-05-16
// ==================================================
namespace AutoMarketerAI;
defined('ABSPATH') || exit;

class OutboundLinks
{
    private const TABLE        = 'amai_outbound_links';
    private const SEED_OPTION  = 'amai_outbound_links_seeded';
    private const MAX_INJECT   = 2;

    // --------------------------------------------------
    // Generate outbound links for a specific post via AI,
    // inject them into the content, save, and return updated HTML.
    // The AI receives the actual post content so keywords will match.
    // --------------------------------------------------
    // --------------------------------------------------
    // Extract real noun-phrase candidates from plain text.
    // Returns up to $limit unique 2-5 word lowercase phrases,
    // skipping phrases that are already linked in the HTML.
    // --------------------------------------------------
    private static function extractCandidates(string $plain, string $html, int $limit = 30): array
    {
        $words = preg_split('/\s+/', trim($plain), -1, PREG_SPLIT_NO_EMPTY);
        if (empty($words)) {
            return [];
        }
        $stop = [
            'a','an','the','and','or','but','in','on','at','to','for','of','with',
            'by','from','as','is','are','was','were','be','been','being','have',
            'has','had','do','does','did','will','would','could','should','may',
            'might','shall','can','this','that','these','those','it','its','we',
            'you','your','they','their','our','my','i','he','she','his','her',
            'not','no','so','if','then','than','when','where','how','what','who',
            'all','any','some','more','most','also','just','very','too','up','out',
            'about','into','over','after','before','each','every','both','such',
        ];
        $seen = [];
        $candidates = [];
        $total = count($words);
        for ($len = 3; $len >= 2; $len--) {
            for ($i = 0; $i <= $total - $len; $i++) {
                $slice = array_slice($words, $i, $len);
                // Skip if first or last word is a stop word
                $first = strtolower(preg_replace('/[^a-z]/i', '', $slice[0]));
                $last  = strtolower(preg_replace('/[^a-z]/i', '', $slice[$len - 1]));
                if (in_array($first, $stop, true) || in_array($last, $stop, true)) {
                    continue;
                }
                $phrase = implode(' ', $slice);
                $key    = strtolower($phrase);
                if (isset($seen[$key])) {
                    continue;
                }
                // Skip if phrase is already linked in HTML
                if (preg_match('/<a[^>]*>' . preg_quote($phrase, '/') . '<\/a>/iu', $html)) {
                    continue;
                }
                $seen[$key]   = true;
                $candidates[] = $phrase;
                if (count($candidates) >= $limit) {
                    break 2;
                }
            }
        }
        return $candidates;
    }

    public static function generateForPost(string $html, int $post_id, string $focus_keyword): string
    {
        $plain = wp_strip_all_tags($html);
        if (trim($plain) === '') {
            return $html;
        }

        // Early exit: post already has 2+ outbound links -- never add more
        preg_match_all('/<a[^>]+href=["\'\']([^"\']+)["\'\'][^>]*>/i', $html, $_om);
        $_oc = 0;
        $_home = home_url();
        foreach ($_om[1] as $_oh) {
            if (!str_starts_with($_oh, '/') && !str_starts_with($_oh, $_home)) {
                $_oc++;
            }
        }
        if ($_oc >= 2) {
            return $html;
        }

        // Extract real phrases from the post -- AI picks from these, so matches are guaranteed
        $candidates = self::extractCandidates($plain, $html, 30);
        if (empty($candidates)) {
            return $html;
        }

        $candidate_list = implode("\n", array_map(
            static fn(string $p): string => '- ' . $p,
            $candidates
        ));

        $year = (int) gmdate('Y');

        $prompt =
            'You are an outbound link strategist. The current year is ' . $year . '. ' .
            'Below is a list of real phrases extracted verbatim from a blog post. ' .
            'Pick exactly 3 of these phrases that would benefit most from an outbound link to an authoritative external source. ' .
            'For each chosen phrase, suggest a specific real URL on a high-authority domain (not a homepage). ' .
            'Focus keyword for context: ' . $focus_keyword . "\n\n" .
            'Candidate phrases (choose ONLY from this list -- copy the phrase EXACTLY as shown):' . "\n" .
            $candidate_list . "\n\n" .
            'BANNED domains -- never suggest URLs from: wikipedia.org, facebook.com, twitter.com, x.com, instagram.com, linkedin.com, tiktok.com, youtube.com, sproutsocial.com, hubspot.com, buffer.com, hootsuite.com, later.com, medium.com, forbes.com, entrepreneur.com, inc.com. ' .
            'No invented or hallucinated URLs.' . "\n\n" .
            'Return ONLY a raw JSON array with exactly 3 objects. Each object must have exactly these keys:' . "\n" .
            '- "keyword": the phrase copied EXACTLY from the candidate list above' . "\n" .
            '- "url": the full URL of a specific real page on a high-authority domain' . "\n" .
            '- "anchor": 2-5 words of natural anchor text for the link' . "\n\n" .
            'Return raw JSON only - no markdown fences, no explanation, no preamble.';

        $active_modules = (array) get_option('amai_active_modules', []);
        $ai_module_id   = '';
        foreach ($active_modules as $mod) {
            if (strpos((string) $mod, 'ai-') === 0) { $ai_module_id = (string) $mod; break; }
        }
        if ($ai_module_id === '') { $ai_module_id = 'ai-claude'; }

        $module_suffix = ucfirst(substr($ai_module_id, 3));
        $module_class  = 'AutoMarketerAI\\Modules\\' . $module_suffix;
        $module_file   = AMAI_PLUGIN_DIR . '/modules/' . $ai_module_id . '.php';

        if (!is_file($module_file)) {
            return $html;
        }
        require_once $module_file;
        if (!class_exists($module_class)) {
            return $html;
        }

        $module = new $module_class();
        if (!method_exists($module, 'generate_raw')) {
            return $html;
        }

        $api_key = \AutoMarketerAI\KeyVault::decrypt((string) get_option('amai_api_key_' . $ai_module_id, ''));
        $model   = $module->get_default_model();
        $raw     = $module->generate_raw($prompt, $api_key, $model, 1024);

        if ($raw === null || $raw === '') {
            return $html;
        }

        $clean = trim((string) preg_replace('/^```(?:json)?\s*|\s*```$/s', '', $raw));
        $items = json_decode($clean, true);
        if (!is_array($items) || count($items) === 0) {
            return $html;
        }

        $injected = 0;
        foreach ($items as $item) {
            if ($injected >= self::MAX_INJECT) { break; }
            if (!is_array($item)) { continue; }

            $keyword = sanitize_text_field((string) ($item['keyword'] ?? ''));
            $url     = esc_url_raw((string) ($item['url']     ?? ''));
            $anchor  = sanitize_text_field((string) ($item['anchor']  ?? ''));

            if ($keyword === '' || $url === '' || $anchor === '') { continue; }
            if (!filter_var($url, FILTER_VALIDATE_URL)) { continue; }

            // Enforce banned domain list in code -- never rely on prompt alone
            $banned_domains = [
                'wikipedia.org','facebook.com','twitter.com','x.com','instagram.com',
                'linkedin.com','tiktok.com','youtube.com','sproutsocial.com','hubspot.com',
                'buffer.com','hootsuite.com','later.com','medium.com','forbes.com',
                'entrepreneur.com','inc.com',
            ];
            $url_host = strtolower((string) parse_url($url, PHP_URL_HOST));
            $is_banned = false;
            foreach ($banned_domains as $banned) {
                if ($url_host === $banned || str_ends_with($url_host, '.' . $banned)) {
                    $is_banned = true;
                    break;
                }
            }
            if ($is_banned) { continue; }

            // Skip if URL already in content
            if (mb_stripos($html, $url) !== false) { continue; }

            // Match keyword verbatim in plain-text context (not inside a tag)
            $pattern = '/(?<!["\'=>])\b(' . preg_quote($keyword, '/') . ')\b(?![^<]*>)/iu';
            if (!preg_match($pattern, $html)) { continue; }

            $link = '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($anchor) . '</a>';
            $html = preg_replace($pattern, $link, $html, 1);
            $injected++;
        }

        if ($injected > 0) {
            wp_update_post(['ID' => $post_id, 'post_content' => $html]);
        }

        return $html;
    }

    // --------------------------------------------------
    // Check whether the table has been seeded for this install
    // --------------------------------------------------
}
