<?php

namespace App\Services;

class SeoScoringService
{
    /**
     * Calculate SEO score for a blog post
     * Aligned with Google (2026 Helpful Content + EEAT), Bing Webmaster, Yandex algorithms
     *
     * @return array{score: int, max_score: int, percentage: int, grade: string, suggestions: array, breakdown: array, meta_suggestions: array}
     */
    public function calculateScore(
        string $title,
        string $excerpt,
        string $content,
        ?string $metaTitle = null,
        ?string $metaDescription = null,
        ?string $slug = null
    ): array {
        $score    = 0;
        $maxScore = 0;
        $breakdown    = [];
        $suggestions  = [];

        // Clean content from HTML tags for text analysis
        $cleanContent = strip_tags($content);

        // 1. Title Analysis – Google/Bing/Yandex (max 15)
        $titleScore = $this->scoreTitleSeo($title, $suggestions);
        $score     += $titleScore;
        $maxScore  += 15;
        $breakdown['title'] = ['score' => $titleScore, 'max' => 15, 'label' => 'Başlık (H1)'];

        // 2. Meta Title – SERP snippet (max 10)
        $metaTitleScore = $this->scoreMetaTitle($metaTitle ?? $title, $suggestions);
        $score     += $metaTitleScore;
        $maxScore  += 10;
        $breakdown['meta_title'] = ['score' => $metaTitleScore, 'max' => 10, 'label' => 'Meta Başlığı'];

        // 3. Meta Description – SERP CTR (max 10)
        $metaDescScore = $this->scoreMetaDescription($metaDescription ?? $excerpt, $suggestions);
        $score     += $metaDescScore;
        $maxScore  += 10;
        $breakdown['meta_description'] = ['score' => $metaDescScore, 'max' => 10, 'label' => 'Meta Açıklaması'];

        // 4. Content Length – Google Helpful Content (max 15)
        $contentLengthScore = $this->scoreContentLength($cleanContent, $suggestions);
        $score     += $contentLengthScore;
        $maxScore  += 15;
        $breakdown['content_length'] = ['score' => $contentLengthScore, 'max' => 15, 'label' => 'İçerik Uzunluğu'];

        // 5. Keyword Optimization – Density & Placement (max 15)
        $keywordScore = $this->scoreKeywordOptimization($title, $cleanContent, $suggestions);
        $score     += $keywordScore;
        $maxScore  += 15;
        $breakdown['keywords'] = ['score' => $keywordScore, 'max' => 15, 'label' => 'Anahtar Kelime'];

        // 6. Heading Structure – H1/H2/H3 hierarchy (max 10)
        $headingScore = $this->scoreHeadingStructure($content, $suggestions);
        $score     += $headingScore;
        $maxScore  += 10;
        $breakdown['headings'] = ['score' => $headingScore, 'max' => 10, 'label' => 'Başlık Yapısı'];

        // 7. Readability – Flesch-Kincaid adapted (max 10)
        $readabilityScore = $this->scoreReadability($cleanContent, $suggestions);
        $score     += $readabilityScore;
        $maxScore  += 10;
        $breakdown['readability'] = ['score' => $readabilityScore, 'max' => 10, 'label' => 'Okunabilirlik'];

        // 8. Excerpt / Description Quality (max 5)
        $excerptScore = $this->scoreExcerpt($excerpt, $suggestions);
        $score     += $excerptScore;
        $maxScore  += 5;
        $breakdown['excerpt'] = ['score' => $excerptScore, 'max' => 5, 'label' => 'Özet'];

        // 9. Internal Link Signals – 2026 Bing/Google (max 5)
        $linkScore = $this->scoreLinkStructure($content, $suggestions);
        $score     += $linkScore;
        $maxScore  += 5;
        $breakdown['links'] = ['score' => $linkScore, 'max' => 5, 'label' => 'Bağlantılar'];

        // 10. Image Alt / Structured Content (max 5)
        $imageScore = $this->scoreImageAlt($content, $suggestions);
        $score     += $imageScore;
        $maxScore  += 5;
        $breakdown['images'] = ['score' => $imageScore, 'max' => 5, 'label' => 'Görseller'];

        $percentage = $maxScore > 0 ? (int) round(($score / $maxScore) * 100) : 0;

        return [
            'score'            => $score,
            'max_score'        => $maxScore,
            'percentage'       => $percentage,
            'grade'            => $this->getGrade($percentage),
            'suggestions'      => $suggestions,
            'breakdown'        => $breakdown,
            'meta_suggestions' => $this->generateMetaSuggestions($title, $cleanContent, $excerpt),
        ];
    }

