<?php

namespace App\Services;

class LearningDeviceAiService
{
    protected $apiKey;
    protected $modelName;
    protected $provider;
    protected $models;

    public function __construct()
    {
        $provider = 'gemini';
        $apiKey = '';

        // 1. Coba baca API Key mandiri milik guru yang sedang login
        try {
            $session = \Config\Services::session();
            $userId = $session->get('user_id');
            if ($userId) {
                $teacherModel = new \App\Models\TeacherModel();
                $teacher = $teacherModel->where('user_id', $userId)->first();
                if ($teacher && !empty($teacher['ai_api_key'])) {
                    $apiKey = $teacher['ai_api_key'];
                    $provider = $teacher['ai_provider'] ?? 'gemini';
                }
            }
        } catch (\Exception $e) {
            // Abaikan jika session/database belum siap
        }

        // 2. Fallback ke API Key Global Admin jika API Key guru kosong
        if (empty($apiKey)) {
            try {
                $schoolSettingModel = new \App\Models\SchoolSettingModel();
                $settings = $schoolSettingModel->getSettings();
                if ($settings) {
                    $provider = $settings['ai_provider'] ?? 'gemini';
                    $apiKey = $settings['ai_api_key'] ?? '';
                }
            } catch (\Exception $e) {
                // Abaikan jika database/tabel belum siap
            }
        }

        // 3. Fallback ke .env jika API Key kosong dan provider adalah gemini
        if (empty($apiKey) && $provider === 'gemini') {
            $apiKey = env('gemini.api_key') ?? '';
        }

        $this->provider = $provider;
        $this->apiKey = $apiKey;

        $this->models = [
            'gemini'   => 'gemini-3.5-flash',
            'openai'   => 'gpt-5.4-mini',
            'deepseek' => 'deepseek-v4-flash',
        ];

        $this->modelName = $this->models[$this->provider] ?? 'gemini-1.5-flash';
    }

    /**
     * Mengirim request HTTP POST langsung ke API Google Gemini
     */
    protected function requestGemini(string $prompt, bool $isJson = true): array
    {
        if (empty($this->apiKey)) {
            return [
                'status'  => 'error',
                'message' => 'API Key Gemini (gemini.api_key) belum diisi di file .env lokal Anda. Silakan isi terlebih dahulu untuk mengaktifkan AI Co-pilot.'
            ];
        }

        $url = "https://generativelanguage.googleapis.com/v1beta/models/{$this->modelName}:generateContent?key={$this->apiKey}";

        $payload = [
            'contents' => [
                [
                    'parts' => [
                        ['text' => $prompt]
                    ]
                ]
            ]
        ];

        if ($isJson) {
            $payload['generationConfig'] = [
                'responseMimeType' => 'application/json'
            ];
        }

        $maxRetries = 3;
        $retryDelay = 2; // detik

        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disabled SSL check to prevent local XAMPP issues
            curl_setopt($ch, CURLOPT_TIMEOUT, 120);

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

            // Jika limitasi API (429) atau server sibuk (500/503) terjadi, tunggu dan coba lagi
            if ($httpCode === 429 || $httpCode === 503 || $httpCode === 500) {
                if ($attempt < $maxRetries) {
                    sleep($retryDelay * $attempt); // Backoff eksponensial (2s, 4s...)
                    continue;
                }
                $errBody = json_decode($response, true);
                $errMsg = $errBody['error']['message'] ?? '';
                if ($errMsg) {
                    return [
                        'status'  => 'error',
                        'message' => 'API Gemini sedang sibuk: ' . $errMsg
                    ];
                }
                return [
                    'status'  => 'error',
                    'message' => 'Server Google Gemini saat ini sedang kelebihan beban (Error ' . $httpCode . '). Ini adalah hal yang wajar karena tingginya permintaan di server pusat mereka. Silakan jeda beberapa detik dan klik tombol Generate sekali lagi.'
                ];
            }

            if ($httpCode === 0 || $response === false) {
                return [
                    'status'  => 'error',
                    'message' => 'Gagal menyambung ke server Google Gemini. (cURL Error: ' . $curlError . ')'
                ];
            }

            if ($httpCode !== 200) {
                $errBody = json_decode($response, true);
                $errMsg = $errBody['error']['message'] ?? substr($response, 0, 200);
                return [
                    'status'  => 'error',
                    'message' => 'API Gemini mengembalikan kode kesalahan HTTP ' . $httpCode . '. Detail: ' . $errMsg
                ];
            }

            $responseData = json_decode($response, true);
            if (!is_array($responseData) || !isset($responseData['candidates'][0]['content']['parts'][0]['text'])) {
                return [
                    'status'  => 'error',
                    'message' => 'Format respon dari API Gemini tidak valid.'
                ];
            }

            $textOutput = $responseData['candidates'][0]['content']['parts'][0]['text'];
            $cleanedOutput = $this->cleanJsonString($textOutput);
            
            return [
                'status' => 'success',
                'data'   => $cleanedOutput
            ];
        }

