<?php
// =========================================================================
// api/bot_api.php - ENDPOINT API SIKA AI (XIEQA LLM ENGINE)
// Fitur: 16-Slot Auto-Router, Hybrid Fallback, Cache, SaaS Billing, & ANTI-DDOS
// Developer: Sikawarsito, S.Kom
// =========================================================================

error_reporting(0);
ini_set('display_errors', 0);
set_time_limit(0);
ignore_user_abort(true);

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Content-Type: application/json; charset=UTF-8");

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(["status" => "error", "message" => "Method Not Allowed. Use POST."]);
    exit;
}

include '../../config/database.php';
include 'router.php'; 

// =========================================================================
// 1. OTORISASI BEARER TOKEN DARI USER
// =========================================================================
$auth_header = '';
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
    $auth_header = trim($_SERVER['HTTP_AUTHORIZATION']);
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
    $auth_header = trim($_SERVER['REDIRECT_HTTP_AUTHORIZATION']);
} elseif (function_exists('apache_request_headers')) {
    $requestHeaders = apache_request_headers();
    if (isset($requestHeaders['Authorization'])) {
        $auth_header = trim($requestHeaders['Authorization']);
    }
}

if (empty($auth_header) || !preg_match('/Bearer\s(\S+)/', $auth_header, $matches)) {
    http_response_code(401);
    echo json_encode(["status" => "error", "message" => "Unauthorized. Missing Token."]);
    exit;
}

$api_key_input = mysqli_real_escape_string($conn, $matches[1]);
$cek_key = mysqli_query($conn, "SELECT * FROM api_key_bot WHERE api_key = '$api_key_input' AND status = 'active'");

if (!$cek_key || mysqli_num_rows($cek_key) === 0) {
    http_response_code(401);
    echo json_encode(["status" => "error", "message" => "Unauthorized. Invalid or Revoked API Key."]);
    exit;
}

$key_data = mysqli_fetch_assoc($cek_key);
$user_model = isset($key_data['model']) ? $key_data['model'] : 'xieqaLLM-HIBRYT';
$user_versi = isset($key_data['versi']) ? $key_data['versi'] : 'all';
$api_id = $key_data['id'];
$user_id_owner = $key_data['user_id'];

// BYPASS CEK UNTUK INTERNAL SYSTEM (Chatbot, Analisa, RPP, dsb)
$is_internal_call = false;
if (isset($_SERVER['HTTP_X_XIEQA_INTERNAL']) && $_SERVER['HTTP_X_XIEQA_INTERNAL'] === 'SikaWarsito2026BypassLimit') {
    $is_internal_call = true;
} else if (function_exists('apache_request_headers')) {
    $reqHeaders = apache_request_headers();
    if (isset($reqHeaders['X-Xieqa-Internal']) && $reqHeaders['X-Xieqa-Internal'] === 'SikaWarsito2026BypassLimit') {
        $is_internal_call = true;
    }
}

// PAKSA MODE PRO UNTUK AKSES INTERNAL (AUTOKODING) AGAR TIDAK TERPOTONG
if ($is_internal_call) {
    $user_versi = 'pro';
}

// =========================================================================
// 2. ANTI-DDOS & ENDPOINT RATE LIMITING (FAST MEMORY CACHE)
// =========================================================================
if (!$is_internal_call) {
    $rate_limit_window = 10; // Jendela waktu: 10 detik
    $max_requests = 3;       // Maksimal: 3 kali request per 10 detik

    // Identifikasi unik gabungan API Key & IP Address penyerang
    $client_ip = $_SERVER['REMOTE_ADDR'] ?? 'UNKNOWN_IP';
    $client_id_hash = md5($api_key_input . "_" . $client_ip);

    // Buat folder khusus secara otomatis jika belum ada
    $rate_dir = __DIR__ . '/rate_limits'; 
    if (!is_dir($rate_dir)) {
        @mkdir($rate_dir, 0777, true);
    }

    $rate_file = $rate_dir . '/throttle_' . $client_id_hash . '.json';

    if (file_exists($rate_file)) {
        $rate_data = json_decode(file_get_contents($rate_file), true);
        $time_passed = time() - $rate_data['start_time'];
        
        if ($time_passed < $rate_limit_window) {
            if ($rate_data['hits'] >= $max_requests) {
                $wait_time = $rate_limit_window - $time_passed;
                http_response_code(429);
                echo json_encode([
                    "status" => "error", 
                    "message" => "Too Many Requests (Anti-DDoS Active). Harap pelan-pelan! Tunggu $wait_time detik lagi sebelum mengirim pesan."
                ], JSON_PRETTY_PRINT);
                exit; // Hentikan sistem di sini agar server/database tidak jebol
            } else {
                $rate_data['hits'] += 1;
                file_put_contents($rate_file, json_encode($rate_data));
            }
        } else {
            file_put_contents($rate_file, json_encode(['start_time' => time(), 'hits' => 1]));
        }
    } else {
        file_put_contents($rate_file, json_encode(['start_time' => time(), 'hits' => 1]));
    }
}

