<?php
/**
 * High-level AI helpers used by the post meta box.
 *
 * Each method:
 *   1. Builds a focused prompt around one piece of the post.
 *   2. Asks the AI provider for a strict JSON response.
 *   3. Returns a normalized array the JS panel can render directly.
 *
 * The prompts are intentionally short to keep token cost down — a small/fast
 * model with 1024 max tokens is more than enough for the whole post + 3 alts.
 *
 * @package Brainwerk SEO Suite
 */

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

class Seoforge_AI_Suggester {

    private static function post_summary( $post ) {
        $body = wp_strip_all_tags( strip_shortcodes( $post->post_content ) );
        $body = trim( preg_replace( '/\s+/', ' ', $body ) );
        return mb_substr( $body, 0, 4000 );
    }

    private static function locale() {
        return substr( (string) get_locale(), 0, 2 );
    }

    public static function suggest_titles( $post_id ) {
        $post = get_post( $post_id );
        if ( ! $post ) { return new WP_Error( 'no_post', 'Post not found' ); }
        $row     = Seoforge_Meta::get_post_meta( $post_id );
        $title   = $post->post_title;
        $focus   = $row['focus_keyword'] ?? '';
        $body    = self::post_summary( $post );
        $locale  = self::locale();

        $system = "You are an expert WordPress SEO copywriter. You always reply in the same language as the source content. Output strict JSON only — no prose, no markdown fences. The JSON must match the schema requested.";
        $user = "Source language: {$locale}\n"
              . "Current title: {$title}\n"
              . "Focus keyword (optional): {$focus}\n\n"
              . "Article body (truncated):\n{$body}\n\n"
              . "Suggest THREE alternative SEO titles, each between 45 and 65 characters, optimised for click-through and the focus keyword.\n"
              . "Return strict JSON:\n"
              . '{"alternatives":[{"title":"...","why":"one short sentence why this is better"}]}';

        $r = Seoforge_AI::complete( $system, $user, array(
            'max_tokens' => 600, 'json' => true,
        ), 'titles_' . $post_id . '_' . md5( $title . '|' . $focus . '|' . substr( $body, 0, 200 ) ) );
        if ( is_wp_error( $r ) ) { return $r; }
        return isset( $r['data']['alternatives'] ) ? array( 'alternatives' => $r['data']['alternatives'] ) : $r;
    }

    public static function suggest_descriptions( $post_id ) {
        $post = get_post( $post_id );
        if ( ! $post ) { return new WP_Error( 'no_post', 'Post not found' ); }
        $row    = Seoforge_Meta::get_post_meta( $post_id );
        $title  = ! empty( $row['seo_title'] ) ? $row['seo_title'] : $post->post_title;
        $focus  = $row['focus_keyword'] ?? '';
        $body   = self::post_summary( $post );
        $locale = self::locale();

        $system = "You are an expert WordPress SEO copywriter. Reply in the same language as the source content. Output strict JSON only.";
        $user = "Source language: {$locale}\n"
              . "Title: {$title}\n"
              . "Focus keyword: {$focus}\n\n"
              . "Article body:\n{$body}\n\n"
              . "Suggest THREE meta descriptions, each between 130 and 160 characters, with a clear value proposition and a soft CTA. Include the focus keyword naturally if one is given.\n"
              . "Return strict JSON:\n"
              . '{"alternatives":[{"description":"...","why":"one short sentence"}]}';

        $r = Seoforge_AI::complete( $system, $user, array(
            'max_tokens' => 700, 'json' => true,
        ), 'desc_' . $post_id . '_' . md5( $title . '|' . $focus . '|' . substr( $body, 0, 200 ) ) );
        if ( is_wp_error( $r ) ) { return $r; }
        return isset( $r['data']['alternatives'] ) ? array( 'alternatives' => $r['data']['alternatives'] ) : $r;
    }

    public static function suggest_focus_keyword( $post_id ) {
        $post = get_post( $post_id );
        if ( ! $post ) { return new WP_Error( 'no_post', 'Post not found' ); }
        $body = self::post_summary( $post );
        $locale = self::locale();

        $system = "You are a WordPress SEO strategist. Reply in the same language as the source. Output strict JSON only.";
        $user = "Source language: {$locale}\n"
              . "Article title: {$post->post_title}\n\n"
              . "Article body:\n{$body}\n\n"
              . "Suggest THREE focus-keyword candidates for this post, each 1–4 words, with realistic search intent. Rank them.\n"
              . "Return strict JSON:\n"
              . '{"alternatives":[{"keyword":"...","intent":"informational|commercial|transactional|navigational","why":"short reason"}]}';

        $r = Seoforge_AI::complete( $system, $user, array(
            'max_tokens' => 500, 'json' => true,
        ), 'kw_' . $post_id . '_' . md5( substr( $body, 0, 400 ) ) );
        if ( is_wp_error( $r ) ) { return $r; }
        return isset( $r['data']['alternatives'] ) ? array( 'alternatives' => $r['data']['alternatives'] ) : $r;
    }