    // ─── Individual Scorers ─────────────────────────────────────────────

    protected function scoreTitleSeo(string $title, array &$suggestions): int
    {
        $score    = 0;
        $length   = mb_strlen($title);
        $wordCount = str_word_count($title);

        // Google SERP: 50-60 chars ideal; Bing tolerates up to 65
        if ($length >= 50 && $length <= 60) {
            $score += 8;
        } elseif ($length >= 35 && $length <= 65) {
            $score += 5;
            $suggestions[] = [
                'type' => 'title', 'level' => 'info',
                'message' => "Başlık $length karakter. Google SERP'i için 50-60 karakter ideal.",
            ];
        } else {
            $score += 2;
            $suggestions[] = [
                'type' => 'title', 'level' => 'warning',
                'message' => "Başlık $length karakter. Kısa/uzun başlıklar SERP'te kesilir (50-60 karakter önerilir).",
            ];
        }

        // Word count
        if ($wordCount >= 3 && $wordCount <= 9) {
            $score += 5;
        } else {
            $score += 2;
            $suggestions[] = [
                'type' => 'title', 'level' => 'info',
                'message' => "Başlıkta $wordCount kelime var. 3-9 kelime arası ideal.",
            ];
        }

        // Power words / CTA – CTR signal
        if ($this->containsPowerWords($title)) {
            $score += 2;
        } else {
            $suggestions[] = [
                'type' => 'title', 'level' => 'info',
                'message' => 'Başlıkta dikkat çekici kelimeler (güçlü kelimeler) kullanmak CTR\'yi artırır.',
            ];
        }

        return min($score, 15);
    }

    protected function scoreMetaTitle(string $metaTitle, array &$suggestions): int
    {
        $length = mb_strlen($metaTitle);

        // Google: ~580px ≈ 60 chars; Yandex: 70 chars
        if ($length >= 50 && $length <= 60) {
            return 10;
        } elseif ($length >= 30 && $length <= 70) {
            $suggestions[] = [
                'type' => 'meta_title', 'level' => 'warning',
                'message' => "Meta başlık $length karakter. Google için 50-60 karakter önerilir.",
            ];
            return 6;
        } elseif ($length > 0) {
            $suggestions[] = [
                'type' => 'meta_title', 'level' => 'error',
                'message' => "Meta başlık $length karakter – çok kısa veya çok uzun (50-60 karakter olmalı).",
            ];
            return 3;
        } else {
            $suggestions[] = [
                'type' => 'meta_title', 'level' => 'error',
                'message' => 'Meta başlığı boş. Google & Bing SERP için zorunludur.',
            ];
            return 0;
        }
    }

    protected function scoreMetaDescription(string $metaDescription, array &$suggestions): int
    {
        $length = mb_strlen($metaDescription);

        // Google: 155-160; Bing: 150-160; Yandex: 200
        if ($length >= 140 && $length <= 165) {
            return 10;
        } elseif ($length >= 100 && $length <= 200) {
            $suggestions[] = [
                'type' => 'meta_description', 'level' => 'warning',
                'message' => "Meta açıklama $length karakter. 140-165 karakter aralığı Google/Bing için idealdir.",
            ];
            return 6;
        } elseif ($length > 0) {
            $suggestions[] = [
                'type' => 'meta_description', 'level' => 'error',
                'message' => "Meta açıklama $length karakter – çok kısa veya çok uzun. 140-165 karakter olmalı.",
            ];
            return 2;
        } else {
            $suggestions[] = [
                'type' => 'meta_description', 'level' => 'error',
                'message' => 'Meta açıklama boş. SERP CTR için zorunludur.',
            ];
            return 0;
        }
    }

