<?php
/**
 * Yoast-Style SEO Analyzer
 * Comprehensive SEO analysis with scoring, readability, keyword optimization, and content insights
 */

class YoastSEOAnalyzer {
    
    private $content;
    private $title;
    private $metaDescription;
    private $focusKeyword;
    private $slug;
    private $url;
    
    public function __construct($data = []) {
        $this->content = $data['content'] ?? '';
        $this->title = $data['title'] ?? '';
        $this->metaDescription = $data['meta_description'] ?? '';
        $this->focusKeyword = $data['focus_keyword'] ?? '';
        $this->slug = $data['slug'] ?? '';
        $this->url = $data['url'] ?? '';
    }
    
    /**
     * Perform comprehensive SEO analysis (Yoast-style)
     */
    public function analyze() {
        $analysis = [
            'seo_score' => $this->calculateSEOScore(),
            'readability_score' => $this->calculateReadabilityScore(),
            'focus_keyword_analysis' => $this->analyzeFocusKeyword(),
            'title_analysis' => $this->analyzeTitle(),
            'meta_description_analysis' => $this->analyzeMetaDescription(),
            'content_analysis' => $this->analyzeContent(),
            'url_analysis' => $this->analyzeURL(),
            'internal_linking' => $this->analyzeInternalLinking(),
            'image_analysis' => $this->analyzeImages(),
            'schema_analysis' => $this->analyzeSchema(),
            'social_analysis' => $this->analyzeSocial(),
            'recommendations' => $this->getRecommendations(),
            'overall_status' => $this->getOverallStatus()
        ];
        
        return $analysis;
    }
    
    /**
     * Calculate overall SEO score (0-100)
     */
    private function calculateSEOScore() {
        $score = 0;
        $maxScore = 0;
        
        // Title optimization (15 points)
        $maxScore += 15;
        $titleAnalysis = $this->analyzeTitle();
        $score += $titleAnalysis['score'];
        
        // Meta description (10 points)
        $maxScore += 10;
        $metaAnalysis = $this->analyzeMetaDescription();
        $score += $metaAnalysis['score'];
        
        // Focus keyword (20 points)
        $maxScore += 20;
        if (!empty($this->focusKeyword)) {
            $keywordAnalysis = $this->analyzeFocusKeyword();
            $score += $keywordAnalysis['score'];
        }
        
        // Content length (10 points)
        $maxScore += 10;
        $contentLength = str_word_count(strip_tags($this->content));
        if ($contentLength >= 300) {
            $score += 10;
        } elseif ($contentLength >= 200) {
            $score += 7;
        } elseif ($contentLength >= 100) {
            $score += 4;
        }
        
        // URL optimization (5 points)
        $maxScore += 5;
        $urlAnalysis = $this->analyzeURL();
        $score += $urlAnalysis['score'];
        
        // Image optimization (10 points)
        $maxScore += 10;
        $imageAnalysis = $this->analyzeImages();
        $score += $imageAnalysis['score'];
        
        // Internal linking (10 points)
        $maxScore += 10;
        $linkingAnalysis = $this->analyzeInternalLinking();
        $score += $linkingAnalysis['score'];
        
        // Readability (10 points)
        $maxScore += 10;
        $readabilityScore = $this->calculateReadabilityScore();
        $score += ($readabilityScore / 100) * 10;
        
        // Schema markup (5 points)
        $maxScore += 5;
        $schemaAnalysis = $this->analyzeSchema();
        if ($schemaAnalysis['has_schema']) {
            $score += 5;
        }
        
        // Social tags (5 points)
        $maxScore += 5;
        $socialAnalysis = $this->analyzeSocial();
        if ($socialAnalysis['has_og_tags'] && $socialAnalysis['has_twitter_tags']) {
            $score += 5;
        }
        
        $finalScore = round(($score / $maxScore) * 100);
        return min(100, max(0, $finalScore));
    }
    
    /**
     * Calculate readability score (Flesch Reading Ease)
     */
    private function calculateReadabilityScore() {
        $text = strip_tags($this->content);
        $text = preg_replace('/[^\w\s]/', '', $text);
        
        if (empty($text)) {
            return 0;
        }
        
        $sentences = preg_split('/[.!?]+/', $text);
        $sentences = array_filter($sentences);
        $sentenceCount = count($sentences);
        
        if ($sentenceCount === 0) {
            return 0;
        }
        
        $words = str_word_count($text);
        $syllables = $this->countSyllables($text);
        
        if ($words === 0 || $sentenceCount === 0) {
            return 0;
        }
        
        $avgSentenceLength = $words / $sentenceCount;
        $avgSyllablesPerWord = $syllables / $words;
        
        // Flesch Reading Ease formula
        $score = 206.835 - (1.015 * $avgSentenceLength) - (84.6 * $avgSyllablesPerWord);
        
        return min(100, max(0, round($score)));
    }
    
