<?php
header('Content-Type: application/json');
error_reporting(E_ALL);
ini_set('display_errors', 1);

require 'config.php'; // Menggunakan konfigurasi database

$action = $_POST['action'] ?? '';
$table = $_POST['table'] ?? ''; // Digunakan oleh action lama, tetap ada
$xid = $_POST['id'] ?? '';     // Digunakan oleh action lama, tetap ada
$ff = $_POST['ff'] ?? '';      // Digunakan oleh action lama, tetap ada

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    error_log("Connection failed: " . $conn->connect_error);
    // Kirim respons JSON bahkan untuk error koneksi awal
    echo json_encode(['success' => false, 'error' => 'Connection failed: ' . $conn->connect_error]);
    exit();
}

// Fungsi untuk escape nama kolom jika merupakan reserved word SQL
function escapeColumnName($column) {
    // Daftar ini bisa diperluas jika perlu
    $reservedWords = ['group', 'left', 'right', 'top', 'bottom', 'order', 'table', 'select', 'delete', 'insert', 'update', 'default', 'user'];
    if (in_array(strtolower($column), $reservedWords)) {
        return "`" . $column . "`";
    }
    return $column;
}

if ($action === 'get_app_config') {
    $brand = $_POST['brand'] ?? '001';
    $cab = $_POST['cab'] ?? '002';

    $stmt = $conn->prepare("SELECT json_config FROM config WHERE brand = ? AND cab = ?");
    if (!$stmt) {
        echo json_encode(['success' => false, 'error' => 'Prepare failed (get_app_config select): ' . $conn->error]);
        $conn->close();
        exit();
    }
    $stmt->bind_param("ss", $brand, $cab);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($row = $result->fetch_assoc()) {
        echo !empty($row['json_config']) ? $row['json_config'] : '[]';
    } else {
        $defaultConfigFile = 'config_default.json';
        if (file_exists($defaultConfigFile)) {
            $defaultJsonConfig = file_get_contents($defaultConfigFile);
            if ($defaultJsonConfig === false) {
                echo json_encode(['success' => false, 'error' => 'Gagal membaca file konfigurasi default.']);
                $stmt->close();
                $conn->close();
                exit();
            }

            json_decode($defaultJsonConfig); // Cek validitas JSON
            if (json_last_error() !== JSON_ERROR_NONE) {
                echo json_encode(['success' => false, 'error' => 'File konfigurasi default bukan JSON yang valid: ' . json_last_error_msg()]);
                $stmt->close();
                $conn->close();
                exit();
            }

            $insertStmt = $conn->prepare("INSERT INTO config (brand, cab, json_config) VALUES (?, ?, ?)");
            if (!$insertStmt) {
                echo json_encode(['success' => false, 'error' => 'Prepare failed (get_app_config insert): ' . $conn->error]);
                $stmt->close();
                $conn->close();
                exit();
            }
            $insertStmt->bind_param("sss", $brand, $cab, $defaultJsonConfig);
            if ($insertStmt->execute()) {
                echo $defaultJsonConfig;
            } else {
                echo json_encode(['success' => false, 'error' => 'Gagal menyimpan konfigurasi default ke database: ' . $insertStmt->error]);
            }
            $insertStmt->close();
        } else {
            echo json_encode(['success' => false, 'error' => 'Konfigurasi tidak ditemukan dan file default config_default.json tidak ada.']);
        }
    }
    $stmt->close();

} elseif ($action === 'save_app_config') {
    $brand = $_POST['brand'] ?? '001';
    $cab = $_POST['cab'] ?? '002';
    $jsonConfigString = $_POST['json_config_string'] ?? null;

    if ($jsonConfigString === null) {
        echo json_encode(['success' => false, 'error' => 'Data konfigurasi JSON tidak diterima.']);
        $conn->close();
        exit();
    }

    json_decode($jsonConfigString); // Cek validitas JSON
    if (json_last_error() !== JSON_ERROR_NONE) {
        echo json_encode(['success' => false, 'error' => 'Data yang dikirim bukan JSON yang valid: ' . json_last_error_msg()]);
        $conn->close();
        exit();
    }

    $stmt = $conn->prepare("INSERT INTO config (brand, cab, json_config) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE json_config = VALUES(json_config), last_modified = NOW()");
    if (!$stmt) {
        echo json_encode(['success' => false, 'error' => 'Prepare failed (save_app_config): ' . $conn->error]);
        $conn->close();
        exit();
    }
    $stmt->bind_param("sss", $brand, $cab, $jsonConfigString);

    if ($stmt->execute()) {
        echo json_encode(['success' => true, 'message' => 'Konfigurasi berhasil disimpan.']);
    } else {
        echo json_encode(['success' => false, 'error' => 'Gagal menyimpan konfigurasi: ' . $stmt->error]);
    }
    $stmt->close();

} elseif ($action === 'searchflexi') {
    // ... (Fungsi searchflexi tetap sama seperti yang Anda berikan) ...
    $keyword = $_POST['keyword'] ?? '';
    $tabel = $_POST['tabel'] ?? '';
    $field1 = $_POST['field1'] ?? '';
    $field2 = $_POST['field2'] ?? '';
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';

    if (empty($tabel) || empty($field1)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field1 untuk searchflexi tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField1 = escapeColumnName($conn->real_escape_string($field1)); // Selalu escape input
    $sql = "SELECT " . $escapedField1;
    if (!empty($field2)) {
        $escapedField2 = escapeColumnName($conn->real_escape_string($field2));
        $sql .= ", " . $escapedField2;
    }
    $sql .= " FROM `" . $conn->real_escape_string($tabel) . "` WHERE ("; // Tambahkan backtick untuk nama tabel
    $sql .= $escapedField1 . " LIKE '%" . $conn->real_escape_string($keyword) . "%'";
    if (!empty($field2)) {
        $sql .= " OR " . $escapedField2 . " LIKE '%" . $conn->real_escape_string($keyword) . "%'";
    }
    $sql .= ")";

    if (!empty($xbrand) && $tabel !== 'menus' && $tabel !== 'menugroups') { // Contoh kondisi kapan brand/cab filter berlaku
        $sql .= " AND brand = '" . $conn->real_escape_string($xbrand) . "'";
    }
    if (!empty($xcab) && $tabel !== 'menus' && $tabel !== 'menugroups') {
        $sql .= " AND cab = '" . $conn->real_escape_string($xcab) . "'";
    }
    $sql .= " LIMIT 20";

    error_log("searchflexi SQL: " . $sql);
    $result = $conn->query($sql);

    if ($result === false) {
        error_log("Query error in searchflexi: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => $conn->error, 'sql' => $sql]);
    } else {
        $data = array();
        while ($row = $result->fetch_assoc()) {
            $data[] = $row;
        }
        echo json_encode($data);
    }


} elseif ($action === 'tambahjson') {
    // ... (Fungsi tambahjson tetap sama seperti yang Anda berikan) ...
    $jsonData = json_decode($_POST['jsonData'] ?? '{}', true);
    $xbrand = $_POST['xbrand'] ?? '001'; // Default jika tidak ada dari frontend
    $xcab = $_POST['xcab'] ?? '002';   // Default jika tidak ada dari frontend

    if (empty($table) || empty($jsonData)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau jsonData untuk tambahjson tidak lengkap.']);
        $conn->close();
        exit();
    }

    // Tambahkan brand dan cab jika belum ada di jsonData
    if (!isset($jsonData['brand'])) $jsonData['brand'] = $xbrand;
    if (!isset($jsonData['cab'])) $jsonData['cab'] = $xcab;
    
    // Hapus 'id' jika ada, karena diasumsikan auto-increment atau tidak diisi saat tambah
    // Kecuali jika 'id' memang bagian dari primary key yang diisi manual
    // if (isset($jsonData['id']) && ($jsonData['id'] === null || $jsonData['id'] === '')) {
    //    unset($jsonData['id']);
    // }


    $columns = [];
    $values = [];
    $placeholders = [];
    $types = '';
    $params_to_bind = []; // Array untuk bind_param

    foreach ($jsonData as $key => $value) {
        $columns[] = escapeColumnName($key); // Gunakan fungsi escapeColumnName
        $placeholders[] = '?';
        
        // Tentukan tipe data untuk bind_param
        if (is_int($value)) {
            $types .= 'i';
        } elseif (is_float($value) || is_double($value)) {
            $types .= 'd';
        } else {
            $types .= 's';
        }
        $params_to_bind[] = $value; // Kumpulkan nilai untuk bind_param
    }

    if (empty($columns)) {
        echo json_encode(['success' => false, 'error' => 'Tidak ada data kolom untuk ditambahkan.']);
        $conn->close();
        exit();
    }

    $sql = "INSERT INTO `" . $conn->real_escape_string($table) . "` (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";
    error_log("tambahjson SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in tambahjson: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); // Gunakan spread operator untuk $params_to_bind
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'id' => $conn->insert_id]);
        } else {
            error_log("Execute error in tambahjson: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} elseif ($action === 'rubahjson') {
    // ... (Fungsi rubahjson tetap sama seperti yang Anda berikan) ...
    $jsonData = json_decode($_POST['jsonData'] ?? '{}', true);
    // ID ('xid') dan filter field ('ff') diambil dari global $xid dan $ff di awal file
    $xbrand_filter = $_POST['xbrand'] ?? ''; 
    $xcab_filter = $_POST['xcab'] ?? '';   

    if (empty($table) || empty($jsonData) || (empty($xid) && empty($ff))) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, jsonData, atau ID/filter field untuk rubahjson tidak lengkap.']);
        $conn->close();
        exit();
    }

    // Hapus 'id' dari jsonData karena ID digunakan di klausa WHERE, bukan untuk di SET
    $id_val_for_where = null;
    if (isset($jsonData['id'])) {
        if (empty($xid) && empty($ff)){ // Jika xid & ff kosong, maka ID dari jsonData dipakai untuk WHERE jika ada
            $id_val_for_where = $jsonData['id'];
        }
        unset($jsonData['id']);
    }


    $updateData = [];
    $types = '';
    $params_to_bind = []; // Array untuk bind_param

    foreach ($jsonData as $column => $value) {
        $escapedColumn = escapeColumnName($column); // Gunakan fungsi escapeColumnName
        $updateData[] = "$escapedColumn = ?";
        
        if (is_int($value)) {
            $types .= 'i';
        } elseif (is_float($value) || is_double($value)) {
            $types .= 'd';
        } else {
            $types .= 's';
        }
        $params_to_bind[] = $value; // Kumpulkan nilai untuk bind_param
    }

    if (empty($updateData)) {
        echo json_encode(['success' => false, 'error' => 'Tidak ada data kolom untuk diupdate.']);
        $conn->close();
        exit();
    }

    $setClause = implode(', ', $updateData);
    $sql = "UPDATE `" . $conn->real_escape_string($table) . "` SET $setClause WHERE ";

    // Logika penentuan kondisi WHERE
    if (!empty($ff) && !empty($xid)) { // Jika ff (filter field) dan xid (filter value) ada
        $sql .= escapeColumnName($ff) . " = ?";
        $types .= 's'; // Asumsi filter value adalah string, sesuaikan jika perlu
        $params_to_bind[] = $xid;
    } elseif (!empty($xid)) { // Jika hanya xid (ID utama) yang ada
        $sql .= "id = ?"; 
        $types .= 'i'; // Asumsi ID utama adalah integer
        $params_to_bind[] = (int)$xid;
    } elseif ($id_val_for_where !== null) { // Jika ID dari jsonData yang dipakai (fallback)
         $sql .= "id = ?"; 
        $types .= (is_int($id_val_for_where) ? 'i' : 's');
        $params_to_bind[] = $id_val_for_where;
    }
    else {
        echo json_encode(['success' => false, 'error' => 'Tidak ada ID atau filter field yang valid untuk rubahjson.']);
        $conn->close();
        exit();
    }
    
    // Tambahan filter brand dan cab jika ada
    if (!empty($xbrand_filter) && $table !== 'menus' && $table !== 'menugroups') {
        $sql .= " AND brand = ?";
        $types .= 's';
        $params_to_bind[] = $xbrand_filter;
    }
    if (!empty($xcab_filter) && $table !== 'menus' && $table !== 'menugroups') {
        $sql .= " AND cab = ?";
        $types .= 's';
        $params_to_bind[] = $xcab_filter;
    }


    error_log("rubahJson SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in rubahJson: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); // Gunakan spread operator
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'message' => 'Record updated successfully']);
        } else {
            error_log("Execute error in rubahJson: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} elseif ($action === 'hapus') {
    // ... (Fungsi hapus tetap sama seperti yang Anda berikan) ...
    $idField = $_POST['idField'] ?? 'id';
    $idValue = $_POST['idValue'] ?? '';
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false'; // 'false' sebagai string default

    if (empty($table) || empty($idField) || $idValue === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, idField, atau idValue untuk hapus tidak lengkap.']);
        $conn->close();
        exit();
    }

    $conditions = [];
    $types = '';
    $params_to_bind = [];

    $conditions[] = escapeColumnName($idField) . " = ?"; // Gunakan fungsi escapeColumnName
    // Tipe data untuk idValue bisa beragam, asumsikan string untuk keamanan umum dengan prepared statement
    $types .= 's'; 
    $params_to_bind[] = $idValue;

    if ($noFilter !== 'true' && $xcab !== 'yyy') { // Tambahan kondisi yyy dari file Anda
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }
    

    $sql = "DELETE FROM `" . $conn->real_escape_string($table) . "`"; // Tambahkan backtick
    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions);
    } else {
        // Ini seharusnya tidak terjadi jika idField dan idValue wajib
        echo json_encode(['success' => false, 'error' => 'Tidak ada kondisi untuk operasi hapus. Operasi dibatalkan demi keamanan.']);
        $conn->close();
        exit();
    }

    error_log("hapus SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in hapus: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); // Gunakan spread operator
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'message' => 'Record deleted successfully']);
        } else {
            error_log("Execute error in hapus: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} 














elseif ($action === 'ambil') {
    // ... (Fungsi ambil tetap sama seperti yang Anda berikan) ...
    $field = $_POST['field'] ?? 'id';
    $valuePost = $_POST['value'] ?? []; // Harapannya ini array
    $value1 = $valuePost[0] ?? ''; 
    $value2 = $valuePost[1] ?? $value1; // Jika value[1] tidak ada, gunakan value[0]
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false';


    if (empty($table)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel untuk ambil tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); // Gunakan fungsi escapeColumnName
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "` "; // Tambahkan backtick
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') { // Kondisi dari file Anda
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }
    

    if ($value1 !== '') { // Hanya tambah kondisi jika value1 ada
        if ($value1 === $value2 || $value2 === '') { // Jika range tidak ada atau value2 kosong
            $conditions[] = $escapedField . " = ?";
            $types .= 's'; // Asumsi tipe string, sesuaikan jika perlu
            $params_to_bind[] = $value1;
        } else { // Jika ada range
            $conditions[] = $escapedField . " >= ?";
            $conditions[] = $escapedField . " <= ?";
            $types .= 'ss'; // Asumsi tipe string
            $params_to_bind[] = $value1;
            $params_to_bind[] = $value2;
        }
    }


    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions);
    }

    error_log("ambil SQL: " . $sql);
    $stmt = $conn->prepare($sql);

    if (!$stmt) {
        error_log("Query error in ambil (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        if (!empty($params_to_bind)) { // Hanya bind jika ada parameter
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambil (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows);
        }
        $stmt->close();
    }

} elseif ($action === 'ambil_select') {
    // ... (Fungsi ambil_select tetap sama seperti yang Anda berikan) ...
    $sql_query = $_POST['sql'] ?? '';

    if (empty($sql_query)) {
        echo json_encode(['success' => false, 'error' => 'Query SQL untuk ambil_select tidak ada.']);
        $conn->close();
        exit();
    }

    // Validasi sederhana: query harus dimulai dengan SELECT (case-insensitive)
    if (stripos(trim($sql_query), 'SELECT') !== 0) {
        echo json_encode(['success' => false, 'error' => 'Hanya query SELECT yang diizinkan untuk ambil_select.']);
        $conn->close();
        exit();
    }
    // PERHATIAN: Meskipun ada validasi ini, mengizinkan query SQL mentah dari klien
    // tetap memiliki risiko keamanan. Pastikan input $sql_query sudah sangat terkontrol
    // atau idealnya, gunakan parameterized queries jika memungkinkan.

    error_log("ambil_select SQL: " . $sql_query);
    $result = $conn->query($sql_query);

    if ($result) {
        $rows = array();
        while ($row = $result->fetch_assoc()) {
            $rows[] = $row;
        }
        echo json_encode($rows);
    } else {
        error_log("MySQL query error in ambil_select: " . $conn->error . " SQL: " . $sql_query);
        echo json_encode(["success" => false, "error" => "Database query error: " . $conn->error, "sql" => $sql_query]);
    }

} elseif ($action === 'ambilakhir') {
    // ... (Fungsi ambilakhir tetap sama seperti yang Anda berikan) ...
    $field = $_POST['field'] ?? 'id';
    $valuePost = $_POST['value'] ?? []; 
    $value1 = $valuePost[0] ?? ''; 
    // $value2 tidak digunakan di ambilakhir versi Anda, jadi kita abaikan.
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false';

    if (empty($table) || empty($field)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field untuk ambilakhir tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); // Gunakan fungsi escapeColumnName
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; // Tambahkan backtick
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') { 
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }

    if (!empty($value1)) { // Jika value1 (filter opsional) diberikan
        $conditions[] = $escapedField . " = ?"; // Asumsi filter adalah equality
        $types .= 's'; // Asumsi tipe string
        $params_to_bind[] = $value1;
    }

    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions);
    }
    // Field untuk ORDER BY adalah field yang sama dengan filter jika value1 ada, atau field utama jika tidak.
    // Namun, di fungsi Anda, $field adalah argumen utama untuk ORDER BY.
    $sql .= " ORDER BY " . $escapedField . " DESC, id DESC LIMIT 1"; 


    error_log("ambilakhir SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in ambilakhir (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        if (!empty($params_to_bind)) { // Hanya bind jika ada parameter
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambilakhir (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows);
        }
        $stmt->close();
    }

} elseif ($action === 'ambilawal') {
    // ... (Fungsi ambilawal tetap sama seperti yang Anda berikan) ...
    $field = $_POST['field'] ?? 'id';
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false';

    if (empty($table) || empty($field)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field untuk ambilawal tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); // Gunakan fungsi escapeColumnName
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; // Tambahkan backtick
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
         if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }

    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions);
    }
    $sql .= " ORDER BY " . $escapedField . " ASC, id ASC LIMIT 1";


    error_log("ambilawal SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in ambilawal (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
         if (!empty($params_to_bind)) { // Hanya bind jika ada parameter
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambilawal (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows);
        }
        $stmt->close();
    }

} elseif ($action === 'berikut') {
    // ... (Fungsi berikut tetap sama seperti yang Anda berikan) ...
    $field = $_POST['field'] ?? 'id';
    $cari = $_POST['cari'] ?? ''; // Nilai saat ini dari field yang diurutkan
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false';

    if (empty($table) || empty($field) || $cari === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, field, atau cari untuk berikut tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); // Gunakan fungsi escapeColumnName
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; // Tambahkan backtick
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    // Kondisi utama untuk 'berikut'
    $conditions[] = $escapedField . " > ?";
    $types .= 's'; // Asumsi tipe data $cari adalah string, sesuaikan jika $field adalah numerik
    $params_to_bind[] = $cari;

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }
    

    $sql .= " WHERE " . implode(' AND ', $conditions);
    $sql .= " ORDER BY " . $escapedField . " ASC, id ASC LIMIT 1"; // Urut ASC untuk record berikutnya


    error_log("berikut SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in berikut (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); // Gunakan spread operator
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in berikut (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows);
        }
        $stmt->close();
    }

} elseif ($action === 'sebelum') {
    // ... (Fungsi sebelum tetap sama seperti yang Anda berikan) ...
    $field = $_POST['field'] ?? 'id';
    $cari = $_POST['cari'] ?? ''; // Nilai saat ini dari field yang diurutkan
    $xbrand = $_POST['xbrand'] ?? '';
    $xcab = $_POST['xcab'] ?? '';
    $noFilter = $_POST['noFilter'] ?? 'false';

    if (empty($table) || empty($field) || $cari === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, field, atau cari untuk sebelum tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); // Gunakan fungsi escapeColumnName
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; // Tambahkan backtick
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    // Kondisi utama untuk 'sebelum'
    $conditions[] = $escapedField . " < ?";
    $types .= 's'; // Asumsi tipe data $cari adalah string
    $params_to_bind[] = $cari;

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
         if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?";
            $types .= 's';
            $params_to_bind[] = $xbrand;
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?";
            $types .= 's';
            $params_to_bind[] = $xcab;
        }
    }

    $sql .= " WHERE " . implode(' AND ', $conditions);
    $sql .= " ORDER BY " . $escapedField . " DESC, id DESC LIMIT 1"; // Urut DESC untuk record sebelumnya

    error_log("sebelum SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in sebelum (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); // Gunakan spread operator
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in sebelum (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows);
        }
        $stmt->close();
    }

} elseif ($action === 'upload') {
    // ... (Fungsi upload tetap sama seperti yang Anda berikan) ...
    if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
        echo json_encode(['success' => false, 'message' => 'No file uploaded or upload error.']);
        $conn->close();
        exit();
    }

    $file = $_FILES['file'];
    $tabel_upload = $_POST['tabel'] ?? '';
    $idField_upload = $_POST['idField'] ?? 'id';
    $idValue_upload = $_POST['idValue'] ?? '';
    $field_upload = $_POST['field'] ?? ''; // Nama kolom di DB untuk menyimpan URL file
    $xbrand_upload = $_POST['xbrand'] ?? ''; // Mungkin tidak selalu dipakai, tergantung kebutuhan
    $xcab_upload = $_POST['xcab'] ?? '';   // Mungkin tidak selalu dipakai

    if (empty($tabel_upload) || empty($idValue_upload) || empty($field_upload)) {
        echo json_encode(['success' => false, 'message' => 'Parameter tabel, idValue, atau field untuk upload tidak lengkap.']);
        $conn->close();
        exit();
    }

    $uploadDir = 'Uploads/'; // Pastikan direktori ini ada dan writable oleh server web
    if (!is_dir($uploadDir)) {
        if (!mkdir($uploadDir, 0775, true)) { // 0775 lebih aman dari 0777
            echo json_encode(['success' => false, 'message' => 'Gagal membuat direktori upload.']);
            $conn->close();
            exit();
        }
    }
    if (!is_writable($uploadDir)) {
        echo json_encode(['success' => false, 'message' => 'Direktori upload tidak writable.']);
        $conn->close();
        exit();
    }

    // Buat nama file unik untuk menghindari penimpaan
    $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    $safeFileName = preg_replace('/[^A-Za-z0-9_\-]/', '_', pathinfo($file['name'], PATHINFO_FILENAME)); // Sanitasi nama file
    $fileName = uniqid($safeFileName . '_', true) . '.' . $fileExtension; // Tambahkan uniqid berbasis waktu
    $targetFilePath = $uploadDir . $fileName;
    $fileUrl = $targetFilePath; // URL bisa relatif atau absolut tergantung setup server Anda

    if (move_uploaded_file($file['tmp_name'], $targetFilePath)) {
        // File berhasil diunggah, sekarang update database
        $escapedFieldUpload = escapeColumnName($field_upload);
        $escapedIdFieldUpload = escapeColumnName($idField_upload);

        $sql_upload = "UPDATE `" . $conn->real_escape_string($tabel_upload) . "` SET " . $escapedFieldUpload . " = ? WHERE " . $escapedIdFieldUpload . " = ?";
        $types_upload = 'ss'; // Untuk URL file (string) dan ID value (string/int)
        $params_to_bind_upload = [$fileUrl, $idValue_upload];

        // Tambahan kondisi brand dan cab jika diperlukan dan jika idField bukan 'id' (karena 'id' biasanya global unik)
        // Perhatikan: logika ini mungkin perlu disesuaikan dengan skema database Anda.
        // Jika ID record sudah unik global, filter brand/cab mungkin tidak perlu saat update path file.
        // Namun, jika ID unik per brand/cab, maka filter ini penting.
        $is_generic_id_field = (strtolower($idField_upload) === 'id');

        if (!$is_generic_id_field && !empty($xbrand_upload)) {
            $sql_upload .= " AND brand = ?";
            $types_upload .= 's';
            $params_to_bind_upload[] = $xbrand_upload;
        }
        if (!$is_generic_id_field && !empty($xcab_upload)) {
            $sql_upload .= " AND cab = ?";
            $types_upload .= 's';
            $params_to_bind_upload[] = $xcab_upload;
        }


        error_log("uploadFile SQL: " . $sql_upload);
        $stmt_upload = $conn->prepare($sql_upload);
        if (!$stmt_upload) {
            error_log("Query error in uploadFile (prepare): " . $conn->error . " SQL: " . $sql_upload);
            // Hapus file yang sudah terlanjur diupload jika query DB gagal
            unlink($targetFilePath); 
            echo json_encode(['success' => false, 'message' => 'Gagal update database (prepare): ' . $conn->error]);
        } else {
            $stmt_upload->bind_param($types_upload, ...$params_to_bind_upload);
            if ($stmt_upload->execute()) {
                echo json_encode(['success' => true, 'message' => 'File uploaded and database updated successfully.', 'url' => $fileUrl]);
            } else {
                error_log("Query error in uploadFile (execute): " . $stmt_upload->error . " SQL: " . $sql_upload);
                // Hapus file yang sudah terlanjur diupload jika query DB gagal
                unlink($targetFilePath); 
                echo json_encode(['success' => false, 'message' => 'Gagal update database (execute): ' . $stmt_upload->error]);
            }
            $stmt_upload->close();
        }
    } else {
        error_log("Failed to move uploaded file: " . ($file['tmp_name'] ?? 'N/A') . " to " . $targetFilePath . " | Error code: " . ($file['error'] ?? 'N/A'));
        echo json_encode(['success' => false, 'message' => 'Gagal memindahkan file yang diunggah. Error code: ' . ($file['error'] ?? 'Unknown error')]);
    }