    protected function scoreContentLength(string $cleanContent, array &$suggestions): int
    {
        $wordCount = str_word_count($cleanContent);

        // Google 2026 Helpful Content: 1500+ words for in-depth, 600+ for basic
        if ($wordCount >= 1500) {
            return 15;
        } elseif ($wordCount >= 800) {
            $suggestions[] = [
                'type' => 'content_length', 'level' => 'info',
                'message' => "İçerik $wordCount kelime. Google 2026 için 1500+ kelime derinlikli içerik olarak değerlendiriliyor.",
            ];
            return 12;
        } elseif ($wordCount >= 400) {
            $suggestions[] = [
                'type' => 'content_length', 'level' => 'warning',
                'message' => "İçerik $wordCount kelime. En az 800 kelime önerilir (Google Helpful Content Update).",
            ];
            return 7;
        } else {
            $suggestions[] = [
                'type' => 'content_length', 'level' => 'error',
                'message' => "İçerik çok kısa ($wordCount kelime). Google thin content olarak işaretleyebilir.",
            ];
            return 2;
        }
    }

    protected function scoreKeywordOptimization(string $title, string $cleanContent, array &$suggestions): int
    {
        $score = 0;

        // Extract main keyword candidates from title
        $stopWords = ['ve', 'ile', 'bu', 'bir', 'de', 'da', 'için', 'olan', 'ya', 'ya da', 'veya', 'the', 'a', 'an', 'of', 'in', 'for', 'on', 'with'];
        $words = preg_split('/\s+/', strtolower($title), -1, PREG_SPLIT_NO_EMPTY);
        $keywords = array_filter($words, fn($w) => mb_strlen($w) > 3 && !in_array($w, $stopWords));

        if (empty($keywords)) {
            $suggestions[] = [
                'type' => 'keywords', 'level' => 'error',
                'message' => 'Başlıktan anahtar kelime çıkarılamadı.',
            ];
            return 3;
        }

        $mainKeyword = reset($keywords);
        $contentLower = strtolower($cleanContent);
        $wordCount = max(1, str_word_count($cleanContent));
        $keywordCount = substr_count($contentLower, $mainKeyword);
        $density = ($keywordCount / $wordCount) * 100;

        // Keyword in first paragraph (Google E-E-A-T signal)
        $firstParagraph = mb_substr($cleanContent, 0, 300);
        if (stripos($firstParagraph, $mainKeyword) !== false) {
            $score += 5;
        } else {
            $suggestions[] = [
                'type' => 'keywords', 'level' => 'warning',
                'message' => "\"$mainKeyword\" anahtar kelimesi ilk paragrafta bulunamadı. Erken kullanım Google için önemli.",
            ];
            $score += 1;
        }

        // Keyword density: 1-2.5% is safe for 2026
        if ($density >= 1.0 && $density <= 2.5) {
            $score += 10;
        } elseif ($density >= 0.5 && $density <= 4.0) {
            $suggestions[] = [
                'type' => 'keywords', 'level' => 'warning',
                'message' => sprintf('"' . $mainKeyword . '" yoğunluğu %.1f%%. 1-2.5%% arasında kalın (keyword stuffing riski).', $density),
            ];
            $score += 6;
        } else {
            $suggestions[] = [
                'type' => 'keywords', 'level' => 'error',
                'message' => sprintf('"' . $mainKeyword . '" yoğunluğu %.1f%% – içerikte yeterince kullanılmıyor.', $density),
            ];
            $score += 2;
        }

        return min($score, 15);
    }

    protected function scoreHeadingStructure(string $content, array &$suggestions): int
    {
        $h1Count = substr_count(strtolower($content), '<h1');
        $h2Count = substr_count(strtolower($content), '<h2');
        $h3Count = substr_count(strtolower($content), '<h3');
        $score   = 0;

        // H1 – only one (Google doc structure)
        if ($h1Count === 1) {
            $score += 4;
        } elseif ($h1Count === 0) {
            $suggestions[] = [
                'type' => 'headings', 'level' => 'error',
                'message' => 'H1 başlık eksik. Google/Bing/Yandex dokümanda tam olarak 1 H1 bekler.',
            ];
            $score += 0;
        } else {
            $suggestions[] = [
                'type' => 'headings', 'level' => 'warning',
                'message' => "$h1Count H1 başlık bulundu – yalnızca 1 adet olmalı.",
            ];
            $score += 2;
        }

        // H2 subheadings
        if ($h2Count >= 2) {
            $score += 4;
        } elseif ($h2Count === 1) {
            $score += 2;
            $suggestions[] = [
                'type' => 'headings', 'level' => 'info',
                'message' => 'En az 2 H2 alt başlık kullanmak içerik yapısını güçlendirir.',
            ];
        } else {
            $suggestions[] = [
                'type' => 'headings', 'level' => 'warning',
                'message' => 'H2 başlık yok. Alt başlıklar hem okuyucu deneyimini hem de SEO\'yu iyileştirir.',
            ];
        }

        // H3 for 2026 hierarchy
        if ($h3Count >= 1) {
            $score += 2;
        } else {
            $suggestions[] = [
                'type' => 'headings', 'level' => 'info',
                'message' => 'H3 başlıklar eklersek başlık hiyerarşisi daha zengin olur (Bing Webmaster öneriyor).',
            ];
        }

        return min($score, 10);
    }