        return [
            'status'  => 'error',
            'message' => 'Gagal terhubung dengan server AI setelah beberapa kali percobaan.'
        ];
    }

    /**
     * Router method to send request to the selected AI provider
     */
    public function requestAi(string $prompt, bool $isJson = true): array
    {
        if (empty($this->apiKey)) {
            return [
                'status'  => 'error',
                'message' => 'API Key untuk penyedia AI ' . strtoupper($this->provider) . ' belum dikonfigurasi di Pengaturan Lembaga.'
            ];
        }

        switch ($this->provider) {
            case 'openai':
                return $this->requestOpenAiCompatible(
                    $prompt,
                    'https://api.openai.com/v1/chat/completions',
                    $this->models['openai'] ?? 'gpt-4o-mini',
                    $this->apiKey,
                    $isJson
                );
            case 'deepseek':
                return $this->requestOpenAiCompatible(
                    $prompt,
                    'https://api.deepseek.com/chat/completions',
                    $this->models['deepseek'] ?? 'deepseek-chat',
                    $this->apiKey,
                    $isJson
                );
            case 'gemini':
            default:
                return $this->requestGemini($prompt, $isJson);
        }
    }

    /**
     * Sends request to an OpenAI-compatible completion endpoint (OpenAI, Deepseek)
     */
    protected function requestOpenAiCompatible(string $prompt, string $url, string $model, string $apiKey, bool $isJson = true): array
    {
        $payload = [
            'model' => $model,
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $prompt
                ]
            ]
        ];

        // Hindari memaksa response_format => json_object karena prompt kita meminta output berupa JSON Array ([...]).
        // Di OpenAI, memaksa json_object untuk prompt yang menghasilkan array akan membuat model loop/hang menghasilkan whitespace tanpa henti.
        // gpt-4o-mini dan deepseek-chat sudah sangat pintar menghasilkan format JSON yang tepat dari instruksi prompt.
        /*
        if ($isJson) {
            $payload['response_format'] = [
                'type' => 'json_object'
            ];
        }
        */

        $maxRetries = 3;
        $retryDelay = 2; // detik

        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $apiKey
            ]);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_TIMEOUT, 120);

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

            // Jika limitasi API (429) atau server error (500/503) terjadi, tunggu dan coba lagi
            if ($httpCode === 429 || $httpCode === 503 || $httpCode === 500) {
                if ($attempt < $maxRetries) {
                    sleep($retryDelay * $attempt);
                    continue;
                }
                $errBody = json_decode($response, true);
                $errMsg = $errBody['error']['message'] ?? '';
                if ($errMsg) {
                    return [
                        'status'  => 'error',
                        'message' => 'API ' . ucfirst($this->provider) . ' mengembalikan kesalahan: ' . $errMsg
                    ];
                }
                return [
                    'status'  => 'error',
                    'message' => 'Server AI sedang sangat sibuk atau limit tercapai (Error ' . $httpCode . '). Silakan tunggu beberapa saat dan coba lagi.'
                ];
            }

            if ($httpCode === 0 || $response === false) {
                return [
                    'status'  => 'error',
                    'message' => 'Gagal menyambung ke server ' . ucfirst($this->provider) . '. (cURL Error: ' . $curlError . ')'
                ];
            }

            if ($httpCode !== 200) {
                $errBody = json_decode($response, true);
                $errMsg = $errBody['error']['message'] ?? substr($response, 0, 200);
                return [
                    'status'  => 'error',
                    'message' => 'API ' . ucfirst($this->provider) . ' mengembalikan kode kesalahan HTTP ' . $httpCode . '. Detail: ' . $errMsg
                ];
            }

            $responseData = json_decode($response, true);
            if (!is_array($responseData) || !isset($responseData['choices'][0]['message']['content'])) {
                return [
                    'status'  => 'error',
                    'message' => 'Format respon dari API ' . ucfirst($this->provider) . ' tidak valid.'
                ];
            }

            $textOutput = $responseData['choices'][0]['message']['content'];
            $cleanedOutput = $this->cleanJsonString($textOutput);
            
            return [
                'status' => 'success',
                'data'   => $cleanedOutput
            ];
        }

        return [
            'status'  => 'error',
            'message' => 'Gagal terhubung dengan server AI setelah beberapa kali percobaan.'
        ];
    }

    protected function cleanJsonString(string $string): string
    {
        $string = trim($string);
        
        // Cari karakter JSON pembuka pertama
        $startPosObj = strpos($string, '{');
        $startPosArr = strpos($string, '[');
        
        $startPos = false;
        if ($startPosObj !== false && $startPosArr !== false) {
            $startPos = min($startPosObj, $startPosArr);
        } else {
            $startPos = $startPosObj !== false ? $startPosObj : $startPosArr;
        }

        // Cari karakter JSON penutup terakhir
        $endPosObj = strrpos($string, '}');
        $endPosArr = strrpos($string, ']');
        
        $endPos = false;
        if ($endPosObj !== false && $endPosArr !== false) {
            $endPos = max($endPosObj, $endPosArr);
        } else {
            $endPos = $endPosObj !== false ? $endPosObj : $endPosArr;
        }

        // Potong string hanya pada bagian JSON saja
        if ($startPos !== false && $endPos !== false && $endPos > $startPos) {
            return trim(substr($string, $startPos, $endPos - $startPos + 1));
        }

        return $string;
    }

    /**
     * Menganalisis CP dan merumuskan daftar Tujuan Pembelajaran (TP)
     */
    public function generateDraftTP(string $cp, string $subject, string $phase, string $grade): array
    {
        $subjectGroup = \App\Config\KkoTaxonomy::getGroupForSubject($subject);
        $kkoService = new \App\Services\KkoService($subjectGroup);
        $kkoText = $kkoService->buildPromptKkoText();

        $prompt = "Anda adalah pakar kurikulum nasional Indonesia (Kurikulum Merdeka). Analisislah teks Capaian Pembelajaran (CP) berikut untuk mata pelajaran '{$subject}', tingkat Kelas '{$grade}', Fase '{$phase}'.\n\n";
        $prompt .= "Teks CP Resmi:\n\"{$cp}\"\n\n";
        $prompt .= "Tugas Anda adalah menurunkan/merumuskan daftar Tujuan Pembelajaran (TP) yang logis, berurutan secara akademis (dari mudah ke kompleks), dan mencakup seluruh aspek CP tersebut.\n\n";
        $prompt .= "Aturan Output:\n";
        $prompt .= "1. Hasilkan antara 4 s.d 8 Tujuan Pembelajaran.\n";
        $prompt .= "2. Setiap TP wajib diawali dengan Kata Kerja Operasional (KKO) yang terukur dan spesifik untuk mapel {$subject}.\n";
        $prompt .= "3. Daftar KKO yang tersedia untuk {$subject}:\n{$kkoText}\n";
        $prompt .= "4. Alokasikan estimasi Jam Pelajaran (JP) yang logis per TP (kelipatan 2 atau 4 JP, misal 4 JP, 8 JP, atau 12 JP).\n";
        $prompt .= "5. Berikan output HANYA dalam format JSON array terstruktur dengan skema berikut (tanpa markdown wrapper):\n";
        $prompt .= "[\n";
        $prompt .= "  {\n";
        $prompt .= "    \"code\": \"TP-1\",\n";
        $prompt .= "    \"description\": \"Rumusan deskripsi TP pertama yang diawali dengan kata kerja operasional terukur...\",\n";
        $prompt .= "    \"allocated_hours\": 8\n";
        $prompt .= "  },\n";
        $prompt .= "  ...\n";
        $prompt .= "]\n";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') {
            return $res;
        }

        $decoded = json_decode($res['data'], true);
        if (!is_array($decoded)) {
            return [
                'status'  => 'error',
                'message' => 'Respon AI tidak dapat diurai sebagai JSON array valid. Detail mentah: ' . substr($res['data'], 0, 150)
            ];
        }

        return [
            'status' => 'success',
            'data'   => $decoded
        ];
    }

    
    /**
     * TAHAP 1: Generate Identitas & Komponen Inti
     */
    public function generateModulStage1(array $tp, string $subject, string $phase, string $grade, string $schoolType, string $dplSelected, string $kbcSelected): array
    {
        $prompt = "Anda adalah pakar pembuat perangkat modul ajar Kurikulum Merdeka. Rumuskan **Materi Esensial** dan Komponen Inti secara detail berdasarkan Tujuan Pembelajaran (TP) berikut:

";
        $prompt .= "Mata Pelajaran: {$subject}
";
        $prompt .= "Kelas: {$grade} (Fase {$phase})
";
        $prompt .= "Deskripsi TP: {$tp['description']}
";
        $prompt .= "Karakter yang Diinginkan: {$dplSelected}
";
        $prompt .= "Nilai Cinta yang Diinginkan: {$kbcSelected}

";
        $prompt .= "Tugas Anda:
";
        $prompt .= "1. Tentukan Model Pembelajaran yang paling cocok (misal: PBL, PjBL, Discovery, dll).
";
        $prompt .= "2. Rumuskan Pemahaman Bermakna dan minimal 2 Pertanyaan Pemantik.
";
        $prompt .= "3. Uraikan Materi Esensial ke dalam 4 struktur: Konsep Esensial, Konteks Fenomena Sehari-hari, Identifikasi Masalah & Solusi, dan Integrasi Karakter & Cinta secara natural.

";
        
        $prompt .= "Berikan output HANYA berupa objek JSON yang valid dengan skema berikut:
";
        $prompt .= "{
";
        $prompt .= "  \"identity\": {
";
        $prompt .= "    \"school_name\": \"[Nama Sekolah]\",
";
        $prompt .= "    \"subject\": \"{$subject}\",
";
        $prompt .= "    \"grade\": \"{$grade}\",
";
        $prompt .= "    \"phase\": \"{$phase}\",
";
        $prompt .= "    \"semester\": \"{$tp['semester']}\"
";
        $prompt .= "  },
";
        $prompt .= "  \"core_components\": {
";
        $prompt .= "    \"tp\": \"{$tp['description']}\",
";
        $prompt .= "    \"model_pembelajaran\": \"Model yang dipilih\",
";
        $prompt .= "    \"pemahaman_bermakna\": \"Manfaat nyata...\",
";
        $prompt .= "    \"pemantik\": [\"Pertanyaan 1\", \"Pertanyaan 2\"],
";
        $prompt .= "    \"materi_esensial\": \"Isi materi esensial (teks naratif dengan 4 struktur)...\",
";
        $prompt .= "    \"media\": [\"Media 1\", \"Media 2\"],
";
        $prompt .= "    \"sumber_belajar\": [\"Sumber 1\", \"Sumber 2\"]
";
        $prompt .= "  }
";
        $prompt .= "}";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') return $res;
        
        $decoded = json_decode($res['data'], true);
        return is_array($decoded) ? ['status' => 'success', 'data' => $decoded] : ['status' => 'error', 'message' => 'Invalid JSON Stage 1'];
    }

    /**
     * TAHAP 2: Generate Skenario Pembelajaran (Romawi III)
     */
    public function generateModulStage2(array $tp, array $stage1Data, string $schoolType, string $dplSelected, string $kbcSelected): array
    {
        $materi = $stage1Data['materi_esensial'] ?? '';
        $model = $stage1Data['model_pembelajaran'] ?? 'Problem Based Learning';
        
        $prompt = "Rumuskan Skenario Pembelajaran (Kegiatan Pendahuluan, Inti, dan Penutup) untuk materi: {$tp['description']} dengan alokasi waktu {$tp['allocated_hours']} JP.

";
        $prompt .= "Wajib menggunakan Model Pembelajaran: {$model}
";
        $prompt .= "Uraikan langkah-langkah guru dan siswa secara detail. Khusus pada Kegiatan Inti, wajib dibagi secara tegas ke dalam 3 tahapan 'Deep Learning' berikut:
";
        $prompt .= "1. Tahap Memahami (Input): Menguasai konsep dan menghubungkannya dengan kehidupan sehari-hari.
";
        $prompt .= "2. Tahap Memecahkan Masalah (Proses): Diberikan masalah nyata. Integrasikan Karakter [{$dplSelected}] dan [{$kbcSelected}].
";
        $prompt .= "3. Tahap Mengaplikasi (Output): Mendemonstrasikan aplikasi pemecahan masalah.

";
        
        if ($schoolType === 'madrasah') {
            $prompt .= "Awali setiap langkah dengan tag [Mindful], [Meaningful], atau [Joyful] sesuai fokus aktivitas.

";
        }

        $prompt .= "Berikan output HANYA berupa objek JSON yang valid dengan skema berikut:
";
        $prompt .= "{
";
        $prompt .= "  \"differentiation\": {
";
        $prompt .= "    \"konten\": [\"Strategi diferensiasi konten...\"],
";
        $prompt .= "    \"proses\": [\"Strategi diferensiasi proses...\"],
";
        $prompt .= "    \"produk\": [\"Strategi diferensiasi produk...\"]
";
        $prompt .= "  },
";
        $prompt .= "  \"kegiatan_pembelajaran\": [
";
        $prompt .= "    {
";
        $prompt .= "      \"pertemuan\": 1,
";
        $prompt .= "      \"tahap_pendahuluan\": \"Langkah pendahuluan rinci...\",
";
        $prompt .= "      \"tahap_inti\": \"Langkah inti (Memahami, Memecahkan Masalah, Mengaplikasi)...\",
";
        $prompt .= "      \"tahap_penutup\": \"Langkah penutup...\"
";
        $prompt .= "    }
";
        $prompt .= "  ]
";
        $prompt .= "}";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') return $res;
        
        $decoded = json_decode($res['data'], true);
        return is_array($decoded) ? ['status' => 'success', 'data' => $decoded] : ['status' => 'error', 'message' => 'Invalid JSON Stage 2'];
    }

    /**
     * TAHAP 3: Generate Asesmen, Refleksi & Lampiran (Romawi IV, V, VI)
     */
    public function generateModulStage3(array $tp, array $stage2Data): array
    {
        $prompt = "Buatlah Instrumen Asesmen, Refleksi, dan Lampiran untuk Modul Ajar tentang: {$tp['description']}.

";
        $prompt .= "1. Refleksi Pembelajaran: Buatkan pertanyaan refleksi kontekstual untuk siswa dan guru.
";
        $prompt .= "2. Asesmen: Buatkan metode Asesmen Diagnostik, Formatif (beserta rubrik observasi proses), dan Sumatif.
";
        $prompt .= "3. Pengayaan & Remedial: Tuliskan strategi tindak lanjut yang spesifik.
";
        $prompt .= "4. Lampiran: Buatkan draf kerangka LKPD (Lembar Kerja Peserta Didik).

";

        $prompt .= "Berikan output HANYA berupa objek JSON yang valid dengan skema berikut:
";
        $prompt .= "{
";
        $prompt .= "  \"assessment\": {
";
        $prompt .= "    \"diagnostik\": \"Metode asesmen awal pembelajaran...\",
";
        $prompt .= "    \"formatif\": \"Bentuk dan rubrik penilaian proses...\",
";
        $prompt .= "    \"sumatif\": \"Bentuk penilaian akhir unit...\"
";
        $prompt .= "  },
";
        $prompt .= "  \"follow_up\": {
";
        $prompt .= "    \"remedial\": \"Tindakan bagi siswa yang belum tuntas...\",
";
        $prompt .= "    \"pengayaan\": \"Tantangan bagi siswa yang memiliki nilai tinggi...\"
";
        $prompt .= "  },
";
        $prompt .= "  \"reflection\": {
";
        $prompt .= "    \"refleksi_guru\": [\"Refleksi kontekstual guru...\"],
";
        $prompt .= "    \"refleksi_siswa\": [\"Refleksi kontekstual siswa...\"]
";
        $prompt .= "  },
";
        $prompt .= "  \"lampiran\": {
";
        $prompt .= "    \"lkpd\": \"Kerangka dan instruksi Lembar Kerja Peserta Didik (LKPD)...\",
";
        $prompt .= "    \"glosarium\": [\"Istilah 1: Arti\", \"Istilah 2: Arti\"]
";
        $prompt .= "  }
";
        $prompt .= "}";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') return $res;
        
        $decoded = json_decode($res['data'], true);
        return is_array($decoded) ? ['status' => 'success', 'data' => $decoded] : ['status' => 'error', 'message' => 'Invalid JSON Stage 3'];
    }

    /**
     * Menganalisis CP dan merumuskan daftar Tujuan Pembelajaran (TP) berbasis Bab/Materi Pokok & Integrasi Karakter
     */
    public function generateTpByChaptersAndCharacters(string $cp, string $subject, string $phase, string $grade, array $chapters, $selectedDpl, $selectedPancaCinta, string $schoolType, int $tpsPerChapter = 4, array $chapterCpMap = []): array
    {
        // Parse CP elements into lookup map: name => description
        $cpElementsLookup = [];
        $cpElementsText = "";
        $elements = json_decode($cp, true);
        if (is_array($elements)) {
            foreach ($elements as $el) {
                $name = $el['name'] ?? '';
                $desc = $el['description'] ?? '';
                if ($name) $cpElementsLookup[$name] = $desc;
                $cpElementsText .= "- Elemen: {$name}\n  Deskripsi: {$desc}\n\n";
            }
        } else {
            $cpElementsText = $cp;
        }

        $prompt = "Anda adalah pakar kurikulum nasional Indonesia (Kurikulum Merdeka). Analisislah Elemen-elemen Capaian Pembelajaran (CP) berikut untuk mata pelajaran '{$subject}', tingkat Kelas '{$grade}', Fase '{$phase}'.\n\n";
        $prompt .= "Elemen CP Resmi:\n{$cpElementsText}\n";
        $prompt .= "Tugas Anda adalah merumuskan Tujuan Pembelajaran (TP) yang diturunkan dari elemen-elemen CP di atas secara spesifik untuk setiap Bab/Materi Pokok berikut:\n";

        // ——— Tulis daftar bab dengan elemen CP per-bab (jika ada) ———
        foreach ($chapters as $index => $chap) {
            $chapLine = ($index + 1) . ". " . trim($chap);
            // Per-chapter CP elements
            $chapCp = isset($chapterCpMap[$index]) ? (array)$chapterCpMap[$index] : [];
            // Back-compat: if empty, also check string-keyed index
            if (empty($chapCp) && isset($chapterCpMap[(string)$index])) {
                $chapCp = (array)$chapterCpMap[(string)$index];
            }
            if (!empty($chapCp)) {
                $chapLine .= " [Elemen CP: " . implode(', ', $chapCp) . "]";
                // Add brief descriptions of each selected element if available
                $descParts = [];
                foreach ($chapCp as $elName) {
                    if (isset($cpElementsLookup[$elName]) && $cpElementsLookup[$elName]) {
                        $descParts[] = "{$elName}: " . $cpElementsLookup[$elName];
                    }
                }
                if (!empty($descParts)) {
                    $chapLine .= "\n   Deskripsi elemen: " . implode("; ", $descParts);
                }
            }
            $prompt .= $chapLine . "\n";
        }

        // ——— Bangun $tpRule ———
        $subjectGroup = \App\Config\KkoTaxonomy::getGroupForSubject($subject);
        $tpRule = "Tuliskan SETIAP TP dengan RUMUS WAJIB: [Kata Kerja Operasional] + [Materi Spesifik] + [Tujuan/Konteks Mapel].\n";
        // KKO dinamis per grup mapel
        $kkoService = new \App\Services\KkoService($subjectGroup);
        $tpRule .= $kkoService->buildPromptKkoText();

        if ($tpsPerChapter === 1) {
            $tpRule .= "Anda merumuskan SATU Tujuan Pembelajaran yang mencakup SELURUH materi bab tersebut.\n";
            $tpRule .= "TP harus menggunakan KKO TINGGI (menganalisis, mengevaluasi, menerapkan) karena harus mewakili seluruh bab.\n";
            $tpRule .= "Format: [KKO tinggi] + [seluruh lingkup materi bab] + [tujuan/konteks sesuai mapel]\n";
            $tpRule .= "CONTOH untuk mapel PAI (1 TP):\n";
            $tpRule .= "  Menganalisis hukum bacaan mad lazim mukhaffaf kilmi, mad lazim mutsaqqal kilmi, mad lazim mukhaffaf harfi, dan mad lazim mutsaqqal harfi serta menerapkannya dalam membaca Al-Qur'an dengan tartil, mencerminkan sikap cinta ilmu dan kesadaran spiritual.\n";
            $tpRule .= "CONTOH untuk mapel IPA (1 TP):\n";
            $tpRule .= "  Menganalisis konsep keanekaragaman hayati tingkat gen, jenis, dan ekosistem serta menyajikan contoh pelestariannya, menumbuhkan sikap cinta lingkungan dan kewargaan terhadap keberlanjutan alam.\n";
        } else {
            $tpRule .= "Anda merumuskan {$tpsPerChapter} Tujuan Pembelajaran yang membagi materi secara runtut.\n";
            $tpRule .= "ALUR pembagian kemampuan:\n";
            $tpRule .= "   - TP ke-1: Memahami — menggunakan KKO DASAR (mengidentifikasi, mendeskripsikan, menjelaskan)\n";
            $tpRule .= "   - TP ke-2: Menganalisis — menggunakan KKO LANJUT (menganalisis, membandingkan, mengklasifikasikan)\n";
            if ($tpsPerChapter >= 3) {
                $tpRule .= "   - TP ke-3: Menerapkan — menggunakan KKO PSIKOMOTOR (menerapkan, mempraktikkan, mendemonstrasikan)\n";
            }
            if ($tpsPerChapter >= 4) {
                $tpRule .= "   - TP ke-4: Mengintegrasikan — mengaitkan dengan konteks nyata/masalah kehidupan sehari-hari\n";
            }
            for ($i = 5; $i <= $tpsPerChapter; $i++) {
                $tpRule .= "   - TP ke-{$i}: Pengembangan lanjutan yang sesuai dengan karakteristik mapel\n";
            }
            $tpRule .= "Setiap TP hanya mencakup BAGIAN dari materi bab, bukan seluruhnya.\n";
            $tpRule .= "CONTOH untuk 3 TP pada mapel PAI (dengan karakter DPL):\n";
            $tpRule .= "  TP-1: \"Mengidentifikasi ciri-ciri mad lazim mukhaffaf kilmi dan mad lazim mutsaqqal kilmi dalam potongan ayat Al-Qur'an dengan cermat sebagai wujud penalaran kritis\"\n";
            $tpRule .= "  TP-2: \"Menganalisis perbedaan mad lazim mukhaffaf harfi dan mad lazim mutsaqqal harfi berdasarkan panjang bacaan dan penyebabnya secara mandiri melalui diskusi kolaboratif\"\n";
            $tpRule .= "  TP-3: \"Menerapkan hukum bacaan mad lazim dalam membaca QS. Al-Baqarah ayat 1-5 dengan tartil sesuai kaidah tajwid, mencerminkan sikap cinta ilmu dan keimanan kepada Allah SWT\"\n";
        }

        // ——— Bangun orientasi nilai untuk prompt ———
        // Transform nama dimensi DPL / Panca Cinta jadi kata sifat natural
        $dplNaturalMap = [
            'Keimanan dan Ketakwaan terhadap Tuhan Yang Maha Esa' => 'religius, beriman, bertakwa',
            'Kewargaan'           => 'peduli sesama, cinta tanah air, berkebhinekaan',
            'Penalaran Kritis'    => 'kritis, analitis, logis',
            'Kreativitas'         => 'kreatif, inovatif, imajinatif',
            'Kolaborasi'          => 'kolaboratif, gotong royong, kerjasama',
            'Kemandirian'         => 'mandiri, bertanggung jawab, percaya diri',
            'Kesehatan'           => 'sehat, bugar, aktif',
            'Komunikasi'          => 'komunikatif, percaya diri, santun',
        ];
        $pancaNaturalMap = [
            'Cinta kepada Tuhan Yang Maha Esa' => 'religius, bersyukur, taqwa',
            'Cinta kepada Ilmu'  => 'cinta ilmu, gemar belajar, tekun',
            'Cinta kepada Lingkungan' => 'peduli lingkungan, cinta alam',
            'Cinta kepada Diri dan Sesama' => 'peduli sesama, empati, saling menghargai',
            'Cinta Tanah Air'    => 'cinta tanah air, nasionalis, bangga sebagai bangsa Indonesia',
        ];
        $allNaturalMap = array_merge($dplNaturalMap, $pancaNaturalMap);

        $isPerChapter = !empty($selectedDpl) && isset($selectedDpl[0]) && is_array($selectedDpl[0]);

        // Kumpulkan semua nilai natural tanpa nempel ke bab tertentu
        $allNaturalValues = [];
        if ($isPerChapter) {
            foreach ($selectedDpl as $vals) {
                foreach ((array)$vals as $v) {
                    $allNaturalValues[] = $allNaturalMap[$v] ?? $v;
                }
            }
            foreach ($selectedPancaCinta as $vals) {
                foreach ((array)$vals as $v) {
                    $allNaturalValues[] = $allNaturalMap[$v] ?? $v;
                }
            }
        } else {
            foreach ((array)$selectedDpl as $v) {
                $allNaturalValues[] = $allNaturalMap[$v] ?? $v;
            }
            foreach ((array)$selectedPancaCinta as $v) {
                $allNaturalValues[] = $allNaturalMap[$v] ?? $v;
            }
        }
        $allNaturalValues = array_values(array_unique(array_filter($allNaturalValues)));

        $karakterPrompt = "\nRUMUSAN TP:\n";
        $karakterPrompt .= "Tulis SETIAP Tujuan Pembelajaran sebagai SATU KALIMAT UTUH dengan rumus WAJIB:\n";
        $karakterPrompt .= "   [KKO] + [materi/substansi] + [konteks/aplikasi] + [nilai yang terintegrasi secara alami]\n\n";
        $karakterPrompt .= "Nilai-nilai yang WAJIB terintegrasi dalam mapel ini: " . (!empty($allNaturalValues) ? implode(', ', $allNaturalValues) : 'karakter positif') . "\n\n";
        $karakterPrompt .= "CONTOH UNTUK MAPEL PAI (nilai: cinta ilmu, religius):\n";
        $karakterPrompt .= "   BENAR: \"Menganalisis hukum bacaan mad lazim dalam QS. Al-Baqarah serta menerapkannya dalam tadarus harian sebagai wujud cinta kepada Al-Qur'an dan kebiasaan membaca dengan tartil.\"\n";
        $karakterPrompt .= "   SALAH: \"Menganalisis hukum bacaan mad lazim dalam QS. Al-Baqarah sebagai wujud Keimanan dan Ketakwaan kepada Tuhan YME.\"\n\n";
        $karakterPrompt .= "ATURAN:\n";
        $karakterPrompt .= "- Nilai yang dipilih (dari daftar di atas) WAJIB ADA dan TERINTEGRASI dalam kalimat TP.\n";
        $karakterPrompt .= "- Gunakan kata-kata yang mencerminkan ESENSI nilai tersebut secara alami, misalnya:\n";
        $karakterPrompt .= "   • 'religius' → \"sebagai wujud cinta kepada Allah\", \"dengan penuh keikhlasan\", \"membiasakan sikap beriman\"\n";
        $karakterPrompt .= "   • 'kritis' → \"melalui penalaran yang mendalam\", \"dengan analisis yang cermat\"\n";
        $karakterPrompt .= "   • 'kreatif' → \"dengan gagasan orisinil\", \"secara inovatif\", \"melalui eksplorasi\"\n";
        $karakterPrompt .= "   • 'cinta tanah air' → \"untuk kemaslahatan bangsa\", \"sebagai wujud kepedulian terhadap lingkungan sekitar\"\n";
        $karakterPrompt .= "   • 'cinta ilmu' → \"dengan rasa ingin tahu yang tinggi\", \"sebagai bentuk kecintaan pada ilmu pengetahuan\"\n";
        $karakterPrompt .= "   • 'gotong royong' → \"secara kolaboratif\", \"melalui diskusi kelompok\", \"dalam kerja sama tim\"\n";
        $karakterPrompt .= "- JANGAN menuliskan nama dimensi resminya ('Keimanan', 'Kewargaan', 'Penalaran Kritis', 'Cinta Tanah Air', dll).\n";
        $karakterPrompt .= "- JANGAN hanya menyebut kegiatan tanpa nilai — pastikan nilai dari daftar di atas terasa dalam kalimat.\n\n";

        $prompt .= $karakterPrompt;

        // Aturan penulisan TP — GABUNGKAN tpRule ke prompt
        $prompt .= "\nAturan Penulisan TP per Bab/Materi Pokok:\n";
        $prompt .= "1. Hasilkan tepat {$tpsPerChapter} Tujuan Pembelajaran (TP) untuk setiap Bab/Materi Pokok.\n";
        $prompt .= "2. {$tpRule}\n";

        if ($schoolType === 'madrasah') {
            $prompt .= "Catatan khusus madrasah: Nilai-nilai Panca Cinta dan DPL sudah tercantum di orientasi di atas. Integrasikan secara alami, jangan dipaksakan.\n";
        }

        $prompt .= "4. Setiap TP harus berurutan dari mudah ke kompleks.\n";
        $prompt .= "5. Alokasikan estimasi Jam Pelajaran (JP) yang logis per TP (kelipatan 2 atau 4 JP, misal 4 JP, 8 JP, atau 12 JP).\n";
        $prompt .= "6. Alokasi JP untuk setiap TP harus MENCERMINKAN kompleksitas TP (TP yang lebih kompleks mendapat JP lebih besar).\n";
        $prompt .= "7. Berikan output HANYA dalam format JSON array dengan skema berikut (tanpa markdown wrapper):\n";
        $prompt .= "[\n";
        $prompt .= "  {\n";
        $prompt .= "    \"code\": \"TP-1\",\n";
        $prompt .= "    \"description\": \"[KKO + Materi + Konteks/Aplikasi + Nilai karakter mengalir]\",\n";
        $prompt .= "    \"allocated_hours\": 8\n";
        $prompt .= "  },\n";
        $prompt .= "  ...\n";
        $prompt .= "]\n";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') {
            return $res;
        }

        $decoded = json_decode($res['data'], true);
        if (!is_array($decoded)) {
            return [
                'status'  => 'error',
                'message' => 'Respon AI tidak dapat diurai sebagai JSON array valid. Detail mentah: ' . substr($res['data'], 0, 150)
            ];
        }

        return [
            'status' => 'success',
            'data'   => $decoded
        ];
    }

    /**
     * Menganalisis TP dan merumuskan Kriteria Ketercapaian Tujuan Pembelajaran (KKTP)
     */
    public function generateKktpForTp(string $tpText, string $cpContext, string $gradeName, string $subject = ''): array
    {
        $prompt = "Anda adalah pakar kurikulum dan asesmen Kurikulum Merdeka. Tugas Anda adalah menyusun Kriteria Ketercapaian Tujuan Pembelajaran (KKTP) untuk SATU Tujuan Pembelajaran (TP) berikut:\n\n";
        $prompt .= "Teks TP: \"{$tpText}\"\n";
        $prompt .= "Konteks CP: \"{$cpContext}\"\n";
        $prompt .= "Tingkat Kelas: {$gradeName}\n\n";

        $prompt .= "Aturan Output:\n";
        $prompt .= "1. Susunlah 3-4 'Indikator Ketercapaian'.\n";
        $prompt .= "2. SETIAP indikator WAJIB menggunakan rumus: [Kata Kerja Operasional] + [Materi Spesifik] + [Bukti yang bisa dinilai melalui asesmen].\n";

        // KKO dinamis per mapel (atau fallback ke hardcoded)
        if (!empty($subject)) {
            $group = \App\Config\KkoTaxonomy::getGroupForSubject($subject);
            $kkoService = new \App\Services\KkoService($group);
            $prompt .= "   - Kata Kerja Operasional untuk {$subject}:\n";
            $prompt .= $kkoService->buildPromptKkoText();
        } else {
            $prompt .= "   - Kata Kerja Operasional yang terukur: mengidentifikasi, menjelaskan, menganalisis, menerapkan, mendemonstrasikan, menyajikan, mempraktikkan, membedakan, mengklasifikasikan, mengevaluasi, dll.\n";
        }
        $prompt .= "   - Materi Spesifik: merujuk langsung pada konten TP, bukan deskripsi umum.\n";
        $prompt .= "   - Bukti asesmen: sesuatu yang bisa diamati/diukur (misal: '...dalam bacaan', '...melalui presentasi', '...pada soal uraian').\n";
        $prompt .= "3. CONTOH indikator yang benar:\n";
        $prompt .= "   - \"Mengidentifikasi ciri-ciri mad lazim mukhaffaf kilmi dalam potongan ayat Al-Qur'an yang dibacakan\"\n";
        $prompt .= "   - \"Menjelaskan perbedaan mad lazim harfi dan mad lazim kilmi melalui diskusi kelompok\"\n";
        $prompt .= "   - \"Menerapkan hukum bacaan mad lazim dalam membaca QS. Al-Baqarah ayat 1-5 dengan tartil\"\n";
        $prompt .= "4. Susunlah rubrik ketercapaian ke dalam 4 level (kategori) dengan deskripsi kualitatif yang SANGAT SPESIFIK DAN KONTEKSTUAL terhadap indikator dan TP di atas. JANGAN HANYA menulis 'Mampu memenuhi 1 indikator', tapi deskripsikan kemampuan nyata murid berdasarkan indikator-indikator tersebut.\n";
        $prompt .= "5. Keempat level tersebut adalah: 'Perlu Bimbingan' (skor 0-59), 'Cukup' (skor 60-69), 'Baik' (skor 70-84), dan 'Sangat Baik' (skor 85-100).\n";
        $prompt .= "6. Berikan output HANYA berupa objek JSON yang valid dengan skema berikut (tanpa blok markdown ```json):\n";
        $prompt .= "{\n";
        $prompt .= "  \"indicators\": [\n";
        $prompt .= "    \"Indikator 1...\",\n";
        $prompt .= "    \"Indikator 2...\"\n";
        $prompt .= "  ],\n";
        $prompt .= "  \"rubric_bimbingan\": \"Deskripsi spesifik untuk siswa yang belum menguasai indikator...\",\n";
        $prompt .= "  \"rubric_cukup\": \"Deskripsi spesifik untuk siswa yang mulai menguasai sebagian indikator...\",\n";
        $prompt .= "  \"rubric_baik\": \"Deskripsi spesifik untuk siswa yang menguasai sebagian besar indikator secara tepat...\",\n";
        $prompt .= "  \"rubric_sangat_baik\": \"Deskripsi spesifik untuk siswa yang menguasai seluruh indikator secara konsisten dan akurat...\"\n";
        $prompt .= "}\n";

        $res = $this->requestAi($prompt, true);
        if ($res['status'] === 'error') {
            return $res;
        }

        $decoded = json_decode($res['data'], true);
        if (!is_array($decoded)) {
            return [
                'status'  => 'error',
                'message' => 'Respon AI tidak dapat diurai sebagai JSON object valid.'
            ];
        }

        return [
            'status' => 'success',
            'data'   => $decoded
        ];
    }

    /**
     * Generate Rencana Pelaksanaan Evaluasi (RPE) / Perencanaan Penilaian
     */
    public function generateRpeForDevice(array $tps, array $promesMap, array $device): array
    {
        $tpDataStr = "";
        foreach ($tps as $index => $tp) {
            $num = $index + 1;
            // Get week info from Promes
            $weekInfo = [];
            if (isset($promesMap[$tp['id']])) {
                foreach ($promesMap[$tp['id']] as $month => $weeks) {
                    foreach ($weeks as $weekNum => $hours) {
                        $weekInfo[] = "Bulan $month Minggu ke-$weekNum ($hours JP)";
                    }
                }
            }
            $weekStr = empty($weekInfo) ? 'Belum dialokasikan' : implode(', ', $weekInfo);

            $tpDataStr .= "TP $num:\n";
            $tpDataStr .= "Tujuan: {$tp['tp_content']}\n";
            $tpDataStr .= "Materi: {$tp['scope']}\n";
            $tpDataStr .= "Alokasi Promes: $weekStr\n\n";
        }

        $prompt = "Anda adalah Asisten Kurikulum Merdeka. Buatkan Rencana Pelaksanaan Evaluasi (Rencana Asesmen).
Berdasarkan data Tujuan Pembelajaran (TP) dan alokasi Program Semester (Promes) di bawah ini:
Mata Pelajaran: {$device['school_subject_id']} Fase: {$device['phase']}

DATA TP DAN PROMES:
$tpDataStr

Tugas Anda:
1. Analisis alokasi minggu dari Promes untuk tiap TP. Asesmen Formatif biasanya dilakukan di minggu terakhir pengajaran TP tersebut. Asesmen Sumatif (jika ada/diperlukan) di akhir materi besar.
2. Tentukan jadwal dan teknik asesmen.

KEMBALIKAN OUTPUT HARUS DALAM FORMAT JSON DENGAN STRUKTUR BERIKUT:
{
  \"jadwal_evaluasi\": [
    {
      \"tp_index\": 1,
      \"materi\": \"Nama materi singkat\",
      \"jenis\": \"Asesmen Formatif / Sumatif\",
      \"waktu_pelaksanaan\": \"Bulan X Minggu ke-Y\",
      \"keterangan\": \"Keterangan pelaksanaan\"
    }
  ],
  \"teknik_asesmen\": [
    {
      \"tp_index\": 1,
      \"teknik\": \"Tes Tertulis / Observasi / Praktik / Penugasan\",
      \"bentuk\": \"Pilihan Ganda / Uraian / Rubrik / Jurnal\",
      \"waktu\": \"Saat / Setelah Pembelajaran\"
    }
  ]
}

