<?php
if (!defined('ABSPATH')) exit;

/**
 * GEO Score (0–100) — how ready a page is to be surfaced/cited by AI answer engines
 * (ChatGPT, Perplexity, Google AI Overviews, etc.).
 *
 * Score combines a deterministic structural pass with an AI assessment across
 * GEO dimensions. Manual single-page analysis is free (BYOK).
 */
class AIVT_GEO_Score {

    /** Weighted GEO dimensions. */
    public static function dimensions() {
        return [
            'answerability' => ['label' => __('Answer-first structure', 'pewit-geo-analysis-tool'), 'weight' => 20],
            'clarity'       => ['label' => __('Clarity & directness', 'pewit-geo-analysis-tool'), 'weight' => 15],
            'factual'       => ['label' => __('Factual density & specifics', 'pewit-geo-analysis-tool'), 'weight' => 15],
            'entities'      => ['label' => __('Entity clarity', 'pewit-geo-analysis-tool'), 'weight' => 12],
            'eeat'          => ['label' => __('E-E-A-T signals', 'pewit-geo-analysis-tool'), 'weight' => 13],
            'structure'     => ['label' => __('Headings, lists & Q&A', 'pewit-geo-analysis-tool'), 'weight' => 10],
            'schema'        => ['label' => __('Structured data', 'pewit-geo-analysis-tool'), 'weight' => 10],
            'freshness'     => ['label' => __('Freshness', 'pewit-geo-analysis-tool'), 'weight' => 5],
        ];
    }

    /** Cheap, deterministic structural signals (no API) used to ground the prompt. */
    public static function structural_signals($post_id) {
        $post = get_post($post_id);
        if (!$post) {
            return [];
        }
        $raw = (string) $post->post_content;
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Intentionally applying WordPress core 'the_content' filter to get rendered content.
        $text = wp_strip_all_tags(apply_filters('the_content', $raw));
        $words = str_word_count(wp_strip_all_tags($text));
        $modified = get_post_modified_time('U', true, $post);
        $age_days = $modified ? floor((time() - $modified) / DAY_IN_SECONDS) : null;

        return [
            'word_count'   => $words,
            'has_h2'       => (bool) preg_match('/<h2[ >]/i', $raw) || (bool) preg_match('/\n#{2,3}\s/', $raw),
            'has_lists'    => (bool) preg_match('/<(ul|ol)[ >]/i', $raw),
            'has_question' => (bool) preg_match('/\?(\s|<)/', $raw),
            'has_schema'   => self::post_has_schema($post_id),
            'age_days'     => $age_days,
            'has_excerpt'  => has_excerpt($post),
        ];
    }

    public static function post_has_schema($post_id) {
        // Reuse AI Schema Builder data when present.
        if (class_exists('AISB_Schema') && method_exists('AISB_Schema', 'get_json')) {
            $json = AISB_Schema::get_json($post_id);
            if (!empty($json)) {
                return true;
            }
        }
        $own = AIVT_Store::get($post_id, AIVT_Store::META_SCHEMA);
        return !empty($own['json']);
    }

    /**
     * Analyze a single post. Returns the saved analysis array or WP_Error.
     *
     * @return array|WP_Error
     */
    public static function analyze($post_id) {
        $ctx = AIVT_API::post_context($post_id, 6000);
        if (empty($ctx)) {
            return new WP_Error('aivt_no_post', __('Post not found', 'pewit-geo-analysis-tool'));
        }
        $sig = self::structural_signals($post_id);
        $prompt = self::build_prompt($ctx, $sig);
        $json = AIVT_API::complete_json($prompt, 1500);
        if (is_wp_error($json)) {
            return $json;
        }

        $dims = self::dimensions();
        $breakdown = [];
        $weighted_sum = 0;
        $weight_total = 0;
        $ai_breakdown = isset($json['dimensions']) && is_array($json['dimensions']) ? $json['dimensions'] : [];
        foreach ($dims as $key => $meta) {
            $raw = isset($ai_breakdown[$key]) ? $ai_breakdown[$key] : null;
            $dscore = is_array($raw) ? (int) ($raw['score'] ?? 0) : (int) $raw;
            $dscore = AIVT_API::clamp_score($dscore);
            $note = is_array($raw) ? (string) ($raw['note'] ?? '') : '';
            $breakdown[$key] = [
                'label' => $meta['label'],
                'score' => $dscore,
                'note'  => $note,
            ];
            $weighted_sum += $dscore * $meta['weight'];
            $weight_total += $meta['weight'];
        }
        $score = $weight_total ? AIVT_API::clamp_score($weighted_sum / $weight_total) : AIVT_API::clamp_score($json['score'] ?? 0);

        $tips = [];
        if (!empty($json['tips']) && is_array($json['tips'])) {
            foreach ($json['tips'] as $t) {
                $tips[] = sanitize_text_field(is_array($t) ? ($t['tip'] ?? '') : $t);
            }
            $tips = array_values(array_filter($tips));
        }

        $data = [
            'score'     => $score,
            'breakdown' => $breakdown,
            'summary'   => sanitize_text_field((string) ($json['summary'] ?? '')),
            'tips'      => array_slice($tips, 0, 8),
            'signals'   => $sig,
        ];
        return AIVT_Store::save($post_id, AIVT_Store::META_GEO, $data);
    }

    private static function build_prompt(array $ctx, array $sig) {
        $dims = self::dimensions();
        $dim_keys = implode(', ', array_keys($dims));
        $context = AIVT_API::context_block();

        $p  = "You are a Generative Engine Optimization (GEO/AEO) analyst. Assess how likely the page below is to be used, quoted or cited by AI answer engines (ChatGPT, Perplexity, Google AI Overviews, Claude).\n";
        $p .= "Score each dimension from 0-100 and give one concrete, specific note per dimension. Dimensions (keys): {$dim_keys}.\n";
        $p .= "Then give 3-6 prioritized, concrete improvement tips (most impactful first).\n\n";
        $p .= "Return ONLY JSON, no markdown:\n";
        $p .= '{"dimensions":{"answerability":{"score":0,"note":""}, "...":"..."},"summary":"one sentence","tips":["..."]}'."\n\n";
        if ($context) {
            $p .= "Site context:\n{$context}\n\n";
        }
        $p .= "Structural signals (already measured, factor them in): " . wp_json_encode($sig) . "\n\n";
        $p .= "PAGE\nTitle: {$ctx['title']}\nURL: {$ctx['url']}\n";
        if (!empty($ctx['focus_keyword'])) {
            $p .= "Target keyword: {$ctx['focus_keyword']}\n";
        }
        if (!empty($ctx['excerpt'])) {
            $p .= "Excerpt: {$ctx['excerpt']}\n";
        }
        $p .= "Content:\n{$ctx['content']}\n";
        return $p;
    }

    public static function score_label($score) {
        $score = (int) $score;
        if ($score >= 85) return __('Excellent', 'pewit-geo-analysis-tool');
        if ($score >= 70) return __('Good', 'pewit-geo-analysis-tool');
        if ($score >= 50) return __('Needs work', 'pewit-geo-analysis-tool');
        return __('Poor', 'pewit-geo-analysis-tool');
    }

    public static function score_class($score) {
        $score = (int) $score;
        if ($score >= 70) return 'ok';
        if ($score >= 50) return 'warn';
        return 'bad';
    }
}
