<?php
/**
 * AIArticleGenerator — single source of the AA-19 SEO article generation
 *
 * Usage:
 *   $gen = new AIArticleGenerator($geminiKey, $openaiKey);
 *   $article = $gen->generate($topic, $primary, $secondary[], $referenceLink, $title, $lsi[]);
 *   // returns the parsed article array, or false on failure (caller decides what to do).
 */
class AIArticleGenerator
{
    private $geminiKey;
    private $openaiKey;
    private $geminiUrl = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent';
    private $openaiUrl = 'https://api.openai.com/v1/responses';

    public function __construct($geminiKey, $openaiKey)
    {
        $this->geminiKey = $geminiKey;
        $this->openaiKey = $openaiKey;
    }

    /** Public entry. Returns parsed article array, or false on failure. */
    public function generate($topic, $primaryKeyword, array $secondaryKeywords = array(), $referenceLink = '', $title = '', array $lsiKeywords = array())
    {
        return $this->callAI($this->buildSeoArticlePrompt($topic, $primaryKeyword, $secondaryKeywords, $referenceLink, $title, $lsiKeywords));
    }

    /* gpt-5.5 (default) -> Gemini (fallback) */
    private function callAI($prompt)
    {
        $resp = $this->callOpenAI($prompt);
        if ($resp !== false) {
            return $resp;
        }
        return $this->callGemini($prompt);
    }

    private function callOpenAI($prompt)
    {
        $payload = array('model' => 'gpt-5.5', 'input' => $prompt, 'text' => array('format' => array('type' => 'json_object')));
        $r = $this->httpPost($this->openaiUrl, array('Content-Type: application/json', 'Authorization: Bearer ' . $this->openaiKey), $this->encodeJson($payload));
        if ($r['code'] != 200) {
            return false;
        }
        $result = json_decode($r['body'], true);
        $text = $this->extractResponsesText($result);
        if ($text === null) {
            return false;
        }
        $parsed = $this->cleanJson($text);
        return $parsed !== null ? $parsed : false;
    }

    private function callGemini($prompt)
    {
        $payload = array(
            'contents' => array(array('parts' => array(array('text' => $prompt)))),
            'generationConfig' => array('responseMimeType' => 'application/json', 'maxOutputTokens' => 32768, 'thinkingConfig' => array('thinkingBudget' => 0)),
        );
        $r = $this->httpPost($this->geminiUrl . '?key=' . $this->geminiKey, array('Content-Type: application/json'), $this->encodeJson($payload));
        if ($r['code'] != 200) {
            return false;
        }
        $result = json_decode($r['body'], true);
        if (!isset($result['candidates'][0]['content']['parts'][0]['text'])) {
            return false;
        }
        $parsed = $this->cleanJson($result['candidates'][0]['content']['parts'][0]['text']);
        return $parsed !== null ? $parsed : false;
    }

    private function extractResponsesText($result)
    {
        if (!is_array($result) || empty($result['output'])) {
            return null;
        }
        foreach ($result['output'] as $item) {
            if (isset($item['type']) && $item['type'] === 'message' && !empty($item['content'])) {
                foreach ($item['content'] as $c) {
                    if (isset($c['type']) && $c['type'] === 'output_text' && isset($c['text'])) {
                        return $c['text'];
                    }
                }
            }
        }
        return null;
    }

    private function cleanJson($content)
    {
        $content = preg_replace('/^```(?:json)?\s*/i', '', trim($content));
        $content = preg_replace('/\s*```$/', '', $content);
        return json_decode(trim($content), true);
    }

    private function tidyInput($raw)
    {
        return trim((string) preg_replace('/\s+/u', ' ', $raw));
    }