Aturan:
- HANYA output JSON valid tanpa teks tambahan atau format markdown.
- `tp_index` adalah nomor urut TP (1, 2, dst).
- Pastikan waktu_pelaksanaan logis sesuai dengan data alokasi Promes.";

        $result = $this->requestAi($prompt, true);

        if ($result['status'] === 'success') {
            $jsonStr = $this->cleanJsonString($result['data']);
            $parsed = json_decode($jsonStr, true);

            if (json_last_error() === JSON_ERROR_NONE && isset($parsed['jadwal_evaluasi'])) {
                return ['status' => 'success', 'data' => $parsed];
            }

            return ['status' => 'error', 'message' => 'Gagal memparsing JSON dari AI.', 'raw' => $result['data']];
        }

        return $result;
    }

    /**
     * AI Autocomplete Program Tahunan (PROTA)
     */
    public function generateProtaDraft(array $tps, int $s1Eff, int $s2Eff, array $device): array
    {
        $tpListStr = "";
        foreach ($tps as $index => $tp) {
            $num = $index + 1;
            $tpListStr .= "- [ID: {$tp['id']}] {$tp['code']}: {$tp['description']} (Baseline JP: {$tp['allocated_hours']})\n";
        }

        $prompt = "Anda adalah Pakar Kurikulum Merdeka Indonesia. Tugas Anda adalah memetakan Tujuan Pembelajaran (TP) berikut ke dalam Program Tahunan (Prota).
Mata Pelajaran: {$device['school_subject_id']} Fase: {$device['phase']} Kelas: {$device['grade_name']}

BATAS MINGGU EFEKTIF DARI RPE:
- Semester 1 (Ganjil): Maksimal {$s1Eff} Minggu
- Semester 2 (Genap): Maksimal {$s2Eff} Minggu

DAFTAR TUJUAN PEMBELAJARAN (TP):
{$tpListStr}

Aturan Penugasan:
1. Bagi TP secara seimbang dan berurutan ke Semester 1 dan Semester 2. TP awal ditempatkan di Semester 1, TP akhir di Semester 2.
2. Alokasikan jumlah minggu (allocated_weeks) untuk setiap TP secara logis. TOTAL allocated_weeks di Semester 1 tidak boleh melebihi {$s1Eff} minggu, dan Semester 2 tidak boleh melebihi {$s2Eff} minggu.
3. Alokasikan jam pelajaran (allocated_hours / JP) secara logis per TP (biasanya 2 s.d 4 JP per minggu, sehingga total JP = allocated_weeks * JP_per_minggu).
4. Tulis catatan singkat (notes) untuk tiap TP (misal: 'Fokus pada kompetensi X').

KEMBALIKAN OUTPUT HANYA DALAM FORMAT JSON ARRAY DENGAN SKEMA BERIKUT (TANPA MARKDOWN WRAPPER):
[
  {
    \"tp_id\": 12,
    \"semester\": 1,
    \"allocated_weeks\": 3,
    \"allocated_hours\": 12,
    \"notes\": \"Catatan singkat\"
  },
  ...
]
Pastikan semua tp_id di atas persis sama dengan ID yang disediakan.";

        $result = $this->requestAi($prompt, true);
        if ($result['status'] === 'success') {
            $jsonStr = $this->cleanJsonString($result['data']);
            $parsed = json_decode($jsonStr, true);

            if (json_last_error() === JSON_ERROR_NONE && is_array($parsed)) {
                return ['status' => 'success', 'data' => $parsed];
            }

            return ['status' => 'error', 'message' => 'Gagal memparsing JSON Prota dari AI.', 'raw' => $result['data']];
        }

        return $result;
    }

    /**
     * AI Autocomplete Program Semester (PROMES)
     */
    public function generatePromesDraft(array $protaTps, array $rpeData, array $device, int $semester): array
    {
        $tpStr = "";
        foreach ($protaTps as $tp) {
            if (isset($tp['semester']) && (int)$tp['semester'] !== $semester) {
                continue;
            }
            $tpStr .= "- [ID: {$tp['tp_id']}] Code: {$tp['tp_code']}: {$tp['tp_description']} (Total JP: {$tp['allocated_hours']} JP, Target: {$tp['allocated_weeks']} Minggu)\n";
        }

        // build RPE Calendar summary
        $rpeCalendarStr = "";
        $months = ($semester == 1) 
            ? [7 => 'Juli', 8 => 'Agustus', 9 => 'September', 10 => 'Oktober', 11 => 'November', 12 => 'Desember']
            : [1 => 'Januari', 2 => 'Februari', 3 => 'Maret', 4 => 'April', 5 => 'Mei', 6 => 'Juni'];

        $semKey = 'sem' . $semester;
        foreach ($months as $mNum => $mName) {
            $mRpe = $rpeData[$semKey][$mNum] ?? [];
            $totalWeeks = $mRpe['total_weeks'] ?? 4;
            $weekStatuses = $mRpe['week_statuses'] ?? [];
            
            $effWeeks = [];
            for ($w = 1; $w <= 5; $w++) {
                if ($w <= $totalWeeks) {
                    $isEff = (bool)($weekStatuses[$w] ?? ($weekStatuses[(string)$w] ?? false));
                    if ($isEff) {
                        $effWeeks[] = "W$w";
                    }
                }
            }
            $rpeCalendarStr .= "- {$mName}: " . (empty($effWeeks) ? "Tidak ada minggu efektif" : implode(", ", $effWeeks)) . "\n";
        }

        $prompt = "Anda adalah Pakar Kurikulum Merdeka Indonesia. Tugas Anda adalah menyusun Program Semester (Promes) Semester {$semester}.
Mata Pelajaran: {$device['school_subject_id']} Kelas: {$device['grade_name']}

DAFTAR TP DAN TARGET ALOKASI JP:
{$tpStr}

KALENDER MINGGU EFEKTIF SEKOLAH (Hanya isi jam pada minggu-minggu berikut):
{$rpeCalendarStr}

Aturan Distribusi JP:
1. Distribusikan total JP setiap TP secara berurutan dan berkelanjutan pada minggu-minggu efektif yang tersedia.
2. HANYA tempatkan JP pada minggu yang ditandai efektif di atas. Jangan menempatkan JP pada minggu yang tidak efektif/tidak terdaftar.
3. Jumlah JP yang didistribusikan per minggu biasanya konstan (misal: 4 JP per minggu, sesuai alokasi JP per minggu mata pelajaran ini).
4. TOTAL JP yang didistribusikan untuk tiap TP harus TEPAT sama dengan target JP TP tersebut.
5. Pastikan urutan pengajaran logis (TP 1 diajarkan dulu, setelah selesai baru lanjut TP 2, dst).

KEMBALIKAN OUTPUT HANYA DALAM FORMAT JSON ARRAY DENGAN SKEMA BERIKUT (TANPA MARKDOWN WRAPPER):
[
  {
    \"tp_id\": 12,
    \"month\": 7,
    \"week_number\": 3,
    \"allocated_hours\": 4
  },
  ...
]
Di mana:
- `tp_id` adalah ID TP.
- `month` adalah nomor bulan (7-12 untuk S1, 1-6 untuk S2).
- `week_number` adalah nomor minggu (1-5).
- `allocated_hours` adalah JP yang dialokasikan pada minggu tersebut (kelipatan JP mingguan, misal 4).";

        $result = $this->requestAi($prompt, true);
        if ($result['status'] === 'success') {
            $jsonStr = $this->cleanJsonString($result['data']);
            $parsed = json_decode($jsonStr, true);

            if (json_last_error() === JSON_ERROR_NONE && is_array($parsed)) {
                return ['status' => 'success', 'data' => $parsed];
            }

            return ['status' => 'error', 'message' => 'Gagal memparsing JSON Promes dari AI.', 'raw' => $result['data']];
        }

        return $result;
    }

    // ================================================================
    // RPM FIELD-BY-FIELD GENERATION METHODS
    // ================================================================

    /**
     * Generate satu field modul ajar berdasarkan field key
     */
    public function generateModulField(string $field, array $context): array
    {
        $device = $context['device'];
        $tp = $context['tp'];
        $pertemuan = $context['pertemuan'] ?? null;

        $subject = $device['subject_name'] ?? '';
        $grade = $device['grade_name'] ?? '';
        $phase = $device['phase'] ?? '';
        $schoolType = $device['school_type'] ?? 'umum';
        $tpDesc = $tp['description'] ?? '';
        $tpCode = $tp['code'] ?? '';
        $cpContent = $device['cp_content'] ?? '';
        // Gunakan nilai per-chapter dari context, fallback ke global device
        $dpl = $context['dpl_text'] ?? $device['dpl'] ?? 'Profil Pelajar Pancasila';
        $pancaCinta = $context['panca_cinta_text'] ?? $device['panca_cinta'] ?? '';
        $schoolTypeLabel = $schoolType === 'madrasah' ? 'Madrasah' : 'Sekolah Umum';

        switch ($field) {

            case 'kompetensi_awal':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $materiCtx = !empty($context['materi_esensial']) ? "\nMateri esensial yang akan dipelajari:\n" . $context['materi_esensial'] : '';
                $prompt = "Anda adalah pakar kurikulum Indonesia. Rumuskan kompetensi awal (prasyarat) yang sudah harus dikuasai siswa sebelum mempelajari materi berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}
TP: {$tpDesc}{$materiCtx}

Kompetensi awal berfokus pada PENGETAHUAN dan KETERAMPILAN DASAR yang sudah harus dimiliki siswa, BUKAN materi yang akan dipelajari di bab ini.

ATURAN FORMAT OUTPUT:
- Berupa narasi paragraf (rata kiri-kanan / justify).
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'sarana_prasarana':
                $modelCtx = !empty($context['model_pembelajaran']) ? "\nModel pembelajaran yang digunakan: {$context['model_pembelajaran']}" : '';
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $schoolTypeLabel = $schoolType === 'madrasah' ? 'Madrasah' : 'Sekolah Umum';
                $prompt = "Buat daftar sarana dan prasarana yang mendukung model pembelajaran yang sudah ditentukan:

BAB/Materi: {$chapterName}
Mata Pelajaran: {$subject}, Kelas: {$grade}
Jenis Sekolah: {$schoolTypeLabel}
TP: {$tpDesc}{$modelCtx}
Karakter yang diintegrasikan: {$dpl} {$pancaCinta}

TULIS PERSIS 4 BARIS BERIKUT (isi bagian ... ):

Media Pembelajaran: ...
Alat/Bahan: ...
Lingkungan Belajar: ...
Materi Pendukung: ...

KETENTUAN:
- Sesuaikan dengan model pembelajaran yang sudah dipilih. Jangan buat daftar yang bertentangan dengan model tersebut.
- Sesuaikan dengan jenis sekolah (umum/madrasah) dan kondisi riil sekolah.
- Jika memungkinkan, integrasikan nilai karakter DPL/Panca Cinta secara natural dalam pilihan media atau lingkungan belajar.
- Pertimbangkan kebutuhan diferensiasi untuk peserta didik dengan hambatan belajar.

CONTOH OUTPUT UNTUK MODEL PBL DI MADRASAH:
Media Pembelajaran: Proyektor, video kasus, LKPD berbasis masalah, kartu ayat, alat presentasi
Alat/Bahan: Spidol warna, kertas plano, sticky notes, alat tulis, Qalam/LCD
Lingkungan Belajar: Ruang kelas dengan tata letak kelompok, pojok baca Al-Qur'an, akses internet
Materi Pendukung: Artikel kasus, buku paket, lembar kerja, sumber daring, mushaf Al-Qur'an

JAWAB HANYA 4 BARIS ITU SAJA. TANPA JUDUL, TANPA PENJELASAN LAIN.";
                break;

            case 'target_peserta_didik':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $materiCtx = !empty($context['materi_esensial']) ? "\nMateri esensial: " . substr($context['materi_esensial'], 0, 200) . '...' : '';
                $prompt = "Buat target peserta didik berdasarkan karakteristik materi berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}
TP: {$tpDesc}{$materiCtx}

TULIS PERSIS 3 BARIS BERIKUT (isi bagian ... ):
Reguler: [Karakteristik siswa reguler] : [Strategi pembelajaran yang sesuai]
CIBI: [Karakteristik siswa CIBI/berbakat] : [Strategi pengayaan yang sesuai]
Hambatan Belajar: [Karakteristik siswa dengan hambatan] : [Strategi remedial/diferensiasi yang sesuai]

KETENTUAN:
- Deskripsikan karakteristik siswa secara spesifik, bukan generik.
- Strategi pembelajaran disesuaikan dengan materi dan model pembelajaran.
- Gunakan Bahasa Indonesia yang baik.

JAWAB HANYA 3 BARIS ITU SAJA. TANPA JUDUL, TANPA PENJELASAN LAIN.";
                break;
            case 'model_pembelajaran':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $kktpCtx = !empty($context['kktp_text']) ? "
Indikator KKTP dari TP terkait:
" . $context['kktp_text'] : '';
                $allTpDesc = !empty($context['tp_descriptions']) ? "
Seluruh TP dalam BAB ini:
" . implode("
", array_map(fn($i, $d) => ($i+1) . ". " . $d, array_keys($context['tp_descriptions']), $context['tp_descriptions'])) : '';
                $prompt = "Tentukan model, pendekatan, metode, dan strategi pembelajaran yang tepat:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase}){$allTpDesc}{$kktpCtx}
Karakter yang diintegrasikan: {$dpl} {$pancaCinta}

TULIS PERSIS format TABEL berikut. Gunakan simbol | sebagai pemisah:

No|Komponen|Deskripsi
1|Model Utama|[Nama model + alasan pemilihan berdasarkan karakteristik materi dan TP]
2|Pendekatan|[Pendekatan pembelajaran yang digunakan, sesuaikan dengan model]
3|Metode|[Metode pembelajaran yang digunakan]
4|Strategi Diferensiasi|[Strategi untuk konten, proses, dan produk]

CONTOH FORMAT OUTPUT:
1|Model Utama|Problem Based Learning (PBL) — dipilih karena materi berupa konsep abstrak yang membutuhkan analisis melalui pemecahan masalah kontekstual, sesuai dengan TP yang menuntut kemampuan menganalisis.
2|Pendekatan|Saintifik dan Deep Learning — mengintegrasikan observasi, eksplorasi, dan refleksi mendalam.
3|Metode|Diskusi kelompok, tanya-jawab, presentasi, penugasan individu.
4|Strategi Diferensiasi|Konten: sumber belajar beragam sesuai gaya belajar; Proses: bimbingan bertahap; Produk: pilihan bentuk presentasi (lisan, tulisan, visual).

ATURAN:
- Tepat 4 baris dengan format \"No|Komponen|Deskripsi\".
- Pilih model yang paling sesuai dengan konten materi dan indikator KKTP, bukan model generik.
- Integrasikan karakter DPL/Panca Cinta secara natural dalam deskripsi.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'pemahaman_bermakna':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $materiCtx = !empty($context['materi_esensial']) ? "\nMateri esensial:\n" . $context['materi_esensial'] : '';
                $prompt = "Buatlah pemahaman bermakna yang menjelaskan MANFAAT dan RELEVANSI mempelajari materi ini bagi kehidupan siswa, baik saat ini maupun di masa depan.

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}{$materiCtx}
Jenjang: {$schoolTypeLabel}
Nilai Karakter: {$dpl}
Nilai Cinta: {$pancaCinta}

Pemahaman bermakna menjawab pertanyaan: \"Mengapa siswa perlu mempelajari ini?\" dan \"Apa manfaatnya dalam kehidupan nyata?\"

ATURAN FORMAT OUTPUT:
- Berupa 1-2 paragraf narasi justify.
- Hubungkan materi dengan fenomena atau pengalaman sehari-hari siswa.
- Integrasikan nilai karakter dan cinta secara alami dalam narasi.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'pertanyaan_pemantik':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $materiCtx = !empty($context['materi_esensial']) ? "\nMateri esensial:\n" . $context['materi_esensial'] : '';
                $prompt = "Buatlah pertanyaan pemantik untuk MEMULAI diskusi dan membangkitkan rasa ingin tahu siswa terhadap materi berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}
TP: {$tpDesc}{$materiCtx}
Nilai Karakter: {$dpl}
Nilai Cinta: {$pancaCinta}

Pertanyaan pemantik bersifat terbuka, kontekstual, dan mendorong siswa berpikir kritis — bukan sekadar pertanyaan hafalan.

ATURAN FORMAT OUTPUT:
- Tepat 4 pertanyaan.
- Format numbered list: 1. ...\n2. ...\n3. ...\n4. ...
- Pertanyaan harus memantik rasa ingin tahu dan relevan dengan kehidupan sehari-hari siswa.
- Jika relevan, kaitkan dengan nilai karakter atau cinta secara alami.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'materi_esensial':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $modelCtx = !empty($context['model_pembelajaran']) ? "\nModel pembelajaran: {$context['model_pembelajaran']}" : '';
                $prompt = "Buat materi esensial untuk pembelajaran berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}
CP: {$cpContent}{$modelCtx}
Jenis Sekolah: {$schoolTypeLabel}
Nilai Karakter (DPL) yang diintegrasikan: {$dpl}
Nilai Cinta (Panca Cinta) yang diintegrasikan: {$pancaCinta}

TULIS PERSIS format berikut (isi bagian ... ):

1. Materi Esensial: [Uraian materi lengkap dan substansial, runtut dari mudah ke kompleks, siap digunakan sebagai bahan ajar]
2. Konteks Fenomena Sehari-hari: [Fenomena nyata dalam kehidupan yang relevan dengan materi]
3. Identifikasi Masalah & Contoh Pemecahan: [Masalah spesifik terkait materi beserta contoh pemecahannya dalam konteks kehidupan sehari-hari]
4. Integrasi Karakter & Cinta: [Integrasi nilai {$dpl} dan {$pancaCinta} secara alami dan kontekstual]

KETENTUAN AKURASI:
- Pastikan SETIAP fakta, angka, aturan, dan definisi yang Anda berikan AKURAT secara ilmiah dan sesuai kurikulum.
- JANGAN membuat data atau informasi yang tidak dapat diverifikasi. Jika tidak yakin, gunakan frasa '(perlu diverifikasi)'.
- Cantumkan sumber referensi yang digunakan untuk fakta utama (misal: buku paket, Permendikbud, KMA, jurnal).
- Jika ada istilah teknis, pastikan definisinya sesuai kaidah dan tidak mengubah makna.

KETENTUAN MATERI:
- Semua 4 bagian WAJIB terisi. Jangan ada yang kosong.
- Contoh pemecahan masalah harus konkret dan dapat diterapkan siswa dalam kehidupan nyata.
- Integrasikan nilai DPL dan Panca Cinta secara alami dan kontekstual, muncul dari substansi materi bukan dipaksakan.
- Sesuaikan kedalaman dan contoh dengan jenis sekolah ({$schoolTypeLabel}) serta model pembelajaran yang digunakan.
- Gunakan Bahasa Indonesia yang baik dan benar.

JAWAB HANYA FORMAT ITU SAJA. TANPA JUDUL, TANPA PENJELASAN LAIN.
Tulis dalam Bahasa Indonesia.";
                break;

            case 'skenario_pendahuluan':
                $subMateriCtx = !empty($context['sub_materi_text']) ? "
Sub materi pertemuan ini: {$context['sub_materi_text']}" : '';
                $modelCtx = !empty($context['model_pembelajaran']) ? ", Model: {$context['model_pembelajaran']}" : '';

                $prompt = "Buat skenario kegiatan PENDAHULUAN untuk pertemuan ke-{$pertemuan}:
Mata Pelajaran: {$subject}, Kelas: {$grade}, Fase: {$phase}
TP: {$tpDesc}{$subMateriCtx}{$modelCtx}
Jenjang: {$schoolTypeLabel}
Nilai Karakter: {$dpl}
Nilai Cinta: {$pancaCinta}

Setiap komponen wajib mendeskripsikan aktivitas GURU dan aktivitas SISWA secara berpasangan, bukan hanya satu sisi.

TULIS PERSIS format TABEL berikut (7 baris). Gunakan simbol | sebagai pemisah:

No|Komponen|Deskripsi
1|Kesiapan Belajar|[Aktivitas guru menyiapkan kelas + aktivitas siswa merespon]
2|Apersepsi|[Aktivitas guru mengaitkan materi sebelumnya + aktivitas siswa mengingat/merespon]
3|Motivasi|[Aktivitas guru memberikan dorongan + aktivitas siswa menanggapi]
4|Asesmen Diagnostik|a. Kognitif: Kuis lisan 3 pertanyaan — (1) prasyarat: [pertanyaan tentang materi prasyarat] (2) pengetahuan awal: [pertanyaan menggali pemahaman awal sub-materi] (3) pengetahuan awal: [pertanyaan menggali pemahaman awal sub-materi] — b. Nonkognitif: Observasi kesiapan belajar — mengamati sikap, fokus, dan kesiapan siswa mengikuti pembelajaran — c. Tindak Lanjut (integrasi hasil kognitif dan nonkognitif): Kategori Tinggi: [pengayaan] | Kategori Sedang: [pembelajaran reguler] | Kategori Perlu Pendampingan: [bimbingan konsep dasar]
5|Tujuan Pembelajaran|[Aktivitas guru menyampaikan tujuan + aktivitas siswa memahami target belajar]
6|Gambaran Kegiatan|[Aktivitas guru menjelaskan langkah + aktivitas siswa mengetahui alur belajar]
7|Penguatan Karakter|[Aktivitas guru menguatkan nilai {$dpl} dan {$pancaCinta} + aktivitas siswa menginternalisasi]

ATURAN:
- Tepat 7 baris dengan format \"No|Komponen|Deskripsi\".
- Setiap deskripsi HARUS berisi aktivitas GURU dan aktivitas SISWA secara berpasangan.
- JANGAN gunakan simbol | di dalam deskripsi. Gunakan koma, titik koma, atau tanda pisah (—) sebagai pengganti.
- Baris 4 (Asesmen Diagnostik): gunakan — sebagai pemisah antar sub-bagian a, b, c.
- Baris 7 (Penguatan Karakter): integrasikan nilai karakter dan cinta secara alami.
- Sesuaikan apersepsi dan motivasi dengan sub-materi pertemuan ini.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'skenario_inti':
                $subMateriCtx = !empty($context['sub_materi_text']) ? "\nSub materi pertemuan ini: {$context['sub_materi_text']}" : '';
                $modelCtx = !empty($context['model_pembelajaran']) ? "\nModel pembelajaran: {$context['model_pembelajaran']}" : '';
                $iktpCtx = !empty($context['iktp_list']) ? "\nIKTP pertemuan ini:\n" . implode("\n", array_map(fn($i, $v) => ($i+1) . ". " . $v, array_keys($context['iktp_list']), array_values($context['iktp_list']))) : '';

                $prompt = "Buat skenario kegiatan INTI untuk pertemuan ke-{$pertemuan} dengan model pembelajaran yang sudah ditentukan:
Mata Pelajaran: {$subject}, Kelas: {$grade}, Fase: {$phase}
TP: {$tpDesc}{$subMateriCtx}{$modelCtx}{$iktpCtx}
Jenjang: {$schoolTypeLabel}
Nilai Karakter: {$dpl}
Nilai Cinta: {$pancaCinta}

Kegiatan Inti wajib mengikuti 3 TAHAPAN DEEP LEARNING berikut:
TAHAP 1 — Memahami (Input): Siswa mengeksplorasi konsep, mengamati, mengidentifikasi masalah. Guru memfasilitasi dengan sumber belajar.
TAHAP 2 — Memecahkan Masalah (Proses): Siswa berdiskusi, menganalisis, dan memecahkan masalah secara kolaboratif. Guru membimbing dan menguatkan.
TAHAP 3 — Mengaplikasi (Output): Siswa menyajikan hasil, mendemonstrasikan pemahaman. Guru memberikan umpan balik.

Setiap tahap HARUS mencantumkan kegiatan GURU dan kegiatan SISWA secara berpasangan serta mengintegrasikan asesmen FORMATIF di dalam proses (bukan terpisah).

TULIS PERSIS format TABEL berikut (3 baris data + 1 baris header). Gunakan simbol | sebagai pemisah:

No|Tahap|Kegiatan Guru|Kegiatan Siswa
1|Tahap 1 — Memahami|[Aktivitas guru memfasilitasi eksplorasi]|[Aktivitas siswa mengeksplorasi konsep]
2|Tahap 2 — Memecahkan Masalah|[Aktivitas guru membimbing pemecahan masalah]|[Aktivitas siswa berdiskusi dan memecahkan masalah]
3|Tahap 3 — Mengaplikasi|[Aktivitas guru memberikan umpan balik]|[Aktivitas siswa menyajikan hasil]

ATURAN:
- Tepat 3 baris data dengan format \"No|Tahap|Kegiatan Guru|Kegiatan Siswa\".
- Setiap baris HARUS mencantumkan aktivitas GURU dan SISWA.
- Integrasikan asesmen formatif (observasi, rubrik, atau ceklist) dalam deskripsi tahapan.
- Sesuaikan dengan model pembelajaran yang dipilih.
- Aktivitas siswa merujuk pada IKTP yang telah ditetapkan.
- Integrasikan nilai karakter dan cinta secara alami dalam tahap 2 dan 3.
- JANGAN gunakan simbol | di luar pemisah kolom.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'skenario_penutup':
                $subMateriCtx = !empty($context['sub_materi_text']) ? "\nSub materi pertemuan ini: {$context['sub_materi_text']}" : '';
                $prompt = "Buat skenario kegiatan PENUTUP untuk pertemuan ke-{$pertemuan}:
Mata Pelajaran: {$subject}, Kelas: {$grade}, Fase: {$phase}
TP: {$tpDesc}{$subMateriCtx}
Jenjang: {$schoolTypeLabel}
Nilai Karakter: {$dpl}
Nilai Cinta: {$pancaCinta}

Kegiatan Penutup mencakup: refleksi, penguatan konsep, umpan balik guru, penyampaian rencana pertemuan berikutnya, dan tugas/tindak lanjut.

Setiap komponen HARUS mencantumkan aktivitas GURU dan SISWA secara berpasangan.

TULIS PERSIS format TABEL berikut (5 baris data + 1 baris header). Gunakan simbol | sebagai pemisah:

No|Komponen|Deskripsi
1|Refleksi|[Aktivitas guru memandu refleksi + aktivitas siswa merefleksikan]
2|Penguatan Konsep|[Aktivitas guru merangkum + aktivitas siswa mencatat]
3|Umpan Balik|[Aktivitas guru memberikan umpan balik + aktivitas siswa menanggapi]
4|Tugas/Tindak Lanjut|[Aktivitas guru memberikan tugas + aktivitas siswa menerima]
5|Rencana Pertemuan Berikutnya|[Aktivitas guru menyampaikan rencana + aktivitas siswa mengetahui]

ATURAN:
- Tepat 5 baris data dengan format \"No|Komponen|Deskripsi\".
- Setiap deskripsi HARUS berisi aktivitas GURU dan SISWA.
- JANGAN gunakan simbol | di dalam deskripsi.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

case 'asesmen_diagnostik':
                $prompt = "Buat instrumen Asesmen Diagnostik (Awal) untuk mengukur kesiapan belajar siswa secara umum:

Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}
Jenjang: {$schoolTypeLabel}

