<?php
namespace CAE\Modules\QAEngine;

class QAEngine
{
    public function check(string $content, array $project, array $topic): array
    {
        $wordCount   = $this->countWords($content);
        $h1Count     = $this->countH1($content);
        $h2Count     = substr_count(strtolower($content), '<h2');
        $density     = $this->keywordDensity($content, $topic['primary_keyword'] ?? '');
        $targetWords = (int)($topic['target_word_count'] ?? 800);
        $minWords    = (int)($targetWords * 0.7);
        $keyword     = strtolower($topic['primary_keyword'] ?? '');

        $readability    = $this->fleschKincaid($content);
        $avgSentLen     = $this->avgSentenceLength($content);
        $paraCount      = $this->countParagraphs($content);
        $uniqueRatio    = $this->uniqueWordRatio($content);
        $plainLen       = strlen(strip_tags($content));

        $checks = [];
        $issues = [];

        // 1 — Word count
        $wcPass = $wordCount >= $minWords;
        if (!$wcPass) $issues[] = "Word count too low: {$wordCount} words (minimum {$minWords})";
        $checks[] = [
            'name'     => 'Word Count',
            'icon'     => 'file-word',
            'required' => "≥ {$minWords} words (70% of {$targetWords} target)",
            'actual'   => number_format($wordCount) . ' words',
            'passed'   => $wcPass,
        ];

        // 2 — H1
        $h1Pass = $h1Count === 1;
        if ($h1Count === 0) $issues[] = 'Missing H1 heading';
        elseif ($h1Count > 1) $issues[] = "Multiple H1 headings: {$h1Count} found (only 1 allowed)";
        $checks[] = [
            'name'     => 'H1 Heading',
            'icon'     => 'type-h1',
            'required' => 'Exactly 1 H1',
            'actual'   => $h1Count . ' found',
            'passed'   => $h1Pass,
        ];

        // 3 — H2 structure
        $h2Pass = $h2Count >= 2;
        if (!$h2Pass) $issues[] = "Too few H2 headings: {$h2Count} (minimum 2)";
        $checks[] = [
            'name'     => 'H2 Structure',
            'icon'     => 'type-h2',
            'required' => 'Minimum 2 H2 sections',
            'actual'   => $h2Count . ' H2s found',
            'passed'   => $h2Pass,
        ];

        // 4 — Keyword present
        $kwPresent = $keyword ? (stripos($content, $keyword) !== false) : true;
        if (!$kwPresent) $issues[] = "Primary keyword \"{$keyword}\" not found in content";
        $checks[] = [
            'name'     => 'Keyword Present',
            'icon'     => 'key',
            'required' => 'Primary keyword must appear',
            'actual'   => $kwPresent ? 'Found ✓' : 'Not found',
            'passed'   => $kwPresent,
        ];

        // 5 — Keyword density
        $kdPass = (!$keyword) || ($density >= 0.3 && $density <= 3.5);
        if ($keyword && $density > 3.5) $issues[] = "Keyword density too high: {$density}% (max 3.5%)";
        elseif ($keyword && $density < 0.3) $issues[] = "Keyword density too low: {$density}% (min 0.3%)";
        $checks[] = [
            'name'     => 'Keyword Density',
            'icon'     => 'percent',
            'required' => '0.3% – 3.5%',
            'actual'   => $density . '%',
            'passed'   => $kdPass,
        ];

        // 6 — Banned words
        $noNoWords   = $project['no_no_words'] ?? '';
        $bannedFound = [];
        if ($noNoWords) {
            foreach (array_filter(array_map('trim', preg_split('/[,\n]+/', $noNoWords))) as $word) {
                if ($word && stripos($content, $word) !== false) {
                    $bannedFound[] = $word;
                    $issues[]      = "Banned word/phrase found: \"{$word}\"";
                }
            }
        }
        $banPass = empty($bannedFound);
        $checks[] = [
            'name'     => 'Banned Words',
            'icon'     => 'slash-circle',
            'required' => 'None from no-no list',
            'actual'   => $banPass ? 'None found ✓' : implode(', ', $bannedFound),
            'passed'   => $banPass,
        ];

        // 7 — Content not empty
        $notEmpty = $plainLen >= 200;
        if (!$notEmpty) $issues[] = 'Content too short after stripping HTML';
        $checks[] = [
            'name'     => 'Content Not Empty',
            'icon'     => 'file-earmark-text',
            'required' => 'Min 200 characters',
            'actual'   => number_format($plainLen) . ' chars',
            'passed'   => $notEmpty,
        ];

        // 8 — No "Conclusion" heading (hard check — triggers retry)
        $hasConclusion = (bool) preg_match('/<h[1-6][^>]*>\s*Conclusion\s*<\/h[1-6]>/i', $content);
        if ($hasConclusion) $issues[] = 'Section heading "Conclusion" found — replace with a specific, engaging heading';
        $checks[] = [
            'name'     => 'No "Conclusion" Heading',
            'icon'     => 'chat-left-text',
            'required' => 'Ending should be natural CTA, not "Conclusion"',
            'actual'   => $hasConclusion ? '⚠ "Conclusion" heading found' : 'Natural ending ✓',
            'passed'   => !$hasConclusion,
        ];

        // 9 — Readability (hard check) — target 7th–10th grade = Flesch ≥ 60
        $readLabel = $readability >= 70 ? 'Easy (6–7th grade)' : ($readability >= 60 ? 'Standard (8–9th grade)' : ($readability >= 50 ? 'Fairly Difficult (10–12th grade)' : 'Too Complex'));
        $readPass  = $readability >= 60;
        if (!$readPass) $issues[] = "Readability too low: {$readability}/100 (minimum 60 — 7th–10th grade level). Use shorter sentences (under 18 words) and simpler everyday vocabulary.";
        $checks[] = [
            'name'     => 'Readability (Flesch)',
            'icon'     => 'book',
            'required' => 'Score ≥ 60 (7th–10th grade level)',
            'actual'   => $readability . ' / 100 — ' . $readLabel,
            'passed'   => $readPass,
        ];

        // 10 — AI Content Score (hard check)
        $aiScore   = $this->aiContentScore($content);
        $aiPass    = $aiScore >= 55;
        if (!$aiPass) $issues[] = "AI content score too low: {$aiScore}/100 (minimum 55). Vary sentence lengths, use specific examples, remove generic transition phrases.";
        $checks[] = [
            'name'     => 'AI Content Score',
            'icon'     => 'robot',
            'required' => 'Score ≥ 55 / 100',
            'actual'   => $aiScore . ' / 100',
            'passed'   => $aiPass,
        ];

        // — Informational metrics (don't block QA pass)
        $sentOk = $avgSentLen >= 10 && $avgSentLen <= 25;
        $checks[] = [
            'name'     => 'Avg Sentence Length',
            'icon'     => 'text-paragraph',
            'required' => '10 – 25 words',
            'actual'   => $avgSentLen . ' words/sentence',
            'passed'   => $sentOk,
            'info'     => true,
        ];

        $checks[] = [
            'name'     => 'Vocabulary Richness',
            'icon'     => 'alphabet',
            'required' => '≥ 40% unique words',
            'actual'   => $uniqueRatio . '% unique',
            'passed'   => $uniqueRatio >= 40,
            'info'     => true,
        ];

        return [
            'passed'              => empty($issues),
            'issues'              => $issues,
            'checks'              => $checks,
            'word_count'          => $wordCount,
            'keyword_density'     => $density,
            'h1_count'            => $h1Count,
            'readability_score'   => $readability,
            'avg_sentence_length' => $avgSentLen,
            'paragraph_count'     => $paraCount,
            'unique_word_ratio'   => $uniqueRatio,
        ];
    }