    protected function scoreReadability(string $cleanContent, array &$suggestions): int
    {
        if (empty(trim($cleanContent))) {
            return 1;
        }

        $score = 4; // base

        $wordCount = max(1, str_word_count($cleanContent));
        $sentences = preg_split('/[.!?]+/u', $cleanContent, -1, PREG_SPLIT_NO_EMPTY);
        $sentenceCount = max(1, count($sentences));
        $avgWordsPerSentence = $wordCount / $sentenceCount;

        // Google 2026 Core Web readability signal: 15-20 words/sentence
        if ($avgWordsPerSentence >= 10 && $avgWordsPerSentence <= 20) {
            $score += 4;
        } elseif ($avgWordsPerSentence > 0) {
            $score += 2;
            $suggestions[] = [
                'type' => 'readability', 'level' => 'warning',
                'message' => sprintf('Cümleler ortalama %d kelime. 10-20 kelime/cümle okunabilirliği artırır.', (int) $avgWordsPerSentence),
            ];
        }

        // Passive voice rough check (Turkish: "-dı/-di/-miş" patterns mixed with olunan/yapılan)
        $passiveCount = preg_match_all('/\b(edildi|yapıldı|verildi|alındı|getirildi|kaldırıldı)\b/u', $cleanContent);
        if ($passiveCount > $sentenceCount * 0.3) {
            $suggestions[] = [
                'type' => 'readability', 'level' => 'info',
                'message' => 'Pasif yapı çok fazla kullanılmış. Aktif cümleler okunabilirliği artırır.',
            ];
            $score -= 1;
        }

        // Paragraph length check
        $paragraphs = preg_split('/\n{2,}/', $cleanContent);
        $longParagraphs = array_filter($paragraphs, fn($p) => str_word_count($p) > 100);
        if (count($longParagraphs) > 0) {
            $suggestions[] = [
                'type' => 'readability', 'level' => 'info',
                'message' => count($longParagraphs) . ' paragraf 100 kelimeden uzun. Paragrafları bölerek okuyucu deneyimini iyileştirin.',
            ];
            $score -= 1;
        }

        return max(1, min($score, 10));
    }

    protected function scoreExcerpt(string $excerpt, array &$suggestions): int
    {
        $length = mb_strlen($excerpt);

        if ($length >= 120 && $length <= 160) {
            return 5;
        } elseif ($length >= 60 && $length <= 200) {
            $suggestions[] = [
                'type' => 'excerpt', 'level' => 'info',
                'message' => "Özet $length karakter. 120-160 karakter SERP snippet ile uyumlu olur.",
            ];
            return 3;
        } elseif ($length > 0) {
            $suggestions[] = [
                'type' => 'excerpt', 'level' => 'warning',
                'message' => "Özet çok kısa veya çok uzun ($length karakter). 120-160 karakter hedefleyin.",
            ];
            return 2;
        } else {
            $suggestions[] = [
                'type' => 'excerpt', 'level' => 'warning',
                'message' => 'Özet boş. Blog listelerinde ve sosyal medya paylaşımlarında görünür.',
            ];
            return 0;
        }
    }