TULIS PERSIS format berikut:

1. Kognitif: [bentuk/teknik — contoh pertanyaan kesiapan kognitif]
2. Nonkognitif: [observasi — aspek yang diamati: kesiapan, fokus, motivasi]
3. Tindak Lanjut: [pemetaan hasil — tindak lanjut per kategori siswa]

KETENTUAN:
- Tepat 3 baris dengan format \"Nomor. Kategori: Deskripsi\".
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'asesmen_formatif':
                $prompt = "Buat Asesmen Formatif untuk materi: {$tpDesc}
Mata Pelajaran: {$subject}, Kelas: {$grade}

OUTPUT HARUS PERSIS 6 BARIS BERIKUT:

BENTUK ASESMEN: (isi sendiri)
INSTRUMEN: (isi sendiri)
RUBRIK PENILAIAN:
Sangat Baik: (isi sendiri)
Baik: (isi sendiri)
Cukup: (isi sendiri)
Kurang: (isi sendiri)

CATATAN PENTING:
- Baris \"Kurang: ...\" HARUS ADA DAN TERISI. Tidak boleh kosong.
- Jangan gunakan tanda kurung siku [ ].
- Jawab langsung 6 baris di atas tanpa tambahan apapun.";
                break;

            case 'asesmen_sumatif':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $prompt = "Buat Asesmen Sumatif untuk materi berikut:
BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}, Kelas: {$grade}, Fase: {$phase}
TP: {$tpDesc}
Jenjang: {$schoolTypeLabel}

TULIS PERSIS format berikut:

A. PILIHAN GANDA
[5 soal pilihan ganda. Setiap soal: nomor. soal]
[Setelah semua soal, tulis baris: Kunci Jawaban: 1. ... 2. ... 3. ... 4. ... 5. ...]

B. ISIAN SINGKAT
[3 soal isian singkat. Setiap soal: nomor. soal]
[Setelah semua soal, tulis baris: Kunci Jawaban: 1. ... 2. ... 3. ...]

C. ESAI
[2 soal essay. Setiap soal: nomor. soal]
[Setelah semua soal, tulis baris: Pedoman Penskoran: 1. ... 2. ...]

KETENTUAN SOAL:
- Seluruh soal WAJIB merujuk pada BAB/Materi Pokok, CP, dan TP yang telah ditetapkan.
- Soal pilihan ganda: rumuskan pokok soal dengan jelas, opsi jawaban homogen dan logis.
- Soal isian singkat: rumuskan kalimat rumpang atau pertanyaan langsung dengan jawaban spesifik.
- Soal essay: rumuskan permasalahan yang menuntut penalaran dan analisis.
- Tingkat kesulitan bervariasi (mudah, sedang, sulit).
- Gunakan Bahasa Indonesia sesuai kaidah PUEBI.
- Langsung ke konten tanpa kata pengantar.";
                break;

            case 'pengayaan':
                $prompt = "Buatlah program PENGAYAAN untuk siswa yang melampaui KKM atau memiliki pemahaman tinggi:

Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}
Jenjang: {$schoolTypeLabel}

