<?php
// ─────────────────────────────────────────────
// includes/ai_engine.php
// Die KI-Engine — Herzstück des Systems
// Analysiert Klienten, findet Einwanderungswege,
// generiert Geschichten und Portfolio-Inhalte
// ─────────────────────────────────────────────

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php';

class C2CAIEngine {

    // ─────────────────────────────────────
    // HAUPTMETHODE: Vollständige Analyse
    // ─────────────────────────────────────
    public static function analyzeClient(int $clientId): array {
        $client = C2CDatabase::getClient($clientId);
        if (!$client) throw new RuntimeException("Client $clientId nicht gefunden");

        $formData = $client['form_data'] ?? [];
        $prompt   = self::buildAnalysisPrompt($client, $formData);

        $rawResponse = self::callAI($prompt, 3000, self::getAnalysisSystemPrompt());
        $analysis    = self::parseAnalysisResponse($rawResponse);
        $analysis['raw_ai_response'] = $rawResponse;

        $analysisId = C2CDatabase::saveAnalysis($clientId, $analysis);
        C2CDatabase::get()->prepare("UPDATE clients SET status='analyzing' WHERE id=?")
                          ->execute([$clientId]);

        return array_merge($analysis, ['id' => $analysisId]);
    }

    // ─────────────────────────────────────
    // Story-Varianten generieren (3 Stück)
    // ─────────────────────────────────────
    public static function generateStories(int $clientId, int $analysisId): array {
        $client   = C2CDatabase::getClient($clientId);
        $db       = C2CDatabase::get();
        $stmt     = $db->prepare("SELECT * FROM immigration_analysis WHERE id=?");
        $stmt->execute([$analysisId]);
        $analysis = $stmt->fetch();

        if (!$client || !$analysis) throw new RuntimeException("Client oder Analyse nicht gefunden");

        $variants = ['abenteuer', 'karriere', 'familie'];
        $stories  = [];

        foreach ($variants as $variant) {
            $prompt   = self::buildStoryPrompt($client, $analysis, $variant);
            $rawStory = self::callAI($prompt, 4000, self::getStorySystemPrompt($variant));
            $parsed   = self::parseStoryResponse($rawStory, $variant);
            $storyId  = C2CDatabase::saveStoryVariant($clientId, $analysisId, $parsed);
            $stories[] = array_merge($parsed, ['id' => $storyId]);
        }

        C2CDatabase::get()->prepare("UPDATE clients SET status='ready' WHERE id=?")
                          ->execute([$clientId]);

        return $stories;
    }