    protected function scoreLinkStructure(string $content, array &$suggestions): int
    {
        $internalLinks = preg_match_all('/<a\s[^>]*href=["\'][^"\']*["\']/i', $content);
        $externalLinks = preg_match_all('/<a\s[^>]*href=["\']https?:\/\//i', $content);

        $score = 0;

        if ($internalLinks >= 2) {
            $score += 3;
        } elseif ($internalLinks >= 1) {
            $score += 2;
            $suggestions[] = [
                'type' => 'links', 'level' => 'info',
                'message' => 'İç bağlantı sayısını artırın (2-5 önerilir). Site içi linkleme Google sıralamasını güçlendirir.',
            ];
        } else {
            $suggestions[] = [
                'type' => 'links', 'level' => 'warning',
                'message' => 'İç bağlantı yok. Google & Bing için site içi linkleme çok önemlidir.',
            ];
        }

        if ($externalLinks >= 1) {
            $score += 2;
        } else {
            $suggestions[] = [
                'type' => 'links', 'level' => 'info',
                'message' => 'Dış kaynak bağlantısı yok. Güvenilir kaynaklara link vermek E-E-A-T skorunu artırır.',
            ];
        }

        return min($score, 5);
    }

    protected function scoreImageAlt(string $content, array &$suggestions): int
    {
        $imgCount = preg_match_all('/<img\s/i', $content);
        $altCount = preg_match_all('/<img\s[^>]*alt=["\'][^"\']+["\']/i', $content);

        if ($imgCount === 0) {
            $suggestions[] = [
                'type' => 'images', 'level' => 'warning',
                'message' => 'Görseller bulunamadı. İçeriğe en az 1 görsel eklemek SERP görsel arama sıralamalarını destekler.',
            ];
            return 1;
        }

        if ($imgCount > 0 && $altCount === $imgCount) {
            return 5;
        } elseif ($altCount > 0) {
            $missing = $imgCount - $altCount;
            $suggestions[] = [
                'type' => 'images', 'level' => 'warning',
                'message' => "$missing görselde alt metni eksik. Alt metin Google Görsel Arama için zorunludur.",
            ];
            return 3;
        } else {
            $suggestions[] = [
                'type' => 'images', 'level' => 'error',
                'message' => 'Görsellerde alt metni yok. Google & Bing için tüm görsellere açıklayıcı alt metni ekleyin.',
            ];
            return 1;
        }
    }

    // ─── Meta Auto-Suggestions ──────────────────────────────────────────

    /**
     * Generate auto-suggestions for meta title and meta description
     */
    public function generateMetaSuggestions(string $title, string $cleanContent, string $excerpt): array
    {
        // Meta Title suggestion: trim to 55 chars
        $metaTitleSuggestion = mb_strlen($title) <= 60
            ? $title
            : mb_substr($title, 0, 57) . '...';

        // Meta Description: prefer excerpt, then first 155 chars of content
        if (!empty($excerpt)) {
            $raw = $excerpt;
        } elseif (!empty($cleanContent)) {
            $raw = $cleanContent;
        } else {
            $raw = $title;
        }

        // Clean and trim
        $raw = preg_replace('/\s+/', ' ', trim($raw));
        if (mb_strlen($raw) > 155) {
            // Trim at last word boundary
            $trimmed = mb_substr($raw, 0, 152);
            $lastSpace = mb_strrpos($trimmed, ' ');
            $metaDescSuggestion = ($lastSpace !== false ? mb_substr($trimmed, 0, $lastSpace) : $trimmed) . '...';
        } else {
            $metaDescSuggestion = $raw;
        }

        return [
            'meta_title'       => $metaTitleSuggestion,
            'meta_description' => $metaDescSuggestion,
        ];
    }

    // ─── Helpers ────────────────────────────────────────────────────────

    protected function containsPowerWords(string $text): bool
    {
        $powerWords = [
            // Turkish
            'en iyi', 'harika', 'müthiş', 'inanılmaz', 'eksiksiz',
            'güvenilir', 'hızlı', 'kolay', 'basit', 'etkili',
            'kanıtlanmış', 'gizli', 'başarılı', 'yöntemi', 'kapsamlı',
            'kesin', 'özel', 'tam rehber', 'adım adım', 'nasıl yapılır',
            'ipucu', 'hata', 'dikkat',
        ];

        $textLower = mb_strtolower($text);
        foreach ($powerWords as $word) {
            if (mb_strpos($textLower, $word) !== false) {
                return true;
            }
        }

        return false;
    }

    protected function getGrade(int $percentage): string
    {
        return match (true) {
            $percentage >= 90 => 'A',
            $percentage >= 80 => 'B',
            $percentage >= 70 => 'C',
            $percentage >= 60 => 'D',
            $percentage >= 50 => 'E',
            default           => 'F',
        };
    }
}