    /**
     * Count syllables in text
     */
    private function countSyllables($text) {
        $text = strtolower($text);
        $words = explode(' ', $text);
        $syllableCount = 0;
        
        foreach ($words as $word) {
            $word = preg_replace('/[^a-z]/', '', $word);
            if (empty($word)) continue;
            
            $vowels = preg_match_all('/[aeiouy]+/', $word);
            $syllableCount += max(1, $vowels);
        }
        
        return $syllableCount;
    }
    
    /**
     * Analyze title optimization
     */
    private function analyzeTitle() {
        $length = mb_strlen($this->title);
        $hasKeyword = !empty($this->focusKeyword) && stripos($this->title, $this->focusKeyword) !== false;
        $hasNumber = preg_match('/\d/', $this->title);
        $startsWithKeyword = !empty($this->focusKeyword) && stripos($this->title, $this->focusKeyword) === 0;
        
        $score = 0;
        $issues = [];
        $good = [];
        
        // Length check
        if ($length === 0) {
            $issues[] = 'SEO title is empty';
        } elseif ($length < 30) {
            $issues[] = "SEO title is too short (${length} characters). Aim for 30-60 characters.";
            $score += 3;
        } elseif ($length > 60) {
            $issues[] = "SEO title is too long (${length} characters). Aim for 30-60 characters.";
            $score += 5;
        } else {
            $good[] = "SEO title length is good (${length} characters)";
            $score += 10;
        }
        
        // Focus keyword check
        if (!empty($this->focusKeyword)) {
            if ($hasKeyword) {
                if ($startsWithKeyword) {
                    $good[] = "Focus keyword appears at the beginning of the SEO title";
                    $score += 5;
                } else {
                    $good[] = "Focus keyword appears in the SEO title";
                    $score += 3;
                }
            } else {
                $issues[] = "Focus keyword does not appear in the SEO title";
            }
        }
        
        return [
            'score' => min(15, $score),
            'length' => $length,
            'has_keyword' => $hasKeyword,
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 15)
        ];
    }
    
    /**
     * Analyze meta description
     */
    private function analyzeMetaDescription() {
        $length = mb_strlen($this->metaDescription);
        $hasKeyword = !empty($this->focusKeyword) && stripos($this->metaDescription, $this->focusKeyword) !== false;
        $hasCallToAction = preg_match('/\b(buy|shop|learn|discover|get|try|order|view|read|click)\b/i', $this->metaDescription);
        
        $score = 0;
        $issues = [];
        $good = [];
        
        if ($length === 0) {
            $issues[] = 'Meta description is empty';
        } elseif ($length < 120) {
            $issues[] = "Meta description is too short (${length} characters). Aim for 120-160 characters.";
            $score += 3;
        } elseif ($length > 160) {
            $issues[] = "Meta description is too long (${length} characters). Aim for 120-160 characters.";
            $score += 5;
        } else {
            $good[] = "Meta description length is good (${length} characters)";
            $score += 7;
        }
        
        if ($hasKeyword) {
            $good[] = "Focus keyword appears in the meta description";
            $score += 3;
        } elseif (!empty($this->focusKeyword)) {
            $issues[] = "Focus keyword does not appear in the meta description";
        }
        
        return [
            'score' => min(10, $score),
            'length' => $length,
            'has_keyword' => $hasKeyword,
            'has_cta' => $hasCallToAction,
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 10)
        ];
    }
    
    /**
     * Analyze focus keyword usage
     */
    private function analyzeFocusKeyword() {
        if (empty($this->focusKeyword)) {
            return [
                'score' => 0,
                'status' => 'bad',
                'issues' => ['No focus keyword set'],
                'good' => []
            ];
        }
        
        $text = strip_tags($this->content);
        $textLower = strtolower($text);
        $keywordLower = strtolower($this->focusKeyword);
        
        $count = substr_count($textLower, $keywordLower);
        $wordCount = str_word_count($text);
        $density = $wordCount > 0 ? ($count / $wordCount) * 100 : 0;
        
        $inTitle = stripos($this->title, $this->focusKeyword) !== false;
        $inMeta = stripos($this->metaDescription, $this->focusKeyword) !== false;
        $inFirstParagraph = $this->checkKeywordInFirstParagraph($text, $keywordLower);
        $inURL = stripos($this->slug, $this->focusKeyword) !== false || stripos($this->url, $this->focusKeyword) !== false;
        $inH1 = $this->checkKeywordInH1();
        $inSubheadings = $this->checkKeywordInSubheadings($keywordLower);
        
        $score = 0;
        $issues = [];
        $good = [];
        
        // Keyword density
        if ($count === 0) {
            $issues[] = "Focus keyword does not appear in the content";
        } elseif ($count === 1) {
            $issues[] = "Focus keyword appears only once in the content. Consider using it more often.";
            $score += 5;
        } elseif ($density < 0.5) {
            $issues[] = "Focus keyword density is very low (${density}%). Aim for 0.5-2.5%.";
            $score += 7;
        } elseif ($density > 2.5) {
            $issues[] = "Focus keyword density is too high (${density}%). Aim for 0.5-2.5%.";
            $score += 8;
        } else {
            $good[] = "Focus keyword density is good (${density}%)";
            $score += 10;
        }
        
        // Keyword placement
        if ($inTitle) {
            $good[] = "Focus keyword appears in the SEO title";
            $score += 3;
        } else {
            $issues[] = "Focus keyword does not appear in the SEO title";
        }
        
        if ($inMeta) {
            $good[] = "Focus keyword appears in the meta description";
            $score += 2;
        } else {
            $issues[] = "Focus keyword does not appear in the meta description";
        }
        
        if ($inFirstParagraph) {
            $good[] = "Focus keyword appears in the first paragraph";
            $score += 2;
        } else {
            $issues[] = "Focus keyword does not appear in the first paragraph";
        }
        
        if ($inURL) {
            $good[] = "Focus keyword appears in the URL";
            $score += 1;
        }
        
        if ($inH1) {
            $good[] = "Focus keyword appears in the H1 heading";
            $score += 1;
        }
        
        if ($inSubheadings) {
            $good[] = "Focus keyword appears in subheadings";
            $score += 1;
        }
        
        return [
            'score' => min(20, $score),
            'count' => $count,
            'density' => round($density, 2),
            'in_title' => $inTitle,
            'in_meta' => $inMeta,
            'in_first_paragraph' => $inFirstParagraph,
            'in_url' => $inURL,
            'in_h1' => $inH1,
            'in_subheadings' => $inSubheadings,
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 20)
        ];
    }
    
    /**
     * Analyze content quality
     */
    private function analyzeContent() {
        $text = strip_tags($this->content);
        $wordCount = str_word_count($text);
        $paragraphCount = substr_count($this->content, '<p>') + substr_count($this->content, '</p>');
        $paragraphCount = max(1, $paragraphCount / 2);
        
        $hasH1 = preg_match('/<h1[^>]*>.*?<\/h1>/i', $this->content);
        $h2Count = preg_match_all('/<h2[^>]*>.*?<\/h2>/i', $this->content);
        $h3Count = preg_match_all('/<h3[^>]*>.*?<\/h3>/i', $this->content);
        
        $imageCount = preg_match_all('/<img[^>]*>/i', $this->content);
        $imageWithAlt = preg_match_all('/<img[^>]*alt=["\'][^"\']+["\'][^>]*>/i', $this->content);
        
        $issues = [];
        $good = [];
        
        if ($wordCount < 300) {
            $issues[] = "Content is too short (${wordCount} words). Aim for at least 300 words.";
        } else {
            $good[] = "Content length is good (${wordCount} words)";
        }
        
        if (!$hasH1) {
            $issues[] = "No H1 heading found. Add an H1 heading with your focus keyword.";
        } else {
            $good[] = "H1 heading found";
        }
        
        if ($h2Count === 0) {
            $issues[] = "No H2 headings found. Consider adding subheadings to improve structure.";
        } else {
            $good[] = "${h2Count} H2 heading(s) found";
        }
        
        if ($imageCount > 0 && $imageWithAlt < $imageCount) {
            $missing = $imageCount - $imageWithAlt;
            $issues[] = "${missing} image(s) without alt text. Add alt text to all images.";
        } elseif ($imageCount > 0) {
            $good[] = "All images have alt text";
        }
        
        return [
            'word_count' => $wordCount,
            'paragraph_count' => $paragraphCount,
            'has_h1' => $hasH1,
            'h2_count' => $h2Count,
            'h3_count' => $h3Count,
            'image_count' => $imageCount,
            'images_with_alt' => $imageWithAlt,
            'issues' => $issues,
            'good' => $good
        ];
    }
    
    /**
     * Analyze URL/Slug
     */
    private function analyzeURL() {
        $url = $this->slug ?: $this->url;
        $length = mb_strlen($url);
        $hasKeyword = !empty($this->focusKeyword) && stripos($url, $this->focusKeyword) !== false;
        $isClean = preg_match('/^[a-z0-9\-]+$/', $url);
        
        $score = 0;
        $issues = [];
        $good = [];
        
        if ($length > 75) {
            $issues[] = "URL is too long (${length} characters). Keep it under 75 characters.";
        } else {
            $good[] = "URL length is good";
            $score += 3;
        }
        
        if ($isClean) {
            $good[] = "URL is clean and readable";
            $score += 2;
        } else {
            $issues[] = "URL contains special characters. Use only lowercase letters, numbers, and hyphens.";
        }
        
        if ($hasKeyword) {
            $good[] = "Focus keyword appears in the URL";
            $score += 2;
        }
        
        return [
            'score' => min(5, $score),
            'length' => $length,
            'has_keyword' => $hasKeyword,
            'is_clean' => $isClean,
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 5)
        ];
    }
    
    /**
     * Analyze internal linking
     */
    private function analyzeInternalLinking() {
        preg_match_all('/<a[^>]*href=["\']([^"\']+)["\'][^>]*>/i', $this->content, $matches);
        $links = $matches[1] ?? [];
        $internalLinks = 0;
        $externalLinks = 0;
        
        foreach ($links as $link) {
            if (preg_match('/^(https?:\/\/)?(www\.)?[^\/]+/i', $link)) {
                if (stripos($link, $_SERVER['HTTP_HOST'] ?? '') !== false) {
                    $internalLinks++;
                } else {
                    $externalLinks++;
                }
            } else {
                $internalLinks++;
            }
        }
        
        $score = 0;
        $issues = [];
        $good = [];
        
        if ($internalLinks === 0) {
            $issues[] = "No internal links found. Add links to related content.";
        } elseif ($internalLinks < 3) {
            $issues[] = "Few internal links (${internalLinks}). Consider adding more internal links.";
            $score += 5;
        } else {
            $good[] = "Good internal linking (${internalLinks} links)";
            $score += 10;
        }
        
        if ($externalLinks > 0) {
            $good[] = "External links found (${externalLinks})";
        }
        
        return [
            'score' => min(10, $score),
            'internal_links' => $internalLinks,
            'external_links' => $externalLinks,
            'total_links' => count($links),
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 10)
        ];
    }
    
    /**
     * Analyze images
     */
    private function analyzeImages() {
        preg_match_all('/<img[^>]*>/i', $this->content, $matches);
        $images = $matches[0] ?? [];
        $totalImages = count($images);
        
        $withAlt = 0;
        $withTitle = 0;
        $largeImages = 0;
        
        foreach ($images as $img) {
            if (preg_match('/alt=["\']([^"\']+)["\']/i', $img, $altMatch)) {
                if (!empty(trim($altMatch[1]))) {
                    $withAlt++;
                }
            }
            if (preg_match('/title=["\']([^"\']+)["\']/i', $img)) {
                $withTitle++;
            }
            if (preg_match('/width=["\'](\d+)["\']/i', $img, $widthMatch)) {
                if ((int)$widthMatch[1] > 1200) {
                    $largeImages++;
                }
            }
        }
        
        $score = 0;
        $issues = [];
        $good = [];
        
        if ($totalImages === 0) {
            $issues[] = "No images found. Consider adding relevant images.";
        } else {
            $good[] = "${totalImages} image(s) found";
            $score += 3;
            
            if ($withAlt === $totalImages) {
                $good[] = "All images have alt text";
                $score += 5;
            } else {
                $missing = $totalImages - $withAlt;
                $issues[] = "${missing} image(s) missing alt text";
            }
            
            if ($withTitle > 0) {
                $good[] = "Some images have title attributes";
                $score += 2;
            }
        }
        
        return [
            'score' => min(10, $score),
            'total' => $totalImages,
            'with_alt' => $withAlt,
            'with_title' => $withTitle,
            'large_images' => $largeImages,
            'issues' => $issues,
            'good' => $good,
            'status' => $this->getStatus($score, 10)
        ];
    }
    
    /**
     * Analyze schema markup
     */
    private function analyzeSchema() {
        $hasSchema = preg_match('/<script[^>]*type=["\']application\/ld\+json["\'][^>]*>/i', $this->content);
        
        return [
            'has_schema' => $hasSchema,
            'status' => $hasSchema ? 'good' : 'bad',
            'issues' => $hasSchema ? [] : ['No schema markup found. Consider adding structured data.'],
            'good' => $hasSchema ? ['Schema markup found'] : []
        ];
    }
    
    /**
     * Analyze social media tags
     */
    private function analyzeSocial() {
        $hasOgTitle = preg_match('/<meta[^>]*property=["\']og:title["\'][^>]*>/i', $this->content);
        $hasOgDesc = preg_match('/<meta[^>]*property=["\']og:description["\'][^>]*>/i', $this->content);
        $hasOgImage = preg_match('/<meta[^>]*property=["\']og:image["\'][^>]*>/i', $this->content);
        $hasTwitterCard = preg_match('/<meta[^>]*name=["\']twitter:card["\'][^>]*>/i', $this->content);
        
        $hasOgTags = $hasOgTitle && $hasOgDesc && $hasOgImage;
        $hasTwitterTags = $hasTwitterCard;
        
        $issues = [];
        $good = [];
        
        if (!$hasOgTags) {
            $issues[] = "Open Graph tags missing or incomplete";
        } else {
            $good[] = "Open Graph tags found";
        }
        
        if (!$hasTwitterTags) {
            $issues[] = "Twitter Card tags missing";
        } else {
            $good[] = "Twitter Card tags found";
        }
        
        return [
            'has_og_tags' => $hasOgTags,
            'has_twitter_tags' => $hasTwitterTags,
            'issues' => $issues,
            'good' => $good,
            'status' => ($hasOgTags && $hasTwitterTags) ? 'good' : 'bad'
        ];
    }
    
    /**
     * Get recommendations
     */
    private function getRecommendations() {
        $recommendations = [];
        $analysis = $this->analyze();
        
        // Collect all issues
        if (!empty($analysis['title_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['title_analysis']['issues']);
        }
        if (!empty($analysis['meta_description_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['meta_description_analysis']['issues']);
        }
        if (!empty($analysis['focus_keyword_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['focus_keyword_analysis']['issues']);
        }
        if (!empty($analysis['content_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['content_analysis']['issues']);
        }
        if (!empty($analysis['url_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['url_analysis']['issues']);
        }
        if (!empty($analysis['image_analysis']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['image_analysis']['issues']);
        }
        if (!empty($analysis['internal_linking']['issues'])) {
            $recommendations = array_merge($recommendations, $analysis['internal_linking']['issues']);
        }
        
        return array_unique($recommendations);
    }
    
    /**
     * Get overall status (good/warning/bad)
     */
    private function getOverallStatus() {
        $seoScore = $this->calculateSEOScore();
        
        if ($seoScore >= 80) {
            return 'good';
        } elseif ($seoScore >= 50) {
            return 'warning';
        } else {
            return 'bad';
        }
    }
    
    /**
     * Get status based on score
     */
    private function getStatus($score, $maxScore) {
        $percentage = ($score / $maxScore) * 100;
        if ($percentage >= 80) {
            return 'good';
        } elseif ($percentage >= 50) {
            return 'warning';
        } else {
            return 'bad';
        }
    }
    
    /**
     * Check if keyword appears in first paragraph
     */
    private function checkKeywordInFirstParagraph($text, $keyword) {
        preg_match('/<p[^>]*>(.*?)<\/p>/i', $text, $match);
        if (isset($match[1])) {
            return stripos(strip_tags($match[1]), $keyword) !== false;
        }
        $words = explode(' ', strip_tags($text));
        $first100 = implode(' ', array_slice($words, 0, 100));
        return stripos($first100, $keyword) !== false;
    }
    
    /**
     * Check if keyword appears in H1
     */
    private function checkKeywordInH1() {
        if (empty($this->focusKeyword)) return false;
        preg_match('/<h1[^>]*>(.*?)<\/h1>/i', $this->content, $match);
        if (isset($match[1])) {
            return stripos(strip_tags($match[1]), $this->focusKeyword) !== false;
        }
        return false;
    }
    
    /**
     * Check if keyword appears in subheadings
     */
    private function checkKeywordInSubheadings($keyword) {
        preg_match_all('/<h[2-6][^>]*>(.*?)<\/h[2-6]>/i', $this->content, $matches);
        if (empty($matches[1])) return false;
        
        foreach ($matches[1] as $heading) {
            if (stripos(strip_tags($heading), $keyword) !== false) {
                return true;
            }
        }
        return false;
    }
}
