<?php

/**
 * Headline Analyzer
 *
 * Analyzes headlines/titles for SEO optimization following Google's guidelines.
 *
 * @package ProRank\SEO\Modules\Content
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Content;

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




defined('ABSPATH') || exit;

/**
 * HeadlineAnalyzer class
 *
 * Provides static methods for analyzing headlines.
 */
class HeadlineAnalyzer {
    /**
     * Optimal title length range (characters)
     */
    private const MIN_LENGTH = 30;
    private const MAX_LENGTH = 60;
    private const OPTIMAL_LENGTH = 55;

    /**
     * Power words that improve CTR
     */
    private const POWER_WORDS = [
        'best', 'top', 'ultimate', 'complete', 'guide', 'how', 'why', 'what',
        'free', 'new', 'proven', 'easy', 'simple', 'quick', 'fast', 'essential',
        'secret', 'powerful', 'amazing', 'incredible', 'effective', 'expert',
        'step-by-step', 'beginner', 'advanced', 'tips', 'tricks', 'strategies',
        'mistakes', 'avoid', 'never', 'always', 'must', 'need', 'ways', 'reasons',
    ];

    /**
     * Emotional words that drive engagement
     */
    private const EMOTIONAL_WORDS = [
        'amazing', 'awesome', 'brilliant', 'epic', 'essential', 'exclusive',
        'fantastic', 'incredible', 'inspiring', 'outstanding', 'powerful',
        'remarkable', 'stunning', 'ultimate', 'unbelievable', 'wonderful',
        'shocking', 'surprising', 'mind-blowing', 'life-changing', 'game-changing',
    ];

    /**
     * Terms that should not influence relevance calculations.
     */
    private const STOP_WORDS = [
        'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'can', 'for', 'from', 'how',
        'in', 'into', 'is', 'it', 'its', 'of', 'on', 'or', 'that', 'the', 'their',
        'this', 'to', 'what', 'when', 'where', 'which', 'who', 'why', 'with', 'your',
    ];