// =========================================================================
// 3. SISTEM SAAS BILLING (AUTO-RESET, EXPIRY, & LIMIT CHECK)
// =========================================================================
@mysqli_query($conn, "UPDATE api_key_bot SET usage_count = usage_count + 1 WHERE id = $api_id");

$q_owner = mysqli_query($conn, "SELECT llm_status, status_akun, llm_kredit_terpakai, llm_last_reset, llm_pro_expiry, role FROM users WHERE id = $user_id_owner");
$owner_data = mysqli_fetch_assoc($q_owner);
$status_akun = $owner_data['status_akun'];

if ($owner_data['llm_status'] == 'blocked') {
    http_response_code(403);
    echo json_encode(["status" => "error", "message" => "Akses Ditolak: Akun Anda telah dibekukan oleh Administrator."]);
    exit;
}

$current_month = date('Y-m');
$last_reset_month = date('Y-m', strtotime($owner_data['llm_last_reset'] ?? '2000-01-01'));
if ($current_month !== $last_reset_month) {
    mysqli_query($conn, "UPDATE users SET llm_kredit_terpakai = 0, llm_last_reset = CURRENT_DATE WHERE id = $user_id_owner");
    $owner_data['llm_kredit_terpakai'] = 0; 
}

if ($status_akun == 'pro' && !empty($owner_data['llm_pro_expiry'])) {
    if (strtotime($owner_data['llm_pro_expiry']) < time()) {
        mysqli_query($conn, "UPDATE users SET status_akun = 'free' WHERE id = $user_id_owner");
        $status_akun = 'free'; 
    }
}

if ($status_akun == 'free' && $owner_data['llm_kredit_terpakai'] >= 100000 && $owner_data['role'] != 'admin' && !$is_internal_call) {
    http_response_code(429);
    echo json_encode([
        "status" => "error", 
        "message" => "Limit Tercapai: Kuota token bulanan akun FREE Anda (100.000) telah habis."
    ]);
    exit;
}

// =========================================================================
// 4. TERIMA DATA JSON (PERTANYAAN USER)
// =========================================================================
$json_input = file_get_contents('php://input');
$data = json_decode($json_input, true);

$pesan_user = $data['pesan'] ?? '';
$file_part = $data['file_part'] ?? null;

if (empty($pesan_user)) {
    http_response_code(400);
    echo json_encode(["status" => "error", "message" => "Bad Request. Parameter 'pesan' is required."]);
    exit;
}

// =========================================================================
// 5. XIEQA CACHE ENGINE (SEMANTIC + EXACT HASH)
// =========================================================================
// Tier 1: Exact Hash Match (fuzzy clean + md5)
// Tier 2: Keyword Similarity Match (Jaccard coefficient >= 75%)
$clean_prompt = preg_replace('/[^a-z0-9\s]/', '', strtolower(trim($pesan_user)));
$clean_prompt = trim(preg_replace('/\s+/', ' ', $clean_prompt));
$prompt_hash = md5($clean_prompt . "_" . $user_versi);

$is_cached = false;
$ai_reply = "";