    // ── Readability ──────────────────────────────────────────────

    private function fleschKincaid(string $content): float
    {
        $text      = preg_replace('/\s+/', ' ', trim(strip_tags($content)));
        $sentences = array_filter(
            preg_split('/[.!?]+/', $text, -1, PREG_SPLIT_NO_EMPTY),
            fn($s) => strlen(trim($s)) > 5
        );
        $sentCount = max(1, count($sentences));
        $words     = preg_split('/\s+/', $text, -1, PREG_SPLIT_NO_EMPTY);
        $wordCount = max(1, count($words));
        $syllables = array_sum(array_map([$this, 'countSyllables'], $words));
        $score     = 206.835 - (1.015 * ($wordCount / $sentCount)) - (84.6 * ($syllables / $wordCount));
        return (float) max(0, min(100, round($score, 1)));
    }

    private function countSyllables(string $word): int
    {
        $word = strtolower(preg_replace('/[^a-z]/i', '', $word));
        if (strlen($word) <= 3) return 1;
        $word  = preg_replace('/(?:[^laeiouy]es|ed|[^laeiouy]e)$/', '', $word);
        $word  = preg_replace('/^y/', '', $word);
        $count = (int) preg_match_all('/[aeiouy]{1,2}/', $word);
        return max(1, $count);
    }