    /**
     * Analyze a headline
     *
     * @param string $title   The headline to analyze.
     * @param int    $post_id Optional post ID for context.
     * @return array Analysis results.
     */
    public static function analyze(string $title, int $post_id = 0): array {
        $title = trim($title);

        if (empty($title)) {
            return self::empty_analysis();
        }

        $length = mb_strlen($title);
        $word_count = str_word_count($title);
        $words = array_map('strtolower', str_word_count($title, 1));

        // Run all checks
        $length_analysis = self::analyze_length($title, $length);
        $power_words = self::analyze_power_words($words);
        $emotional_words = self::analyze_emotional_words($words);
        $number_analysis = self::analyze_numbers($title);
        $question_analysis = self::analyze_question($title);
        $caps_analysis = self::analyze_capitalization($title);
        $uniqueness = self::analyze_uniqueness($title, $post_id);
        $readability = self::analyze_readability($title);
        $content_match = self::analyze_content_match($title, $post_id);
        $stuffing = self::analyze_stuffing($words);
        $rewrite_risk = self::analyze_rewrite_risk(
            $length_analysis,
            $caps_analysis,
            $uniqueness,
            $content_match,
            $stuffing
        );

        // Calculate overall score
        $scores = [
            $length_analysis['score'] * 0.20,      // 20% weight
            $content_match['score'] * 0.25,        // 25% weight
            $rewrite_risk['score'] * 0.15,         // 15% weight
            $uniqueness['score'] * 0.15,           // 15% weight
            $stuffing['score'] * 0.10,             // 10% weight
            $readability['score'] * 0.05,          // 5% weight
            $power_words['score'] * 0.05,          // 5% weight
            $emotional_words['score'] * 0.03,      // 3% weight
            $number_analysis['score'] * 0.01,      // 1% weight
            $question_analysis['score'] * 0.01,    // 1% weight
        ];

        $overall_score = (int) round(array_sum($scores));

        // Collect all recommendations
        $recommendations = array_merge(
            $length_analysis['recommendations'] ?? [],
            $power_words['recommendations'] ?? [],
            $emotional_words['recommendations'] ?? [],
            $number_analysis['recommendations'] ?? [],
            $caps_analysis['recommendations'] ?? [],
            $uniqueness['recommendations'] ?? [],
            $readability['recommendations'] ?? [],
            $content_match['recommendations'] ?? [],
            $stuffing['recommendations'] ?? []
        );

        // Collect issues
        $issues = [];
        if ($length_analysis['score'] < 70) {
            $issues[] = [
                'type' => 'length',
                'severity' => $length_analysis['score'] < 50 ? 'error' : 'warning',
                'message' => $length_analysis['message'],
            ];
        }
        if (!$uniqueness['is_unique']) {
            $issues[] = [
                'type' => 'uniqueness',
                'severity' => 'warning',
                'message' => __('This title may not be unique', 'prorank-seo'),
            ];
        }
        if ($caps_analysis['score'] < 50) {
            $issues[] = [
                'type' => 'capitalization',
                'severity' => 'warning',
                'message' => $caps_analysis['message'],
            ];
        }
        foreach ($content_match['issues'] as $issue) {
            $issues[] = [
                'type' => 'content_match',
                'severity' => $issue['severity'] ?? 'warning',
                'message' => $issue['message'] ?? '',
            ];
        }
        foreach ($stuffing['issues'] as $issue) {
            $issues[] = [
                'type' => 'stuffing',
                'severity' => $issue['severity'] ?? 'warning',
                'message' => $issue['message'] ?? '',
            ];
        }

        return [
            'title' => $title,
            'overall_score' => $overall_score,
            'grade' => self::score_to_grade($overall_score),
            'length' => $length_analysis,
            'word_count' => $word_count,
            'rewrite_risk' => $rewrite_risk,
            'content_match' => $content_match,
            'power_words' => $power_words,
            'emotional_words' => $emotional_words,
            'numbers' => $number_analysis,
            'question' => $question_analysis,
            'capitalization' => $caps_analysis,
            'uniqueness' => $uniqueness,
            'stuffing' => $stuffing,
            'readability' => $readability,
            'recommendations' => $recommendations,
            'issues' => $issues,
        ];
    }