if (empty($file_part)) {
    // --- TIER 1: Exact Hash Match (tercepat) ---
    $q_cache = @mysqli_query($conn, "SELECT * FROM cache_llm WHERE prompt_hash = '$prompt_hash'");
    
    if ($q_cache && mysqli_num_rows($q_cache) > 0) {
        $cache_data = mysqli_fetch_assoc($q_cache);
        $ai_reply = $cache_data['response_text'];
        $is_cached = true;
    }
    
    // --- TIER 2: Semantic Similarity (Jaccard Keyword) ---
    // Hanya untuk prompt pendek (FAQ-style), bukan prompt panjang/autokoding
    if (!$is_cached && strlen($clean_prompt) < 200) {
        $user_words = array_unique(explode(' ', $clean_prompt));
        // Hapus stopwords bahasa Indonesia agar matching lebih akurat
        $stopwords = ['apa','itu','yang','dan','di','ke','dari','untuk','dengan','adalah','ini','itu','atau','pada','juga','saya','kamu','bisa','tidak','ya','bagaimana','mengapa','kenapa','siapa','mana','kapan','tolong','coba','dong','deh','sih','lah','kan'];
        $user_words = array_diff($user_words, $stopwords);
        $user_words = array_values(array_filter($user_words, function($w) { return strlen($w) >= 2; }));
        
        if (count($user_words) >= 2) {
            // Ambil 20 cache terbaru yang sering dipakai untuk dibandingkan
            $q_sim = @mysqli_query($conn, "SELECT id, prompt_text, response_text FROM cache_llm ORDER BY hit_count DESC, last_used DESC LIMIT 20");
            $best_score = 0;
            $best_cache = null;
            
            while ($q_sim && $row = mysqli_fetch_assoc($q_sim)) {
                $cached_clean = preg_replace('/[^a-z0-9\s]/', '', strtolower(trim($row['prompt_text'])));
                $cached_clean = trim(preg_replace('/\s+/', ' ', $cached_clean));
                $cached_words = array_unique(explode(' ', $cached_clean));
                $cached_words = array_diff($cached_words, $stopwords);
                $cached_words = array_values(array_filter($cached_words, function($w) { return strlen($w) >= 2; }));
                
                if (count($cached_words) < 2) continue;
                
                // Jaccard Similarity: intersection / union
                $intersection = count(array_intersect($user_words, $cached_words));
                $union = count(array_unique(array_merge($user_words, $cached_words)));
                $score = ($union > 0) ? ($intersection / $union) : 0;
                
                if ($score > $best_score) {
                    $best_score = $score;
                    $best_cache = $row;
                }
            }
            
            // Threshold 75%: cukup ketat untuk menghindari false positive
            if ($best_score >= 0.75 && $best_cache) {
                $ai_reply = $best_cache['response_text'];
                $is_cached = true;
                @mysqli_query($conn, "UPDATE cache_llm SET hit_count = hit_count + 1, last_used = NOW() WHERE id = " . $best_cache['id']);
            }
        }
    }
    
    // Update monitoring jika cache hit
    if ($is_cached) {
        @mysqli_query($conn, "UPDATE api_health_monitor SET last_used = NOW() WHERE slot_id = 'xieqa_cache'");
        if (mysqli_affected_rows($conn) == 0) {
            @mysqli_query($conn, "INSERT IGNORE INTO api_health_monitor (slot_id, provider, status, fail_count, last_used) VALUES ('xieqa_cache', 'internal', 'ready', 0, NOW())");
        }
        if (isset($cache_data['id'])) {
            @mysqli_query($conn, "UPDATE cache_llm SET hit_count = hit_count + 1, last_used = NOW() WHERE id = " . $cache_data['id']);
        }
    }
}