ATURAN FORMAT OUTPUT:
- Gunakan bullet list (diawali dengan -).
- Opsi kegiatan: proyek mandiri, tutor sebaya, riset mini, latihan lanjutan.
- Setiap opsi diikuti deskripsi singkat.
- Sesuaikan dengan karakteristik materi dan kondisi sekolah.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'remedial':
                $prompt = "Buatlah program REMEDIAL untuk siswa yang belum mencapai KKM:

Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}
Jenjang: {$schoolTypeLabel}

TULIS PERSIS format berikut (4 baris):

1. Identifikasi Masalah: [deskripsi spesifik kesulitan siswa terkait materi ini]
2. Strategi Pembelajaran Ulang: [metode bimbingan yang sesuai dengan materi]
3. Batas Waktu: [waktu pelaksanaan remedial yang realistis]
4. Instrumen Ulang: [bentuk dan teknik asesmen ulang]

ATURAN:
- Tepat 4 baris dengan format \"Nomor. Kategori: Deskripsi\".
- Sesuaikan dengan karakteristik materi dan kondisi sekolah.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            case 'lkpd':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $pertemuanData = $context['pertemuan_data'] ?? [];
                $pertemuan = $pertemuanData['pertemuan'] ?? $context['pertemuan'] ?? 1;
                $subMateri = $pertemuanData['sub_materi'] ?? $context['sub_materi_text'] ?? '';
                $pendahuluan = $pertemuanData['pendahuluan'] ?? '';
                $inti = $pertemuanData['inti'] ?? '';
                $penutup = $pertemuanData['penutup'] ?? '';
                $iktpList = $pertemuanData['iktp'] ?? $context['iktp_list'] ?? [];

                $iktpStr = '';
                if (!empty($iktpList)) {
                    $iktpStr = "IKTP pertemuan ini:\n";
                    foreach ((array)$iktpList as $i => $ik) {
                        $iktpStr .= ($i + 1) . ". " . $ik . "\n";
                    }
                }

                $prompt = "Anda adalah pengembang perangkat pembelajaran Kurikulum Merdeka. Buat konten Lembar Kerja Peserta Didik (LKPD) untuk SATU PERTEMUAN berdasarkan data berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}, Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}