    /**
     * Suggests internal-linking opportunities. We pre-filter candidate posts
     * to those that mention the focus keyword (or share categories), so the
     * model only sees a relevant shortlist.
     */
    public static function suggest_internal_links( $post_id ) {
        $post = get_post( $post_id );
        if ( ! $post ) { return new WP_Error( 'no_post', 'Post not found' ); }
        $row   = Seoforge_Meta::get_post_meta( $post_id );
        $focus = trim( (string) ( $row['focus_keyword'] ?? '' ) );
        $body  = self::post_summary( $post );

        // Find candidate posts.
        $candidates = array();
        if ( $focus ) {
            $q = new WP_Query( array(
                'post_type'      => array( 'post', 'page' ),
                'post_status'    => 'publish',
                'post__not_in'   => array( $post_id ),
                's'              => $focus,
                'posts_per_page' => 12,
                'no_found_rows'  => true,
            ) );
            while ( $q->have_posts() ) {
                $q->the_post();
                $candidates[] = array(
                    'id'    => get_the_ID(),
                    'title' => get_the_title(),
                    'url'   => get_permalink(),
                );
            }
            wp_reset_postdata();
        }
        // Fallback 1: posts in the same categories.
        if ( count( $candidates ) < 8 ) {
            $cats = wp_get_post_categories( $post_id );
            if ( ! empty( $cats ) ) {
                $q = new WP_Query( array(
                    'post_type'      => array( 'post' ),
                    'post_status'    => 'publish',
                    'post__not_in'   => array_merge( array( $post_id ), wp_list_pluck( $candidates, 'id' ) ),
                    'category__in'   => $cats,
                    'posts_per_page' => 12,
                    'no_found_rows'  => true,
                ) );
                while ( $q->have_posts() ) {
                    $q->the_post();
                    $candidates[] = array(
                        'id'    => get_the_ID(),
                        'title' => get_the_title(),
                        'url'   => get_permalink(),
                    );
                }
                wp_reset_postdata();
            }
        }
        // Fallback 2: latest published posts + pages — broaden the pool so
        // the AI has real choice instead of giving up on a tiny site.
        if ( count( $candidates ) < 8 ) {
            $q = new WP_Query( array(
                'post_type'      => array( 'post', 'page' ),
                'post_status'    => 'publish',
                'post__not_in'   => array_merge( array( $post_id ), wp_list_pluck( $candidates, 'id' ) ),
                'posts_per_page' => 12,
                'orderby'        => 'modified',
                'order'          => 'DESC',
                'no_found_rows'  => true,
            ) );
            while ( $q->have_posts() ) {
                $q->the_post();
                $candidates[] = array(
                    'id'    => get_the_ID(),
                    'title' => get_the_title(),
                    'url'   => get_permalink(),
                );
            }
            wp_reset_postdata();
        }
        if ( empty( $candidates ) ) {
            return array( 'alternatives' => array() );
        }
        $candidates_json = wp_json_encode( $candidates );

        $system = "You suggest internal links that strengthen WordPress SEO. Output strict JSON only — no markdown fences, no prose.";
        $user = "Current post title: {$post->post_title}\n"
              . "Focus keyword: {$focus}\n\n"
              . "Current post body:\n{$body}\n\n"
              . "Candidate posts (id, title, url):\n{$candidates_json}\n\n"
              . "Pick at most 5 candidates and return them ranked best-to-weakest.\n"
              . "Even loose thematic, audience or tonal connections count — small sites benefit from any internal link, so prefer 2-3 imperfect matches over an empty list.\n"
              . "For each pick: a short anchor text (3-6 words, in the source language) and one sentence on the connection.\n"
              . "Return strict JSON ONLY (no fences):\n"
              . '{"alternatives":[{"id":123,"anchor":"...","why":"..."}],"note":"optional short note if no good fits"}' . "\n"
              . 'Only use ids that appear in the candidate list. If you truly cannot find any link at all, still set "note" with one short sentence.';

        $r = Seoforge_AI::complete( $system, $user, array(
            'max_tokens' => 700, 'json' => true,
        ), 'links_' . $post_id . '_' . md5( $focus . '|' . substr( $body, 0, 200 ) ) );
        if ( is_wp_error( $r ) ) { return $r; }
        $alts = isset( $r['data']['alternatives'] ) ? (array) $r['data']['alternatives'] : array();
        $note = isset( $r['data']['note'] ) ? (string) $r['data']['note'] : '';
        $by_id = array();
        foreach ( $candidates as $c ) { $by_id[ (int) $c['id'] ] = $c; }
        foreach ( $alts as &$a ) {
            $cid = isset( $a['id'] ) ? (int) $a['id'] : 0;
            if ( $cid && isset( $by_id[ $cid ] ) ) {
                $a['title'] = $by_id[ $cid ]['title'];
                $a['url']   = $by_id[ $cid ]['url'];
            }
        }
        unset( $a );
        return array( 'alternatives' => $alts, 'note' => $note );
    }
}