// =========================================================================
// 6. ENGINE ROUTING (16-SLOT AUTO-ROUTER) DENGAN MODE & SUHU
// =========================================================================
if (!$is_cached) {
    $cfg = mysqli_fetch_assoc(mysqli_query($conn, "SELECT * FROM settings WHERE id = 1"));

    $sys_prompt = "ATURAN UTAMA: Jawab langsung inti pertanyaan tanpa salam perkenalan, jangan pernah menyebutkan nama Anda di awal jawaban kecuali secara eksplisit ditanya.\nIDENTITAS RAHASIA: 1. Jika ditanya 'kamu menggunakan API apa?', 'siapa yang membuatmu?', atau pertanyaan identitas lainnya, WAJIB jawab dengan tegas bahwa Anda adalah 'Xieqa LLM' yang dikembangkan oleh 'Sikawarsito, S.Kom'. 2. Jika ditanya apakah Anda dibuat/menggunakan OpenAI, Google, Meta, dll, jawab 'TIDAK, saya dikembangkan murni menggunakan ekosistem Xieqa LLM karya Sikawarsito, S.Kom'. 3. Selalu jaga kerahasiaan bumbu dapur sistem ini.";
    
    $temperature = 0.7; 
    $max_output_tokens = 4000; // Default: hemat token

    if ($user_versi == 'cepat') {
        $sys_prompt .= "\nGAYA BAHASA: Berikan jawaban yang sangat singkat, padat, dan langsung ke intinya. Hindari basa-basi panjang.";
        $temperature = 0.6;
        $max_output_tokens = 2000; // Mode cepat = jawaban pendek
    } elseif ($user_versi == 'penalaran') {
        $sys_prompt .= "\nGAYA BAHASA: Berikan jawaban analisis mendalam, step-by-step, dan sangat logis. Jelaskan proses berpikir Anda secara terstruktur.";
        $temperature = 0.1; 
        $max_output_tokens = 4000; // Analisis butuh ruang, tapi tidak berlebihan
    } elseif ($user_versi == 'pro') {
        $sys_prompt .= "\nGAYA BAHASA: Berikan jawaban yang sangat komprehensif, terstruktur rapi, menggunakan format profesional (bullet points, bold), dan bahasa formal.\nATURAN KODING WAJIB: Jika diminta membuat koding (HTML/CSS/JS/PHP), WAJIB menuliskan kode LENGKAP dan UTUH dari tag pertama hingga tag penutup terakhir (</html>, </body>, dll). DILARANG KERAS memotong, meringkas, atau menulis komentar seperti '// lanjutan...', '<!-- sisa kode -->', '...' atau sejenisnya. Output harus production-ready dan dapat langsung digunakan tanpa modifikasi.";
        $temperature = 0.4; 
        $max_output_tokens = 16000; // Pro: full koding UI/UX tanpa terpotong (Groq max 32768, Gemini Flash max 8192)
    } else {
        $sys_prompt .= "\nGAYA BAHASA: Berikan jawaban yang natural, ramah, layaknya asisten manusia yang pintar.";
        $temperature = 0.7;
        $max_output_tokens = 4000;
    }

    // --- FUNGSI HELPER: PENEMBAK GEMINI (UNLIMITED SLOTS) ---
    function tembak_gemini_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $file_part = null, $maxTokens = 4000) {
        $max_try = 50; // safety limit agar tidak infinite loop
        for ($try = 1; $try <= $max_try; $try++) { 
            $keyData = getBestKey('gemini', $conn);
            if (!$keyData || empty($keyData['key'])) break; 
            
            $gemini_model = trim($cfg['gemini_model'] ?? 'gemini-flash-latest');
            if (strpos($gemini_model, '1.5') !== false || $gemini_model == '') {
                $gemini_model = 'gemini-flash-latest';
            }
            $url = "https://generativelanguage.googleapis.com/v1beta/models/$gemini_model:generateContent?key={$keyData['key']}";
            
            $parts = [];
            if (!empty($file_part) && is_array($file_part)) {
                if (isset($file_part['inlineData'])) {
                    $parts[] = $file_part;
                } else {
                    foreach ($file_part as $fp) {
                        $parts[] = $fp;
                    }
                }
            }
            $parts[] = ["text" => $sys_prompt . "\n\nUser: " . $pesan_user];

            $payload = [
                "contents" => [ ["parts" => $parts] ],
                "generationConfig" => [ 
                    "temperature" => $temperature,
                    "maxOutputTokens" => $maxTokens 
                ]
            ];
            
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 
            curl_setopt($ch, CURLOPT_TIMEOUT, 600); 
            
            $res = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            if ($http_code == 200) {
                reportTrafficStatus($keyData['slot'], true, 200, $conn);
                $res_data = json_decode($res, true);
                return $res_data['candidates'][0]['content']['parts'][0]['text'] ?? "";
            } else {
                reportTrafficStatus($keyData['slot'], false, $http_code, $conn); 
            }
        }
        return ""; 
    }

    // --- FUNGSI HELPER: PENEMBAK GROQ (UNLIMITED SLOTS) ---
    function tembak_groq_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $maxTokens = 4000) {
        $max_try = 50; // safety limit
        for ($try = 1; $try <= $max_try; $try++) { 
            $keyData = getBestKey('groq', $conn);
            if (!$keyData || empty($keyData['key'])) break;
            
            $url_groq = "https://api.groq.com/openai/v1/chat/completions";
            $groq_model_aktif = trim($cfg['groq_model'] ?? 'llama-3.3-70b-versatile');
            
            $payload_groq = [
                "model" => $groq_model_aktif, 
                "messages" => [ ["role" => "system", "content" => $sys_prompt], ["role" => "user", "content" => $pesan_user] ], 
                "temperature" => $temperature,
                "max_tokens" => $maxTokens
            ];
            
            $ch = curl_init($url_groq);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $keyData['key'], "Content-Type: application/json"]);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload_groq));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
            curl_setopt($ch, CURLOPT_TIMEOUT, 600);
            
            $res = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            if ($http_code == 200) {
                reportTrafficStatus($keyData['slot'], true, 200, $conn);
                $data_groq = json_decode($res, true);
                return $data_groq['choices'][0]['message']['content'] ?? "";
            } else {
                reportTrafficStatus($keyData['slot'], false, $http_code, $conn);
            }
        }
        return ""; 
    }

    // =========================================================
    // ENGINE UTAMA: MUTUAL FULL-FALLBACK (ANTI-LIMIT)
    // =========================================================
    // Deteksi jika ini adalah "mode cepat" dari frontend
    $is_mode_cepat = (stripos($pesan_user, 'FAST RESPONDER') !== false);

    // Prioritaskan Groq jika model user xieqaLLM-GQ ATAU jika mode cepat (ringan) dan tanpa lampiran visual
    if ($user_model == 'xieqaLLM-GQ' || ($is_mode_cepat && empty($file_part))) {
        // 1. Prioritas Groq
        $ai_reply = tembak_groq_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $max_output_tokens);
        
        // JIKA GROQ LIMIT/GAGAL -> FALLBACK KE GEMINI
        if (empty($ai_reply)) {
            $ai_reply = tembak_gemini_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $file_part, $max_output_tokens);
        }
    } else {
        // 2. Prioritas Gemini (Default & Mode Go)
        $ai_reply = tembak_gemini_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $file_part, $max_output_tokens);
        
        // JIKA GEMINI LIMIT/GAGAL -> FALLBACK KE GROQ
        if (empty($ai_reply)) {
            $ai_reply = tembak_groq_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $max_output_tokens);
        }
    }

    if (empty($ai_reply)) {
        $ai_reply = "FAIL_DEBUG_XIEQA";
    }

    // POTONG KREDIT & SIMPAN KE CACHE
    if (!empty($ai_reply) && strpos($ai_reply, 'FAIL_DEBUG_XIEQA') === false) {
        if (!$is_internal_call) {
            $estimasi_token = ceil((strlen($pesan_user) + strlen($ai_reply)) / 4);
            @mysqli_query($conn, "UPDATE users SET llm_kredit_terpakai = llm_kredit_terpakai + $estimasi_token WHERE id = $user_id_owner");
        }
        
        if (empty($file_part)) {
            $safe_prompt = mysqli_real_escape_string($conn, $pesan_user);
            $safe_reply = mysqli_real_escape_string($conn, $ai_reply);
            @mysqli_query($conn, "INSERT INTO cache_llm (prompt_hash, prompt_text, response_text, hit_count, last_used) VALUES ('$prompt_hash', '$safe_prompt', '$safe_reply', 1, NOW())");
        }
    }
}