Pertemuan ke-: {$pertemuan}
Sub-Materi: {$subMateri}
TP: {$tpDesc}
{$iktpStr}
Kegiatan Inti (Tahap 1, 2, 3):
{$inti}

Buatlah LKPD yang berisi 3 bagian berikut dalam BAHASA SISWA (bukan narasi guru), langsung berupa instruksi/pertanyaan yang akan dikerjakan siswa:

1. LANGKAH KEGIATAN: Turunan dari kegiatan inti. Tulis 3-5 langkah konkret yang harus dilakukan siswa. Awali dengan kata kerja. Format numbered list.

2. SOAL/TUGAS: Soal atau tugas yang dikerjakan siswa, diturunkan dari kegiatan inti Tahap 2 dan 3. Sesuaikan dengan IKTP. Tulis 2-4 soal/tugas. Format numbered list.

3. KESIMPULAN: 1 pertanyaan refleksi yang mengarahkan siswa menyimpulkan pembelajaran hari ini.

Berikan output HANYA berupa objek JSON yang valid dengan skema berikut (tanpa markdown wrapper):
{
  \"lkpd_langkah\": \"1. ...\\n2. ...\\n3. ...\",
  \"lkpd_soal\": \"1. ...\\n2. ...\",
  \"lkpd_kesimpulan\": \"Tulis pertanyaan refleksi...\"
}