    private function avgSentenceLength(string $content): float
    {
        $text      = strip_tags($content);
        $sentences = array_filter(
            preg_split('/[.!?]+/', $text, -1, PREG_SPLIT_NO_EMPTY),
            fn($s) => strlen(trim($s)) > 10
        );
        if (empty($sentences)) return 0.0;
        $total = array_sum(array_map(fn($s) => str_word_count(trim($s)), $sentences));
        return (float) round($total / count($sentences), 1);
    }

    private function countParagraphs(string $content): int
    {
        return max(1, substr_count($content, '<p'));
    }

    private function uniqueWordRatio(string $content): float
    {
        $words = preg_split('/[\s,!?.;:]+/', strtolower(strip_tags($content)), -1, PREG_SPLIT_NO_EMPTY);
        if (empty($words)) return 0.0;
        return (float) round(count(array_unique($words)) / count($words) * 100, 1);
    }

    // ── Core Counts ──────────────────────────────────────────────

    private function countWords(string $content): int
    {
        $text = preg_replace('/\s+/', ' ', trim(strip_tags($content)));
        return empty($text) ? 0 : str_word_count($text);
    }

    private function countH1(string $content): int
    {
        return (int) preg_match_all('/<h1[\s>]/i', $content);
    }

    private function keywordDensity(string $content, string $keyword): float
    {
        if (empty($keyword)) return 0.0;
        $text       = strtolower(strip_tags($content));
        $totalWords = str_word_count($text);
        if ($totalWords === 0) return 0.0;
        $kwWords     = str_word_count(strtolower($keyword));
        $occurrences = substr_count($text, strtolower($keyword));
        return round(($occurrences * $kwWords / $totalWords) * 100, 2);
    }

    private function aiContentScore(string $content): int
    {
        $text      = strip_tags($content);
        $sentences = array_values(array_filter(
            preg_split('/(?<=[.!?])\s+/', $text, -1, PREG_SPLIT_NO_EMPTY),
            fn($s) => str_word_count(trim($s)) >= 3
        ));
        if (count($sentences) < 3) return 70;

        // Sub-score 1: sentence length variety (coefficient of variation)
        $lengths = array_map(fn($s) => str_word_count(trim($s)), $sentences);
        $mean    = array_sum($lengths) / count($lengths);
        $variance = array_sum(array_map(fn($l) => ($l - $mean) ** 2, $lengths)) / count($lengths);
        $cv       = $mean > 0 ? sqrt($variance) / $mean : 0;
        $varietyScore = min(100, (int)($cv * 200));

        // Sub-score 2: unique sentence-starter diversity
        $starters = array_map(fn($s) => strtolower(preg_split('/\s+/', trim($s))[0] ?? ''), $sentences);
        $starterScore = count($sentences) > 0
            ? (int)(count(array_unique($starters)) / count($sentences) * 100)
            : 100;

        // Sub-score 3: absence of AI filler phrases
        $fillers = [
            'it is important to note', "it's worth noting", 'it is worth noting',
            'in conclusion', 'in summary', 'to summarize', 'as we have seen',
            'in today\'s', 'in the realm of', 'the world of', 'dive into',
            'delve into', 'unlock the', 'game-changer', 'game changer',
            'revolutionize', 'it is crucial', 'it is essential to',
            'needless to say', 'as mentioned earlier', 'as previously mentioned',
        ];
        $fillerCount = 0;
        $lower = strtolower($text);
        foreach ($fillers as $f) {
            $fillerCount += substr_count($lower, $f);
        }
        $fillerScore = max(0, 100 - $fillerCount * 12);

        return (int)round(($varietyScore + $starterScore + $fillerScore) / 3);
    }
}