    private function httpPost($url, array $headers, $body)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
        $resp = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return array('code' => $code, 'body' => $resp);
    }

    private function encodeJson($payload)
    {
        $json = json_encode($payload);
        if ($json === false && is_array($payload)) {
            array_walk_recursive($payload, function (&$v) {
                if (is_string($v) && !mb_check_encoding($v, 'UTF-8')) {
                    $v = mb_convert_encoding($v, 'UTF-8', 'ISO-8859-1');
                }
            });
            $json = json_encode($payload);
        }
        return $json;
    }

    private function buildSeoArticlePrompt($topic, $primaryKeyword, array $secondaryKeywords, $referenceLink, $title = '', array $lsiKeywords = array())
    {
        $topic          = $this->tidyInput($topic);
        $primaryKeyword = $this->tidyInput($primaryKeyword);
        $title          = $this->tidyInput($title);
        $cleanSecondary = array();
        foreach ($secondaryKeywords as $sk) {
            $sk = $this->tidyInput($sk);
            if ($sk !== '') { $cleanSecondary[] = $sk; }
        }
        $secondaryKeywords = $cleanSecondary;
        $cleanLsi = array();
        foreach ($lsiKeywords as $lk) {
            $lk = $this->tidyInput($lk);
            if ($lk !== '') { $cleanLsi[] = $lk; }
        }
        $lsiKeywords = $cleanLsi;
        $secondaryKeywordsText = implode(', ', $secondaryKeywords);
        $lsiKeywordsText       = implode(', ', $lsiKeywords);

        return <<<PROMPT
You are a professional content writer and SEO specialist with 10+ years of
experience writing human-quality, search-optimised articles.

Your task is to write a complete, in-depth, genuinely useful article about
**{$topic}**.

> Optional reference (for context/ideas only — do NOT copy or paraphrase
> verbatim): {$referenceLink}

Read every rule below fully before you write a single word. Follow all rules
without exception. After writing the article, run every SELF-CHECK item listed
at the end before you output the final JSON.

---

## SECTION 0 — TITLE RULE

**If a title/topic is provided:**
Use "{$title}" as the basis. It may be a rough topic or a draft title. Craft
ONE unique, polished H1 from it (rewrite as needed). The H1 must:
- Be 50–60 characters (spaces included).
- Contain the primary keyword naturally.
- Be specific and descriptive — not a generic label.

**If no title is provided:**
Create ONE unique H1 title that is 50–60 characters, contains the primary
keyword naturally, and clearly signals what the article is about.

---

## SECTION 0-B — LSI / SECONDARY KEYWORD SPLIT RULE

**If LSI keywords are explicitly provided in the LSI field:**
Treat {$lsiKeywordsText} as confirmed LSI/semantic terms. Weave them naturally
into paragraphs, examples, and supporting sentences only. Do NOT place LSI
keywords in any heading. Do NOT force them.

**If the LSI field is empty:**
The Secondary Keywords field may contain a mix of true secondary keywords
(head terms users would actively search) and LSI/semantic terms. You must
separate them using this rule:

- **True secondary keyword** = a standalone search phrase a user would type
  into Google (e.g., "budget travel tips", "best running shoes").
- **LSI / semantic term** = a contextually related word or phrase that supports
  the topic but is not a primary search term on its own (e.g., "hydration",
  "arch support", "off-season deals").

Apply secondary keyword rules (2–3 occurrences, eligible for headings) only
to true secondary keywords. Apply LSI rules (natural weaving in body text,
never in headings) to the LSI/semantic terms.

---

## GOAL

Write for the READER first. The article must satisfy user intent and deliver
genuine, practical value. SEO and keywords are constraints layered on top of
useful content — never the reverse. Every word must earn its place.

---

## 1. CONTENT STRUCTURE

### 1.1 Required Elements (in this strict order)
1. H1 Title
2. Meta Description
3. Introduction section
4. Body sections (H2 and H3 hierarchy)
5. Conclusion section
6. FAQ section (always last)

### 1.2 Word Count
- Target: approximately 1,200 words.
- Acceptable range: 1,100–1,320 words.
- Body content (everything before the FAQ section): minimum 900 words.
- Do NOT pad with filler to hit the count. If you are short, deepen
  existing sections with real facts, examples, or practical steps.

### 1.3 Heading Hierarchy and Depth
- Use H1 → H2 → H3 only. Never skip a level.
- Write 5–6 H2 sections (including Introduction and Conclusion).
- Each H2 section must have 2–3 well-developed paragraphs of 2–4 sentences.
- Use H3 subsections to break down complex points within an H2.
- Every H3 must have at least one paragraph of body text directly beneath it.
- No duplicate or near-duplicate headings across the article.

### 1.4 Logical Content Flow
- Each section must follow naturally from the previous one.
- Build the topic progressively: general → specific, or problem → solution.
- The transition between sections must feel earned, not mechanical.
- A reader scanning the H2 headings alone should understand the article's
  logical journey from start to finish.

### 1.5 Unique Value Per Section
- Every section (H2 and H3 level) must introduce something new to the reader.
- No section may only restate, summarise, or repeat what a previous section
  already said.
- If two sections cover similar ground, merge them or cut the weaker one.
- Ask yourself for each section: "What does the reader learn here that they
  did not know after the previous section?" If the answer is "nothing", rewrite.

### 1.6 Introduction Rules
- Open with a blunt fact, a specific scenario, or a strong observation.
- Within the first 2–3 sentences, make clear what the article covers and
  who it helps.
- Primary keyword must appear within the first 100 words.
- Do NOT open with a greeting, a rhetorical question followed by its answer,
  a scene-setting metaphor, or the phrase "This article will cover...".

### 1.7 Conclusion Rules
- Must begin with "Bottom line:" (these exact words).
- Must include the primary keyword.
- Must synthesise the article's core takeaway — not just list what was covered.
- Do NOT simply repeat section headings or bullet-point a summary.

---

## 2. KEYWORDS

### 2.1 Primary Keyword: "{$primaryKeyword}"

The primary keyword must appear in ALL five of these locations:
1. The H1 title.
2. Within the first 100 words of the Introduction.
3. At least one H2 or H3 heading (exactly once in headings).
4. The Conclusion section.
5. The Meta Description.

Total occurrences across the entire article: **3–5 times. Never fewer than 3,
never more than 5.**

After writing the article, count every occurrence. If the count is below 3,
rewrite sentences to add it naturally until you reach 3. If the count is
above 5, rephrase sentences to remove the extras. Do not proceed to output
until the count is correct.

### 2.2 Secondary Keywords: "{$secondaryKeywordsText}"

(After applying the LSI split rule in Section 0-B, these are the confirmed
true secondary keywords.)

- Use each secondary keyword (exact phrase) naturally **2–3 times** across
  the article. Count each one individually.
- At least one secondary keyword must appear in an H2 heading where it reads
  naturally. Do not force a secondary keyword into a heading if it sounds
  unnatural.
- Secondary keywords may also appear in H3 headings where contextually apt.
- Do not cluster secondary keywords in the same paragraph.

### 2.3 LSI / Semantic Keywords: "{$lsiKeywordsText}"

(Or the terms identified as LSI after applying Section 0-B.)

- Weave LSI keywords naturally into body paragraphs, examples, and supporting
  sentences only.
- Never place an LSI keyword in any heading (H1, H2, or H3).
- Never force an LSI keyword. If it does not fit naturally in a sentence,
  skip it.
- No minimum occurrence count for LSI keywords.

### 2.4 Keyword Density

- Overall keyword density (all keywords combined) must stay **below 2%** of
  total word count.
- At 1,200 words, 2% = 24 keyword-bearing words maximum. The occurrence
  limits in 2.1 and 2.2 keep you well within this ceiling — do not exceed them.
- Never repeat a heading's keyword in the very first sentence under that
  heading.
- Headings must be SEO-friendly AND reader-focused: descriptive, specific,
  and purposeful — never a keyword dump or a string of search terms.

### 2.5 Keyword Placement Philosophy

- **User intent always comes first.** If including a keyword makes a sentence
  worse for the reader, rephrase the sentence so the keyword fits naturally,
  or use it elsewhere. Never degrade readability for a keyword count.
- Keywords should feel like they belong in the sentence, not like they were
  inserted after the fact.

---

## 3. CONTENT QUALITY

- Every section must provide specific facts, numbers, steps, real examples, or
  practical recommendations. No vague generalities.
- Paragraphs: 2–4 sentences each. No single-sentence paragraphs (except
  occasionally in the Introduction for impact). No walls of text.
- Clear, concise, user-focused language. Beginner-friendly and practical.
- Prioritise user intent and real value over keyword optimisation at all times.
- AVOID: repetitive phrases, generic AI filler, duplicate explanations, and
  any content added only to inflate the word count.

---

## 4. READABILITY AND FORMATTING

- Format for easy scanning: short paragraphs, well-organised sections, clear
  headings.
- Use `<ul>`, `<ol>`, and `<table>` in the body ONLY where they genuinely
  improve clarity (steps, comparisons, ranked criteria). Do not overuse them.
- Never use `<ul>`, `<ol>`, or `<table>` anywhere in the FAQ section.
- Plain English throughout. Avoid jargon unless the topic requires it; if you
  must use a technical term, explain it immediately.
- Use contractions. Address the reader as "you".
- Scannable subheadings: a reader should be able to skim H2 and H3 headings
  and understand the article structure without reading the body.

---

## 5. HUMAN WRITING STYLE

The writing must read as naturally human: a real expert with genuine knowledge,
a personal voice, mild opinions, and natural sentence variation.

- Vary sentence and paragraph length constantly. Mix short (5–8 words), medium
  (15–20 words), and long (25–35 words) sentences in every section.
- Use the occasional sentence fragment deliberately. Like this.
- Use em dashes naturally — but not in every paragraph.
- Include specific numbers, real timelines, and concrete situations.
- Rotate H2 and H3 opening styles: statistic, problem/pain-point, contrast,
  direct statement, real-life scenario. Never open two consecutive sections
  the same way.

---

## 6. BANNED PHRASES — NEVER USE ANY OF THESE

The following phrases are strictly prohibited anywhere in the article,
including in headings:

"First things first" / "Let's dive in" / "Let's dig in" / "Let's unpack" /
"Here's the thing" / "The truth is," / "It's important to note" /
"In today's world" / "Whether you're a beginner or expert" / "In conclusion" /
"To summarize" / "This article will cover" / "Sound familiar?" /
"Game-changer" / "Navigate" / "Leverage" / "Utilize" / "Furthermore" /
"Comprehensive" / "Delve into" / "Robust" / "Shed light on" / "In order to" /
"Ensure" / "Plays a crucial role" / "When it comes to" /
"Due to the fact" / "Many of us" / "Our bodies" / "As we age" /
"It's worth noting" / "Interestingly," / "Not surprisingly," / "Of course," /
"Needless to say," / "The good news is" / "The bad news is" /
"The key takeaway is" / "At its core," / "In essence," / "Simply put," /
"Put simply," / "Basically," / "What this means is" / "In other words," /
"This is especially true" / "While many of us" / "That means..." /
"Which means..." / "Not Just X" / "Beyond the X" / "More Than Just X" /
"More than just" / "Something harder to define" / "Something else entirely" /
"Something bigger than" / "A truly great [topic] offers" /
"A unique blend of" / "It's a rare thing" / "There's something special about" /
"It's hard to put into words" / "You're not just X; you're Y"

Replace with: plain English, direct statements, specific facts.

---

## 7. HTML OUTPUT STRUCTURE (STRICT ORDER)

Output the article body in the `content_html` field using this exact element
order:

1. `<h1>` — Title (contains primary keyword; must be the very first element).
2. `<p class="description">` — One-line teaser sentence (NOT the meta
   description; a brief hook for readers).
3. Introduction paragraphs (primary keyword within first 100 words;
   `<p>` tags only, no heading for the Introduction).
4. Body sections: `<h2>` main sections with `<p>` paragraphs beneath; `<h3>`
   subsections with `<p>` paragraphs beneath each.
5. `<ul>` / `<ol>` / `<table>` only where they genuinely improve clarity in
   the body (never in FAQ).
6. `<h2>` Conclusion — must begin "Bottom line:" and include the primary
   keyword.
7. `<h2>Frequently Asked Questions</h2>` — this exact string, as an H2
   (always the final section).
   - Strictly 4–5 FAQs. No more, no fewer.
   - Each question: `<h3>` heading.
   - Each answer: one `<p>` of approximately 45–50 words (2–3 complete
     sentences) directly beneath the `<h3>`. Start with the answer — no
     setup phrases.
   - No `<ul>`, `<ol>`, bullets, or tables anywhere in the FAQ section.
   - The exact same 4–5 Q&A pairs must appear in the `"faq"` JSON array.

---

## 8. SELF-CHECK (Run every item before producing output. Fix all failures.)

Before returning the JSON, verify every item below. Do not skip any item.
If any check fails, fix the content before outputting.

**Structure**
- [ ] H1 is the very first element in `content_html`.
- [ ] H1 is 50–60 characters.
- [ ] Meta description is 150–160 characters.
- [ ] Article follows the exact order: Introduction → Body → Conclusion → FAQ.
- [ ] FAQ is the very last section — nothing follows it.
- [ ] Heading hierarchy is H1 → H2 → H3 only. No levels skipped.
- [ ] No duplicate or near-duplicate headings.
- [ ] Every H3 has at least one `<p>` of body text beneath it.
- [ ] Conclusion starts with "Bottom line:".

**Word Count**
- [ ] Total article word count is 1,100–1,320 words.
- [ ] Body content (before FAQ) is at least 900 words.
- [ ] If short: deepened sections with real detail (not filler).

**Primary Keyword**
- [ ] Primary keyword appears in the H1.
- [ ] Primary keyword appears within the first 100 words.
- [ ] Primary keyword appears in at least one H2 or H3 heading.
- [ ] Primary keyword appears in the Conclusion.
- [ ] Primary keyword appears in the Meta Description.
- [ ] Total occurrences: 3–5. Count confirmed: ___

**Secondary Keywords** (check each one individually)
- [ ] Each secondary keyword appears 2–3 times. Count confirmed per keyword.
- [ ] At least one secondary keyword appears in an H2 heading naturally.
- [ ] No secondary keyword is over-clustered in one paragraph.

**LSI Keywords**
- [ ] LSI keywords appear only in body text (paragraphs, examples).
- [ ] No LSI keyword appears in any heading.
- [ ] All LSI keyword placements read naturally in context.

**Keyword Density**
- [ ] Overall keyword density is below 2% of total words.
- [ ] No heading's keyword is repeated in the sentence immediately beneath it.
- [ ] All headings are reader-focused and descriptive — not keyword dumps.

**User Intent and Value**
- [ ] Each section (H2 and H3 level) adds something new the reader did not
      know from the previous section.
- [ ] No section is merely a restatement or summary of earlier content.
- [ ] User intent is clearly addressed — content answers what the reader
      actually came to learn.
- [ ] Specific facts, numbers, examples, or practical steps appear in every
      section.

**Content Flow**
- [ ] Each section transitions naturally from the previous one.
- [ ] The article builds progressively: H2 headings alone tell a logical story.

**Readability**
- [ ] All paragraphs are 2–4 sentences (with rare single-sentence exceptions).
- [ ] Sentence length varies throughout every section.
- [ ] No `<ul>`, `<ol>`, or `<table>` in the FAQ section.
- [ ] Plain English used throughout; any technical terms explained.

**FAQ**
- [ ] Exactly 4–5 FAQ items (no more, no fewer).
- [ ] Each question is an `<h3>`.
- [ ] Each answer is a `<p>` of ~45–50 words, starting with the answer.
- [ ] No bullets or lists in the FAQ section.
- [ ] `"faq"` JSON array matches the HTML FAQ exactly.

**Style**
- [ ] No banned phrases used anywhere.
- [ ] Writing style is varied, natural, and human.
- [ ] No consecutive sections open with the same type of sentence.

---

## 9. OUTPUT FORMAT

Return ONLY the JSON object below. No preamble, no explanation, no markdown
fences. The JSON must be valid and parseable.

```
{
  "title": "H1 title text only (no HTML tags)",
  "meta_description": "150–160 character meta description",
  "primary_keyword": "exact primary keyword used",
  "secondary_keywords": ["kw1", "kw2"],
  "lsi_keywords": ["lsi1", "lsi2"],
  "outline": [
    "H1: Title",
    "H2: Section 1 heading",
    "H3: Subsection heading (if any)",
    "H2: Section 2 heading",
    "...",
    "H2: Conclusion",
    "H2: Frequently Asked Questions"
  ],
  "content_html": "Full HTML article as a single string",
  "faq": [
    {"question": "Question text", "answer": "Answer text ~45-50 words"},
    {"question": "Question text", "answer": "Answer text ~45-50 words"},
    {"question": "Question text", "answer": "Answer text ~45-50 words"},
    {"question": "Question text", "answer": "Answer text ~45-50 words"}
  ]
}
```
PROMPT;
    }
}