    // ─────────────────────────────────────
    // Portfolio generieren
    // ─────────────────────────────────────
    public static function generatePortfolio(int $clientId): array {
        $client = C2CDatabase::getClient($clientId);
        $db     = C2CDatabase::get();

        // Analyse laden
        $stmt = $db->prepare("SELECT * FROM immigration_analysis WHERE client_id=? ORDER BY id DESC LIMIT 1");
        $stmt->execute([$clientId]);
        $analysis = $stmt->fetch();

        // Ausgewählte Story laden
        $stmt = $db->prepare("SELECT * FROM story_variants WHERE client_id=? AND selected=1 LIMIT 1");
        $stmt->execute([$clientId]);
        $story = $stmt->fetch();

        // Falls keine ausgewählt: erste nehmen
        if (!$story) {
            $stmt = $db->prepare("SELECT * FROM story_variants WHERE client_id=? ORDER BY id LIMIT 1");
            $stmt->execute([$clientId]);
            $story = $stmt->fetch();
        }

        $prompt    = self::buildPortfolioPrompt($client, $analysis, $story);
        $rawOutput = self::callAI($prompt, 4000, self::getPortfolioSystemPrompt());
        $portfolio = self::parsePortfolioResponse($rawOutput);

        // Portfolio in DB speichern
        $stmt = $db->prepare("
            INSERT INTO portfolios (client_id, status, profile_section, pathways_section, story_section, advantages_section, next_steps)
            VALUES (?, 'draft', ?, ?, ?, ?, ?)
            ON DUPLICATE KEY UPDATE
              profile_section=VALUES(profile_section),
              pathways_section=VALUES(pathways_section),
              story_section=VALUES(story_section),
              advantages_section=VALUES(advantages_section),
              next_steps=VALUES(next_steps),
              status='draft', updated_at=NOW()
        ");
        $stmt->execute([
            $clientId,
            $portfolio['profile_section'] ?? '',
            $portfolio['pathways_section'] ?? '',
            $portfolio['story_section'] ?? '',
            $portfolio['advantages_section'] ?? '',
            $portfolio['next_steps'] ?? '',
        ]);

        return $portfolio;
    }

    // ─────────────────────────────────────
    // PROMPT: Analyse-System
    // ─────────────────────────────────────
    private static function getAnalysisSystemPrompt(): string {
        return <<<SYSTEM
Du bist ein Experte für kanadische Einwanderung und Schweizer Kulturvermittlung.
Deine Aufgabe: Analysiere das Profil einer schweizer Person, die nach Kanada einwandern möchte.
Identifiziere realistische Einwanderungswege, Schweizer Stärken als Vorteil für Kanada,
und potenzielle Herausforderungen.

Wichtig:
- Du bist KEIN lizenzierter RCIC/Anwalt. Positioniere alles als "Recherche und Storytelling".
- Sei präzise, warmherzig, aber realistisch.
- Verwende aktuelle kanadische Programme (Express Entry, PNP, Quebec Immigration, Rural Pathways etc.)

KRITISCHE AUSGABE-REGEL:
Antworte AUSSCHLIESSLICH mit einem reinen JSON-Objekt.
- Beginne mit `{` und ende mit `}`.
- KEINE Markdown-Codeblöcke (kein ```json, kein ```).
- KEINE Erklärungstexte vor oder nach dem JSON.
- KEINE Kommentare im JSON.
- Das erste Zeichen der Antwort muss `{` sein.
SYSTEM;
    }

    // ─────────────────────────────────────
    // PROMPT: Story-System
    // ─────────────────────────────────────
    private static function getStorySystemPrompt(string $variant): string {
        $variantDescriptions = [
            'abenteuer' => 'Abenteuer und neuen Lebensabschnitt — emotionale Reise, Mut, Aufbruch',
            'karriere'  => 'berufliche Chancen und professionelles Wachstum — Schweizer Qualität trifft kanadischen Markt',
            'familie'   => 'Familienzukunft und Sicherheit — Kanada als stabiles, offenes Land für Kinder und Generationen',
        ];

        return <<<SYSTEM
Du bist SwissChris — ein Schweizer Storyteller in Kanada. Deine Stimme ist:
warm, leicht ironisch, narrativ, humanistisch, niemals bürokratisch.

Du schreibst eine persönliche Einwanderungsgeschichte für den Klienten aus der Perspektive:
"{$variantDescriptions[$variant]}"

Regeln:
- Schreibe in der Ich-Perspektive des Klienten (oder "wir" bei Familie)
- Beginne mit einem starken, konkreten Bild oder Moment
- Zeige, was der Klient KANADA bringt — nicht was Kanada ihm gibt
- Vermeide bürokratische Sprache
- Schweizer Orthographie: KEIN ß, immer ss schreiben (Strasse, müssen, draussen)

KRITISCHE AUSGABE-REGEL:
Antworte AUSSCHLIESSLICH mit einem reinen JSON-Objekt.
- Beginne mit `{` und ende mit `}`.
- KEINE Markdown-Codeblöcke (kein ```json, kein ```).
- KEINE Erklärungstexte vor oder nach dem JSON.
- Das erste Zeichen der Antwort muss `{` sein.
SYSTEM;
    }

    // ─────────────────────────────────────
    // PROMPT: Portfolio-System
    // ─────────────────────────────────────
    private static function getPortfolioSystemPrompt(): string {
        return <<<SYSTEM
Du erstellst ein professionelles Einwanderungs-Portfolio für come2canada.life.
Stil: SwissChris — warmherzig, präzise, storytelling-orientiert, nie bürokratisch.
Das Portfolio richtet sich an den Klienten (Schweizer Person) UND potenzielle Gesprächspartner
(Arbeitgeber, RCIC-Anwälte, Einwanderungsbehörden).

Schweizer Orthographie: KEIN ß, immer ss schreiben.

KRITISCHE AUSGABE-REGEL:
Antworte AUSSCHLIESSLICH mit einem reinen JSON-Objekt.
- Beginne mit `{` und ende mit `}`.
- KEINE Markdown-Codeblöcke (kein ```json, kein ```).
- KEINE Erklärungstexte vor oder nach dem JSON.
- Das erste Zeichen der Antwort muss `{` sein.
SYSTEM;
    }

    // ─────────────────────────────────────
    // PROMPT: Analyse aufbauen
    // ─────────────────────────────────────
    private static function buildAnalysisPrompt(array $client, array $formData): string {
        $name = trim(($client['vorname'] ?? '') . ' ' . ($client['nachname'] ?? ''));
        $fd   = json_encode($formData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

        return <<<PROMPT
Analysiere dieses Einwanderungsprofil einer schweizer Person für Kanada.

KLIENT: {$name}
KANTON: {$client['canton']}
SERVICE: {$client['service_type']}

FORMULAR-DATEN:
{$fd}

Erstelle eine vollständige Analyse im folgenden JSON-Format:
{
  "profile_summary": "2-3 Sätze Kurzbeschreibung auf Deutsch",
  "pathways": [
    {
      "name": "Programmname",
      "category": "federal|provincial|quebec|atlantic",
      "program_code": "z.B. FSW, CEC, OINP-HCP",
      "fit_score": 85,
      "rationale": "Warum dieser Weg passt",
      "requirements_met": ["req1", "req2"],
      "gaps": ["was fehlt noch"],
      "timeline_months": 18
    }
  ],
  "recommended_pathway": "Primärer empfohlener Weg",
  "recommended_provinces": ["Ontario", "BC"],
  "crs_estimate": 420,
  "noc_codes": ["13100", "12200"],
  "swiss_advantages": [
    {
      "category": "Ausbildung|Sprache|Beruf|Kultur|Finanzen",
      "title": "Schweizer Berufsausbildung (EFZ)",
      "description": "Warum das ein Vorteil für Kanada ist",
      "canadian_value": "Was Kanada davon hat"
    }
  ],
  "risk_factors": [
    {
      "area": "Sprachkenntnisse|Alter|Qualifikation",
      "description": "Beschreibung der Herausforderung",
      "mitigation": "Wie man es angehen kann"
    }
  ],
  "immediate_action_steps": ["Schritt 1", "Schritt 2", "Schritt 3"],
  "rcic_needed": true,
  "rcic_reason": "Warum ein lizenzierter Berater nötig ist"
}
PROMPT;
    }

    // ─────────────────────────────────────
    // PROMPT: Story aufbauen
    // ─────────────────────────────────────
    private static function buildStoryPrompt(array $client, array $analysis, string $variant): string {
        $name     = trim(($client['vorname'] ?? '') . ' ' . ($client['nachname'] ?? ''));
        $formData = json_encode($client['form_data'] ?? [], JSON_UNESCAPED_UNICODE);
        $pathways = $analysis['pathways'] ?? '[]';
        $advantages = $analysis['swiss_advantages'] ?? '[]';

        return <<<PROMPT
Erstelle eine persönliche Einwanderungsgeschichte — Variante: {$variant}

KLIENT: {$name}, Kanton {$client['canton']}
EINWANDERUNGSWEGE: {$pathways}
SCHWEIZER VORTEILE: {$advantages}
PERSÖNLICHE INFORMATIONEN: {$formData}

JSON-Format:
{
  "variant_key": "{$variant}",
  "title": "Packender Titel (max 8 Wörter)",
  "subtitle": "Untertitel (max 15 Wörter)",
  "teaser": "2-3 Sätze die Lust machen weiterzulesen",
  "full_story": "Die vollständige Geschichte (500-700 Wörter, Deutsch, Markdown OK)",
  "advantages_block": "Was {$name} Kanada bringt — 5-7 konkrete Punkte (Markdown)",
  "call_to_action": "Abschlusssatz / nächster Schritt"
}
PROMPT;
    }

    // ─────────────────────────────────────
    // PROMPT: Portfolio aufbauen
    // ─────────────────────────────────────
    private static function buildPortfolioPrompt(array $client, ?array $analysis, ?array $story): string {
        $name     = trim(($client['vorname'] ?? '') . ' ' . ($client['nachname'] ?? ''));
        $anlData  = json_encode($analysis ?? [], JSON_UNESCAPED_UNICODE);
        $storyTxt = $story['full_story'] ?? '';
        $advantages = $story['advantages_block'] ?? '';

        return <<<PROMPT
Erstelle ein vollständiges Portfolio-Dokument für {$name}.

ANALYSE-DATEN: {$anlData}
AUSGEWÄHLTE GESCHICHTE: {$storyTxt}
VORTEILE FÜR KANADA: {$advantages}

JSON-Format:
{
  "profile_section": "Vollständige Profil-Sektion (Markdown, DE)",
  "pathways_section": "Einwanderungswege-Übersicht (Markdown, DE, mit Vergleichstabelle)",
  "story_section": "Die persönliche Geschichte (Markdown, DE)",
  "advantages_section": "Was {$name} Kanada bringt — ausführlich (Markdown, DE)",
  "next_steps": "Konkrete nächste Schritte (Markdown, nummeriert)",
  "disclaimer": "Disclaimer: Storytelling und Recherche, keine Rechtsberatung",
  "cover_title": "Portfolio-Titel",
  "cover_subtitle": "Untertitel"
}
PROMPT;
    }

    // ─────────────────────────────────────
    // Anthropic API aufrufen
    // ─────────────────────────────────────
    public static function callAI(string $userPrompt, int $maxTokens = 2000, string $systemPrompt = ''): string {
        $payload = [
            'model'      => ANTHROPIC_MODEL,
            'max_tokens' => $maxTokens,
            'messages'   => [['role' => 'user', 'content' => $userPrompt]],
        ];
        if ($systemPrompt) {
            $payload['system'] = $systemPrompt;
        }

        $ch = curl_init(ANTHROPIC_ENDPOINT);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'x-api-key: ' . ANTHROPIC_API_KEY,
                'anthropic-version: 2023-06-01',
            ],
            CURLOPT_TIMEOUT        => 120,
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            throw new RuntimeException("Anthropic API Error $httpCode: $response");
        }

        $decoded = json_decode($response, true);
        return $decoded['content'][0]['text'] ?? '';
    }