    /**
     * Analyze title length
     */
    private static function analyze_length(string $title, int $length): array {
        $score = 100;
        $status = 'good';
        $message = __('Title length is optimal for search results', 'prorank-seo');
        $recommendations = [];

        if ($length < self::MIN_LENGTH) {
            $score = max(20, 100 - ((self::MIN_LENGTH - $length) * 5));
            $status = 'too_short';
            $message = sprintf(
/* translators: %1$: value, %2$: value, %3$: value */
                __('Title is too short (%1$d characters). Aim for %2$d-%3$d characters.', 'prorank-seo'),
                $length,
                self::MIN_LENGTH,
                self::MAX_LENGTH
            );
            $recommendations[] = __('Add more descriptive words to your title', 'prorank-seo');
        } elseif ($length > self::MAX_LENGTH) {
            $score = max(40, 100 - (($length - self::MAX_LENGTH) * 3));
            $status = 'too_long';
            $message = sprintf(
/* translators: %1$: value, %2$: value */
                __('Title may be truncated in search results (%1$d characters). Keep it under %2$d.', 'prorank-seo'),
                $length,
                self::MAX_LENGTH
            );
            $recommendations[] = __('Shorten your title to prevent truncation in search results', 'prorank-seo');
        }

        return [
            'score' => $score,
            'status' => $status,
            'message' => $message,
            'length' => $length,
            'min' => self::MIN_LENGTH,
            'max' => self::MAX_LENGTH,
            'optimal' => self::OPTIMAL_LENGTH,
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze power words
     */
    private static function analyze_power_words(array $words): array {
        $found = array_intersect($words, self::POWER_WORDS);
        $count = count($found);

        $score = min(100, $count * 50); // 50 points per power word, max 100
        if ($count === 0) {
            $score = 40;
        }

        $recommendations = [];
        if ($count === 0) {
            $recommendations[] = __('Add a power word like "best", "guide", "how to", or "tips" to improve CTR', 'prorank-seo');
        }

        return [
            'score' => $score,
            'count' => $count,
            'found' => array_values($found),
            'suggestions' => array_slice(array_diff(self::POWER_WORDS, $found), 0, 5),
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze emotional words
     */
    private static function analyze_emotional_words(array $words): array {
        $found = array_intersect($words, self::EMOTIONAL_WORDS);
        $count = count($found);

        $score = $count > 0 ? min(100, 60 + ($count * 20)) : 50;

        $recommendations = [];
        if ($count === 0) {
            $recommendations[] = __('Consider adding an emotional word to make the title more engaging', 'prorank-seo');
        }

        return [
            'score' => $score,
            'count' => $count,
            'found' => array_values($found),
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze numbers in title
     */
    private static function analyze_numbers(string $title): array {
        $has_number = preg_match('/\d+/', $title, $matches);
        $number = $has_number ? $matches[0] : null;

        $score = $has_number ? 100 : 60;

        $recommendations = [];
        if (!$has_number) {
            $recommendations[] = __('Titles with numbers (e.g., "10 Tips", "5 Ways") often get higher CTR', 'prorank-seo');
        }

        return [
            'score' => $score,
            'has_number' => (bool) $has_number,
            'number' => $number,
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze if title is a question
     */
    private static function analyze_question(string $title): array {
        $is_question = str_ends_with(trim($title), '?');
        $starts_with_question = preg_match('/^(how|what|why|when|where|who|which|can|do|does|is|are|will|should)/i', $title);

        return [
            'score' => ($is_question || $starts_with_question) ? 100 : 70,
            'is_question' => $is_question,
            'starts_with_question_word' => (bool) $starts_with_question,
        ];
    }

    /**
     * Analyze capitalization
     */
    private static function analyze_capitalization(string $title): array {
        $is_all_caps = $title === strtoupper($title) && strlen($title) > 3;
        $is_all_lower = $title === strtolower($title);
        $has_title_case = preg_match('/^[A-Z]/', $title);

        $score = 80;
        $message = __('Capitalization looks good', 'prorank-seo');
        $recommendations = [];

        if ($is_all_caps) {
            $score = 30;
            $message = __('Avoid ALL CAPS - it looks like shouting', 'prorank-seo');
            $recommendations[] = __('Use proper title case instead of all capitals', 'prorank-seo');
        } elseif ($is_all_lower) {
            $score = 50;
            $message = __('Consider using title case for a more professional look', 'prorank-seo');
            $recommendations[] = __('Capitalize the first letter of major words', 'prorank-seo');
        } elseif ($has_title_case) {
            $score = 100;
            $message = __('Great use of capitalization', 'prorank-seo');
        }

        return [
            'score' => $score,
            'is_all_caps' => $is_all_caps,
            'is_all_lower' => $is_all_lower,
            'has_title_case' => (bool) $has_title_case,
            'message' => $message,
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze uniqueness
     */
    private static function analyze_uniqueness(string $title, int $post_id): array {
        global $wpdb;

        $is_unique = true;
        $duplicates = [];
        $is_template = false;

        // Check for template-like titles
        if (preg_match('/^(page|post|untitled|draft|test)\s*\d*$/i', $title)) {
            $is_template = true;
            $is_unique = false;
        }

        // Check for duplicate titles in database
        if ($post_id > 0) {
            $results = $wpdb->get_results($wpdb->prepare(
                "SELECT ID, post_title FROM {$wpdb->posts}
                 WHERE post_title = %s
                 AND ID != %d
                 AND post_status IN ('publish', 'draft', 'pending')
                 AND post_type IN ('post', 'page')
                 LIMIT 5",
                $title,
                $post_id
            ));

            if (!empty($results)) {
                $is_unique = false;
                foreach ($results as $post) {
                    $duplicates[] = [
                        'id' => $post->ID,
                        'title' => $post->post_title,
                        'edit_link' => get_edit_post_link($post->ID, 'raw'),
                    ];
                }
            }
        }

        $score = 100;
        $recommendations = [];

        if ($is_template) {
            $score = 20;
            $recommendations[] = __('Replace the generic title with a descriptive, unique headline', 'prorank-seo');
        } elseif (!$is_unique) {
            $score = 50;
            $recommendations[] = __('This title is used elsewhere. Consider making it unique.', 'prorank-seo');
        }

        return [
            'score' => $score,
            'is_unique' => $is_unique,
            'is_template' => $is_template,
            'duplicates' => $duplicates,
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Analyze readability
     */
    private static function analyze_readability(string $title): array {
        $word_count = str_word_count($title);
        $avg_word_length = $word_count > 0 ? mb_strlen(str_replace(' ', '', $title)) / $word_count : 0;

        $score = 80;
        $recommendations = [];

        // Very short titles
        if ($word_count < 3) {
            $score = 50;
            $recommendations[] = __('Title is very short. Add more context.', 'prorank-seo');
        }
        // Very long average word length suggests complex words
        elseif ($avg_word_length > 8) {
            $score = 60;
            $recommendations[] = __('Consider using simpler, shorter words for better readability', 'prorank-seo');
        }
        // Good word count
        elseif ($word_count >= 5 && $word_count <= 10) {
            $score = 100;
        }

        return [
            'score' => $score,
            'word_count' => $word_count,
            'avg_word_length' => round($avg_word_length, 1),
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Convert score to letter grade
     */
    private static function score_to_grade(int $score): string {
        if ($score >= 90) return 'A';
        if ($score >= 80) return 'B';
        if ($score >= 70) return 'C';
        if ($score >= 60) return 'D';
        return 'F';
    }

    /**
     * Analyze how well the headline aligns with the post content.
     */
    private static function analyze_content_match(string $title, int $post_id): array {
        if ($post_id <= 0) {
            return [
                'score' => 0,
                'precision' => 0,
                'recall' => 0,
                'matched_terms' => [],
                'missing_terms' => [],
                'issues' => [],
                'recommendations' => [],
                'applicable' => false,
            ];
        }

        $post = get_post($post_id);
        $content = $post ? wp_strip_all_tags(trim(($post->post_title ?? '') . ' ' . ($post->post_excerpt ?? '') . ' ' . ($post->post_content ?? ''))) : '';

        if ($content === '') {
            return [
                'score' => 0,
                'precision' => 0,
                'recall' => 0,
                'matched_terms' => [],
                'missing_terms' => [],
                'issues' => [],
                'recommendations' => [],
                'applicable' => false,
            ];
        }

        $title_terms = self::extract_significant_terms($title);
        $content_frequencies = self::get_term_frequencies($content);
        $content_terms = array_keys($content_frequencies);

        if (empty($title_terms) || empty($content_terms)) {
            return [
                'score' => 0,
                'precision' => 0,
                'recall' => 0,
                'matched_terms' => [],
                'missing_terms' => [],
                'issues' => [],
                'recommendations' => [],
                'applicable' => false,
            ];
        }

        $matched_terms = array_values(array_intersect($title_terms, $content_terms));
        $missing_terms = array_values(array_diff($title_terms, $matched_terms));
        $top_content_terms = array_slice($content_terms, 0, min(6, count($content_terms)));
        $recall_matches = array_values(array_intersect($top_content_terms, $title_terms));

        $precision = count($matched_terms) / max(1, count($title_terms));
        $recall = count($recall_matches) / max(1, count($top_content_terms));
        $full_phrase_bonus = stripos($content, $title) !== false ? 0.1 : 0;
        $score = (int) round(min(100, (($precision * 0.7) + ($recall * 0.2) + $full_phrase_bonus) * 100));

        $issues = [];
        $recommendations = [];

        if (!empty($missing_terms)) {
            $issues[] = [
                'message' => sprintf(
                    /* translators: %s: comma-separated missing terms */
                    __('These title terms are weakly represented in the content: %s', 'prorank-seo'),
                    implode(', ', array_slice($missing_terms, 0, 4))
                ),
                'severity' => $score < 60 ? 'medium' : 'low',
            ];
            $recommendations[] = __('Align the title more closely with the main terms used in the article body.', 'prorank-seo');
        }

        if ($score < 50) {
            $recommendations[] = __('The title promise and the article body look misaligned. Tighten one or the other.', 'prorank-seo');
        }

        return [
            'score' => $score,
            'precision' => round($precision, 2),
            'recall' => round($recall, 2),
            'matched_terms' => $matched_terms,
            'missing_terms' => $missing_terms,
            'issues' => $issues,
            'recommendations' => $recommendations,
            'applicable' => true,
        ];
    }

    /**
     * Detect keyword stuffing patterns in the headline.
     */
    private static function analyze_stuffing(array $words): array {
        $normalized_words = [];
        foreach ($words as $word) {
            $word = self::normalize_term($word);
            if ($word === '' || in_array($word, self::STOP_WORDS, true)) {
                continue;
            }
            $normalized_words[] = $word;
        }

        if (empty($normalized_words)) {
            return [
                'score' => 100,
                'issues' => [],
                'recommendations' => [],
            ];
        }

        $frequencies = array_count_values($normalized_words);
        $repeated_terms = array_filter($frequencies, static fn($count) => $count > 1);
        $excess_repetitions = array_sum(array_map(static fn($count) => $count - 1, $repeated_terms));
        $score = max(25, 100 - ($excess_repetitions * 30) - (max(0, count($repeated_terms) - 1) * 10));

        $issues = [];
        $recommendations = [];

        foreach ($repeated_terms as $term => $count) {
            $issues[] = [
                'message' => sprintf(
                    /* translators: 1: repeated term, 2: count */
                    __('The term "%1$s" is repeated %2$d times in the title.', 'prorank-seo'),
                    $term,
                    $count
                ),
                'severity' => $count >= 3 ? 'medium' : 'low',
            ];
        }

        if (!empty($issues)) {
            $recommendations[] = __('Avoid repeating the same keyword in the headline unless it is genuinely necessary.', 'prorank-seo');
        }

        return [
            'score' => (int) $score,
            'issues' => $issues,
            'recommendations' => $recommendations,
        ];
    }

    /**
     * Estimate Google rewrite risk from defensible headline factors.
     */
    private static function analyze_rewrite_risk(
        array $length_analysis,
        array $caps_analysis,
        array $uniqueness,
        array $content_match,
        array $stuffing
    ): array {
        $risk_points = 0;
        $issues = [];

        if (($length_analysis['status'] ?? 'good') === 'too_long') {
            $risk_points += 35;
            $issues[] = [
                'message' => __('This title is likely to be truncated or rewritten because it is too long.', 'prorank-seo'),
                'severity' => 'medium',
            ];
        } elseif (($length_analysis['status'] ?? 'good') === 'too_short') {
            $risk_points += 20;
            $issues[] = [
                'message' => __('This title is short enough to look generic, which increases rewrite risk.', 'prorank-seo'),
                'severity' => 'low',
            ];
        }

        if (!($uniqueness['is_unique'] ?? true)) {
            $risk_points += 25;
            $issues[] = [
                'message' => __('Duplicate or template-like titles are more likely to be rewritten.', 'prorank-seo'),
                'severity' => 'medium',
            ];
        }

        if (($caps_analysis['is_all_caps'] ?? false) === true) {
            $risk_points += 30;
            $issues[] = [
                'message' => __('ALL CAPS styling can trigger title rewrites.', 'prorank-seo'),
                'severity' => 'high',
            ];
        } elseif (($caps_analysis['score'] ?? 100) < 70) {
            $risk_points += 10;
        }

        if (($content_match['applicable'] ?? false) && ($content_match['score'] ?? 100) < 60) {
            $risk_points += 20;
            $issues[] = [
                'message' => __('The title does not align closely enough with the page content.', 'prorank-seo'),
                'severity' => 'medium',
            ];
        }

        if (($stuffing['score'] ?? 100) < 70) {
            $risk_points += 20;
            $issues[] = [
                'message' => __('Keyword repetition makes the title look manipulative.', 'prorank-seo'),
                'severity' => 'medium',
            ];
        }

        $score = max(0, 100 - $risk_points);
        $level = 'low';
        if ($risk_points >= 55) {
            $level = 'high';
        } elseif ($risk_points >= 25) {
            $level = 'medium';
        }

        return [
            'score' => (int) $score,
            'level' => $level,
            'issues' => $issues,
        ];
    }

    /**
     * Extract significant terms from text.
     *
     * @return array<int,string>
     */
    private static function extract_significant_terms(string $text): array {
        $tokens = preg_split('/[^a-z0-9-]+/i', strtolower($text)) ?: [];
        $terms = [];

        foreach ($tokens as $token) {
            $token = self::normalize_term($token);
            if ($token === '' || strlen($token) < 3 || in_array($token, self::STOP_WORDS, true)) {
                continue;
            }
            $terms[] = $token;
        }

        return array_values(array_unique($terms));
    }

    /**
     * Get weighted term frequencies from content.
     *
     * @return array<string,int>
     */
    private static function get_term_frequencies(string $text): array {
        $tokens = preg_split('/[^a-z0-9-]+/i', strtolower($text)) ?: [];
        $frequencies = [];

        foreach ($tokens as $token) {
            $token = self::normalize_term($token);
            if ($token === '' || strlen($token) < 3 || in_array($token, self::STOP_WORDS, true)) {
                continue;
            }
            $frequencies[$token] = ($frequencies[$token] ?? 0) + 1;
        }

        arsort($frequencies);
        return $frequencies;
    }

    /**
     * Normalize similar word forms to improve simple term matching.
     */
    private static function normalize_term(string $term): string {
        $term = trim(strtolower($term));

        if ($term === '') {
            return '';
        }

        if (strlen($term) > 4 && str_ends_with($term, 'ies')) {
            return substr($term, 0, -3) . 'y';
        }

        if (strlen($term) > 3 && str_ends_with($term, 's') && !str_ends_with($term, 'ss')) {
            return substr($term, 0, -1);
        }

        return $term;
    }

    /**
     * Return empty analysis for empty title
     */
    private static function empty_analysis(): array {
        return [
            'title' => '',
            'overall_score' => 0,
            'grade' => 'F',
            'length' => [
                'score' => 0,
                'status' => 'empty',
                'message' => __('Please enter a title', 'prorank-seo'),
                'length' => 0,
                'min' => self::MIN_LENGTH,
                'max' => self::MAX_LENGTH,
                'optimal' => self::OPTIMAL_LENGTH,
            ],
            'word_count' => 0,
            'rewrite_risk' => ['score' => 0, 'level' => 'high', 'issues' => []],
            'content_match' => ['score' => 0, 'precision' => 0, 'recall' => 0, 'issues' => [], 'applicable' => false],
            'power_words' => ['score' => 0, 'count' => 0, 'found' => []],
            'emotional_words' => ['score' => 0, 'count' => 0, 'found' => []],
            'numbers' => ['score' => 0, 'has_number' => false],
            'question' => ['score' => 0, 'is_question' => false],
            'capitalization' => ['score' => 0],
            'uniqueness' => ['score' => 0, 'is_unique' => true, 'duplicates' => []],
            'stuffing' => ['score' => 100, 'issues' => []],
            'readability' => ['score' => 0, 'word_count' => 0],
            'recommendations' => [__('Enter a title to see analysis', 'prorank-seo')],
            'issues' => [],
        ];
    }
}