KETENTUAN:
- Semua bagian HARUS terisi.
- Bahasa langsung ke siswa, gunakan kata \"kamu\".
- Langkah kegiatan = instruksi (bukan narasi penjelasan).
- Soal/tugas = butir soal nyata (bukan petunjuk mengerjakan soal).
- Jangan menyertakan identitas siswa (nama/kelas) atau tujuan — itu sudah ada di template LKPD.";


                $res = $this->requestAi($prompt, true);
                if ($res['status'] === 'error') return $res;

                $decoded = json_decode($res['data'], true);
                if (!is_array($decoded)) {
                    return [
                        'status' => 'error',
                        'message' => 'Respon AI untuk LKPD tidak dapat diurai sebagai JSON valid.'
                    ];
                }

                return [
                    'status' => 'success',
                    'data'   => [
                        'lkpd_langkah'   => $decoded['lkpd_langkah'] ?? '',
                        'lkpd_soal'      => $decoded['lkpd_soal'] ?? '',
                        'lkpd_kesimpulan'=> $decoded['lkpd_kesimpulan'] ?? '',
                    ]
                ];
                break;

            case 'glosarium':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $materiCtx = !empty($context['materi_esensial']) ? "\nMateri esensial bab ini:\n" . substr($context['materi_esensial'], 0, 500) . '...' : '';
                $prompt = "Bertindaklah sebagai ahli kurikulum Indonesia, pengembang perangkat ajar, dan editor akademik.

Tugas Anda adalah menyusun Glosarium untuk Rencana Pembelajaran (RPM/Modul Ajar) dengan data berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}, Kelas: {$grade} (Fase {$phase})
TP: {$tpDesc}{$materiCtx}
Jenjang: {$schoolTypeLabel}

Ketentuan:
1. Ambil hanya istilah-istilah penting yang muncul dalam materi, tujuan pembelajaran, asesmen, dan aktivitas pembelajaran.
2. Jangan memasukkan istilah yang sudah umum dipahami peserta didik.
3. Setiap istilah ditulis secara alfabetis.
4. Berikan definisi singkat, jelas, akurat, dan sesuai tingkat perkembangan peserta didik.
5. Gunakan bahasa Indonesia yang baku dan mudah dipahami.
6. Jika terdapat istilah asing, tuliskan istilah asing tersebut dan artinya.
7. Hindari definisi yang terlalu panjang atau seperti artikel ensiklopedia.
8. Pastikan definisi konsisten dengan CP, TP, dan materi pembelajaran.
9. Jika terdapat istilah yang berpotensi menimbulkan salah tafsir, berikan definisi yang paling relevan dengan konteks pembelajaran.

Hasil akhir disajikan dalam format tabel berikut (gunakan | sebagai pemisah):

No|Istilah|Definisi
1|[istilah 1]|[definisi]
2|[istilah 2]|[definisi]
3|[istilah 3]|[definisi]
...

Susun secara alfabetis (berdasarkan abjad). Minimal 5 istilah. Jangan ada istilah yang sama.
JAWAB HANYA FORMAT TABEL ITU SAJA. TANPA JUDUL, TANPA PENJELASAN LAIN.";
                break;

            case 'sub_materi':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $modelCtx = !empty($context['model_pembelajaran']) ? "
Model & Pendekatan Pembelajaran: {$context['model_pembelajaran']}" : '';
                $kktpCtx = !empty($context['kktp_text']) ? "
Indikator KKTP:
" . $context['kktp_text'] : '';
                $allTpDescs = $context['tp_descriptions'] ?? [];
                $subMateriCount = (int)($context['sub_materi_count'] ?? 1);

                $tpListStr = '';
                if (!empty($allTpDescs)) {
                    $tpListStr = "\nDaftar TP terkait:\n";
                    foreach ($allTpDescs as $i => $desc) {
                        $tpListStr .= ($i + 1) . ". " . $desc . "\n";
                    }
                }

                $prompt = "Anda adalah pakar kurikulum Indonesia. Berdasarkan BAB/MATERI POKOK berikut, bagi materi ini menjadi {$subMateriCount} sub-materi yang runtut dan logis.

BAB/MATERI POKOK: {$chapterName}
Jenjang: {$schoolTypeLabel}{$tpListStr}{$kktpCtx}{$modelCtx}

TULIS SETIAP SUB-MATERI DALAM 1 BARIS (tanpa penomoran di awal baris), dengan format berikut:
[Isi sub-materi 1]
[Isi sub-materi 2]
[Isi sub-materi 3]
...

CONTOH:
Konsep dan ciri-ciri keanekaragaman hayati
Tingkatan keanekaragaman hayati (gen, jenis, ekosistem)
Manfaat dan peranan keanekaragaman hayati
Upaya pelestarian keanekaragaman hayati

ATURAN:
- Tepat {$subMateriCount} baris. WAJIB persis {$subMateriCount} baris, tidak lebih, tidak kurang.
- Setiap baris langsung berisi sub-materi tanpa nomor, tanpa strip, tanpa bullet.
- Runtut dari konsep dasar ke aplikasi/kompleks.
- Gunakan Bahasa Indonesia yang baik dan benar.
- Format HANYA teks, setiap sub-materi dalam 1 baris dan dipisahkan dengan baris baru (\\n).";
                break;

            case 'iktp':
                $subMateri = $context['sub_materi_text'] ?? '';
                $iktpCount = (int)($context['iktp_count'] ?? 3);

                // Ambil KKO spesifik per mapel
                $subjectGroup = \App\Config\KkoTaxonomy::getGroupForSubject($subject);
                $kkoService = new \App\Services\KkoService($subjectGroup);
                $kkoIKTP = $kkoService->buildPromptKkoText();

                $prompt = "Anda adalah pakar kurikulum Indonesia. Berdasarkan SUB-MATERI berikut, rumuskan {$iktpCount} Indikator Ketercapaian Tujuan Pembelajaran (IKTP) yang SPESIFIK, TERUKUR, dan dapat DIAMATI.

SUB-MATERI: {$subMateri}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}

IKTP adalah perilaku atau kemampuan yang dapat diamati dan diukur, bukan sekadar daftar materi yang dipelajari.

TULIS SETIAP IKTP DALAM 1 BARIS dimulai dengan KKO, dengan format:
[KKO] [materi/objek spesifik dari sub-materi] [kriteria ketercapaian]

DAFTAR KKO yang tersedia untuk {$subject}:
{$kkoIKTP}

PILIH KKO dari daftar di atas. Jangan menggunakan KKO di luar daftar tersebut.
Variasikan KKO yang digunakan, jangan monoton.

ATURAN:
- Tepat {$iktpCount} baris. WAJIB persis {$iktpCount} baris, tidak lebih, tidak kurang.
- Setiap baris DIAWALI dengan KKO dari daftar KKO {$subject}.
- Setelah KKO, sebutkan MATERI/OBJEK SPESIFIK dari sub-materi, bukan materi umum.
- Jika memungkinkan, tambahkan KRITERIA atau konteks ketercapaian (misal: 'dengan tepat', 'secara mandiri', 'melalui diskusi').
- URUTKAN dari KKO yang paling sederhana ke paling kompleks (dari mudah ke sulit).
- Jangan menulis daftar materi saja tanpa KKO.
- Gunakan Bahasa Indonesia yang baik dan benar.
- Format HANYA teks, setiap IKTP dalam 1 baris dan dipisahkan dengan baris baru (\\n).";
                break;

            case 'daftar_pustaka':
                $chapterName = $context['chapter_name'] ?? 'Materi Pokok';
                $prompt = "Bertindaklah sebagai ahli kurikulum dan pengembang perangkat ajar Indonesia. Buatlah daftar pustaka yang digunakan sebagai referensi dalam penyusunan Modul Ajar berikut:

BAB/Materi Pokok: {$chapterName}
Mata Pelajaran: {$subject}
Kelas: {$grade} (Fase {$phase})
Jenjang: {$schoolTypeLabel}

TULIS PERSIS format berikut setiap baris (APA Style, urut alfabetis):

[Penulis]. (Tahun). Judul Buku. Kota: Penerbit.
[Penulis]. (Tahun). Judul Artikel. Jurnal/Media.

KETENTUAN:
- Cantumkan peraturan perundang-undangan terkini yang relevan (Permendikbud, Permendikdasmen, KMA, SK Dirjen, dll).
- Cantumkan buku referensi utama yang sesuai dengan mata pelajaran {$subject} dan jenjang {$schoolTypeLabel}.
- Gunakan format APA Style.
- Urutkan secara alfabetis berdasarkan nama penulis.
- Minimal 5 referensi.
- Langsung ke konten tanpa kata pengantar.
- Tulis dalam Bahasa Indonesia.";
                break;

            default:
                return ['status' => 'error', 'message' => 'Field ' . $field . ' tidak dikenal.'];
        }

// Tambahkan instruksi format output
        $prompt .= "\n\nBerikan output HANYA berupa teks narasi tanpa markdown wrapper, tanpa JSON, tanpa blok kode. Langsung ke konten.";

        return $this->requestAi($prompt, false);
    }
}