// BARU: Action untuk generate nomor seri
} elseif ($action === 'get_next_serial_number') {
    $table_name = $_POST['table_name'] ?? '';
    $field_name = $_POST['field_name'] ?? ''; // Nama kolom tempat noseri disimpan
    $prefix = $_POST['prefix'] ?? '';         // Prefix yang sudah dibentuk oleh frontend (misal, INV/001/24)
    $numeric_length = isset($_POST['numeric_length']) ? (int)$_POST['numeric_length'] : 0;
    // Anda bisa tambahkan $xbrand dan $xcab jika nomor seri unik per brand/cab
    // dan prefix yang dikirim dari frontend belum mencakup informasi ini secara unik.
    // $xbrand_noseri = $_POST['xbrand'] ?? '';
    // $xcab_noseri = $_POST['xcab'] ?? '';

    if (empty($table_name) || empty($field_name) || $numeric_length <= 0) {
        // Prefix boleh kosong jika format hanya angka (misal: 99999)
        echo json_encode(['success' => false, 'error' => 'Parameter tidak lengkap untuk generate nomor seri (nama tabel, nama field, atau panjang numerik).']);
        $conn->close();
        exit();
    }

    $new_serial_number_generated = '';
    $conn->autocommit(FALSE); // Penting untuk LOCK TABLES
    $lock_status = $conn->query("LOCK TABLES `" . $conn->real_escape_string($table_name) . "` WRITE");

    if ($lock_status) {
        try {
            $escaped_table_name = "`" . $conn->real_escape_string($table_name) . "`";
            $escaped_field_name = escapeColumnName($conn->real_escape_string($field_name)); // Gunakan escapeColumnName

            // Query untuk mendapatkan MAX serial dengan prefix yang sama
            // Tambahkan filter brand/cab di sini jika noseri harus unik per brand/cab dan prefix belum unik
            $sql_max = "SELECT MAX($escaped_field_name) AS last_serial 
                        FROM $escaped_table_name 
                        WHERE $escaped_field_name LIKE ?";
            
            $stmt_max = $conn->prepare($sql_max);
            if (!$stmt_max) {
                throw new Exception("Prepare statement gagal (MAX serial): " . $conn->error . " SQL: " . $sql_max);
            }

            $like_prefix_param = $prefix . '%';
            $stmt_max->bind_param("s", $like_prefix_param);
            
            if (!$stmt_max->execute()) {
                 throw new Exception("Eksekusi statement gagal (MAX serial): " . $stmt_max->error);
            }
            $result_max = $stmt_max->get_result();
            
            $next_num = 1; // Default jika tidak ada noseri dengan prefix ini
            if ($result_max && $row_max = $result_max->fetch_assoc()) {
                if ($row_max['last_serial'] !== null) {
                    $last_serial = $row_max['last_serial'];
                    // Pastikan prefix dari last_serial sama dengan prefix yang dicari
                    if (strpos($last_serial, $prefix) === 0) {
                        $numeric_part_str = substr($last_serial, strlen($prefix));
                        if (is_numeric($numeric_part_str) && $numeric_part_str !== "") { // Pastikan numerik dan tidak kosong
                            $next_num = (int)$numeric_part_str + 1;
                        } elseif (empty($numeric_part_str) && strlen($last_serial) == strlen($prefix)) {
                             // Kasus jika last_serial adalah prefix itu sendiri (misal format hanya 'PRFX' dan sudah ada 'PRFX')
                             // Seharusnya tidak terjadi jika ada bagian numerik, tapi sebagai fallback.
                             $next_num = 1; // Atau logika lain jika prefix sendiri sudah dianggap sebagai "0"
                        }
                        // Jika numeric_part_str tidak valid, $next_num tetap 1
                    }
                }
            }
            $stmt_max->close();

            $padded_next_num = str_pad((string)$next_num, $numeric_length, '0', STR_PAD_LEFT);
            $new_serial_number_generated = $prefix . $padded_next_num;
            
            // Di sini tidak ada INSERT ke tabel utama. Fungsi ini hanya generate nomor.
            
            $conn->commit(); // Commit jika ada operasi lain dalam transaksi (tidak ada di sini)
            echo json_encode(['success' => true, 'new_serial_number' => $new_serial_number_generated]);

        } catch (Exception $e) {
            $conn->rollback(); // Rollback jika terjadi error dalam try
            error_log("Error dalam action get_next_serial_number: " . $e->getMessage());
            echo json_encode(['success' => false, 'error' => 'Server error saat generate nomor seri: ' . $e->getMessage()]);
        } finally {
            $conn->query("UNLOCK TABLES"); // Selalu UNLOCK TABLES
            $conn->autocommit(TRUE);    // Kembalikan ke mode autocommit
        }
    } else {
        error_log("Gagal LOCK TABLES untuk tabel: " . $table_name . " Error: " . $conn->error);
        echo json_encode(['success' => false, 'error' => 'Gagal mengunci tabel untuk generate nomor seri.']);
    }

} else {
    if (!empty($action)) {
        echo json_encode(['success' => false, 'error' => 'Invalid action specified: ' . htmlspecialchars($action)]);
    } else {
         echo json_encode(['success' => false, 'error' => 'No action specified.']);
    }
}

$conn->close();
?>