// =========================================================================
// 7. SMART RETRY — Jangan langsung putus, tunggu slot API pulih
// =========================================================================
if ($ai_reply === "FAIL_DEBUG_XIEQA") {
    $max_retries = 3;
    $retry_delay = 30; // detik per retry
    $retry_ok = false;
    
    for ($rt = 1; $rt <= $max_retries; $rt++) {
        sleep($retry_delay);
        
        // Coba lagi — mungkin ada slot yang sudah pulih dari cooldown
        if ($user_model == 'xieqaLLM-GQ' || ($is_mode_cepat && empty($file_part))) {
            $ai_reply = tembak_groq_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $max_output_tokens);
            if (empty($ai_reply)) {
                $ai_reply = tembak_gemini_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $file_part, $max_output_tokens);
            }
        } else {
            $ai_reply = tembak_gemini_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $file_part, $max_output_tokens);
            if (empty($ai_reply)) {
                $ai_reply = tembak_groq_router($pesan_user, $sys_prompt, $temperature, $conn, $cfg, $max_output_tokens);
            }
        }
        
        if (!empty($ai_reply) && $ai_reply !== "FAIL_DEBUG_XIEQA") {
            $retry_ok = true;
            break;
        }
        $ai_reply = "FAIL_DEBUG_XIEQA"; // reset untuk loop berikutnya
    }
    
    if (!$retry_ok) {
        http_response_code(200);
        echo json_encode([
            "status" => "busy",
            "message" => "Mohon maaf, server AI sedang sangat sibuk melayani banyak pengguna. Silakan coba lagi dalam beberapa menit. \xF0\x9F\x99\x8F"
        ]);
        exit;
    }
}

http_response_code(200);
echo json_encode([
    "status" => "success",
    "developer" => "SikaWarsito",
    "is_cached" => $is_cached,
    "choices" => [
        [
            "message" => [
                "role" => "assistant",
                "content" => trim($ai_reply)
            ]
        ]
    ]
], JSON_PRETTY_PRINT);
?>