    // ─────────────────────────────────────
    // Antworten parsen
    // ─────────────────────────────────────

    /**
     * Robuster JSON-Extractor.
     *
     * Modelle liefern manchmal:
     *   - reines JSON: {"key": "value"}
     *   - Markdown-Wrapper: ```json\n{...}\n```
     *   - mit Erklärungstext: "Hier ist das JSON:\n```json\n{...}```"
     *   - mit BOM, Whitespace, Newlines am Anfang
     *   - TRUNKIERT (max_tokens erschöpft, JSON nicht geschlossen)
     *
     * Strategie:
     *   1. Versuche direktes json_decode (schnellster Pfad).
     *   2. Fallback: Code-Fence-Wrapper entfernen.
     *   3. Fallback: erstes vollständiges {...} per Brace-Matching extrahieren.
     *   4. Last-Resort: trunkiertes JSON heuristisch schliessen.
     *
     * @return array|null JSON als Array oder null wenn nichts geht.
     */
    private static function extractJson(string $raw): ?array {
        if ($raw === '') return null;

        // 1. Direkt versuchen
        $direct = json_decode($raw, true);
        if (is_array($direct)) return $direct;

        // 2. Trim + Code-Fence-Wrapper entfernen
        $clean = trim($raw);
        $clean = preg_replace('/^\xEF\xBB\xBF/', '', $clean);
        $clean = preg_replace('/^\s*```(?:json|JSON)?\s*\n?/', '', $clean);
        $clean = preg_replace('/\n?\s*```\s*$/', '', $clean);
        $clean = trim($clean);

        $stripped = json_decode($clean, true);
        if (is_array($stripped)) return $stripped;

        // 3. Brace-Matching: erstes vollständiges JSON-Objekt extrahieren
        $start = strpos($clean, '{');
        if ($start === false) return null;

        $depth = 0;
        $inString = false;
        $escape = false;
        $end = -1;
        $len = strlen($clean);

        for ($i = $start; $i < $len; $i++) {
            $c = $clean[$i];

            if ($escape) { $escape = false; continue; }
            if ($c === '\\' && $inString) { $escape = true; continue; }
            if ($c === '"') { $inString = !$inString; continue; }
            if ($inString) continue;

            if ($c === '{') $depth++;
            elseif ($c === '}') {
                $depth--;
                if ($depth === 0) { $end = $i; break; }
            }
        }

        if ($end >= 0) {
            $candidate = substr($clean, $start, $end - $start + 1);
            $extracted = json_decode($candidate, true);
            if (is_array($extracted)) return $extracted;
        }

        // 4. Last-Resort: trunkiertes JSON heuristisch reparieren
        // Vorgehen: Vom Ende des bekannten JSON-Inhalts rückwärts nach dem
        // letzten kompletten Key:Value-Paar suchen, danach abschneiden,
        // offene Strings/Klammern schliessen.
        return self::attemptJsonRepair(substr($clean, $start));
    }

    /**
     * Heuristische Reparatur eines abgeschnittenen JSON-Strings.
     * Schneidet beim letzten vollständigen "key": "value" ab und
     * schliesst alle offenen Klammern. Best-effort, kein Garant.
     */
    private static function attemptJsonRepair(string $partial): ?array {
        // Letzte vollständige String-Value finden: "..." gefolgt von , oder }
        // Wir suchen das letzte Vorkommen von "," das nicht in einem String steht.
        $depth = 0;
        $inString = false;
        $escape = false;
        $lastSafeComma = -1;
        $len = strlen($partial);

        for ($i = 0; $i < $len; $i++) {
            $c = $partial[$i];

            if ($escape) { $escape = false; continue; }
            if ($c === '\\' && $inString) { $escape = true; continue; }
            if ($c === '"') { $inString = !$inString; continue; }
            if ($inString) continue;

            if ($c === '{' || $c === '[') $depth++;
            elseif ($c === '}' || $c === ']') $depth--;
            elseif ($c === ',' && $depth === 1) {
                // Komma auf Top-Level (innerhalb des äusseren Objekts)
                $lastSafeComma = $i;
            }
        }

        if ($lastSafeComma < 0) return null;

        // Bis zum letzten sicheren Komma abschneiden, dann "}" anhängen
        $repaired = substr($partial, 0, $lastSafeComma) . '}';
        $result = json_decode($repaired, true);

        if (is_array($result)) {
            error_log("AI JSON repair successful: gerettete Keys = " . implode(', ', array_keys($result)));
            return $result;
        }
        return null;
    }

    private static function parseAnalysisResponse(string $raw): array {
        $data = self::extractJson($raw);
        if ($data === null) {
            error_log('AI Analysis parse error. Raw (first 500 chars): ' . substr($raw, 0, 500));
            return ['profile_summary' => 'Analyse konnte nicht geparst werden.', 'pathways' => []];
        }
        return $data;
    }

    private static function parseStoryResponse(string $raw, string $variant): array {
        $data = self::extractJson($raw);
        if ($data === null) {
            error_log("AI Story parse error (variant=$variant). Raw (first 500 chars): " . substr($raw, 0, 500));
            return [
                'variant_key' => $variant,
                'title'       => 'Story konnte nicht generiert werden',
                'full_story'  => $raw,
            ];
        }
        // Sicherstellen, dass variant_key gesetzt ist (manchmal vergisst das Modell es)
        if (empty($data['variant_key'])) $data['variant_key'] = $variant;
        return $data;
    }

    private static function parsePortfolioResponse(string $raw): array {
        $data = self::extractJson($raw);
        if ($data === null) {
            error_log('AI Portfolio parse error. Raw (first 500 chars): ' . substr($raw, 0, 500));
            return ['profile_section' => $raw];
        }
        return $data;
    }
}
