<?php
/**
 * Class APK Importer
 *
 * Kelas utama untuk mengimpor data aplikasi Android (APK) ke dalam WordPress sebagai postingan.
 *
 * Fungsi utama:
 * - Mengimpor data dari Google Play dan berbagai situs scraper/MOD (5play, apkmody, liteapks, dll)
 * - Membuat atau memperbarui postingan dengan judul, konten, gambar, meta, taxonomy
 * - Menangani duplikat berdasarkan GP_ID, source_url, judul+versi+ukuran
 * - Mendukung upload gambar ke media library atau simpan URL saja
 * - Mendukung shortlink otomatis (OUO, ShrinkEarn, dll)
 * - Menyimpan link download (bisa upload APK atau simpan link eksternal)
 * - Memaksa status 'draft' jika tidak ada GP_ID / Package Name
 *
 * Dependensi utama:
 * - WordPress core functions (wp_insert_post, wp_update_post, wp_remote_get, dll)
 * - Variabel global $opt_themes (pengaturan theme)
 *
 * Fitur penting:
 * - Pengecekan poster wajib di awal (gagal jika poster tidak valid/tidak bisa diakses)
 * - Duplikat check prioritas: GP_ID → source_url → judul+versi+ukuran
 * - Opsi theme mengontrol: status post, limit screenshot, upload APK, shortlink, dll
 * - Logging error ke debug.log untuk memudahkan troubleshooting
 *
 * Penggunaan utama:
 * $importer = new APK_Importer($data_aplikasi);
 * $result = $importer->import();
 *
 * @package    APK Importer
 * @subpackage Classes
 * @since      1.0.0
 * @author     [exthem.es]
 */
 
class APK_Importer {

    /** @var array */
    private $data;

    /** @var array */
    private $options;

    /** @var int */
    private $post_id = 0;

    /** @var bool */
    private $is_update = false;
	
    /** @var array Koleksi entry download sementara */
    private $download_entries = [];
	
	/**
	 * Constructor class APK_Importer
	 *
	 * Fungsi ini dipanggil otomatis saat membuat instance baru dari class ini.
	 * Tugas utama:
	 * - Menyimpan data aplikasi yang akan diimport ke properti $this->data
	 * - Mengambil semua pengaturan theme (opsi import) dan menyimpannya ke $this->options
	 *
	 * @param array $data Data lengkap aplikasi yang akan diimport 
	 *                    (berisi title_GP, poster_GP, GP_ID, images_GP, dll)
	 */
	public function __construct(array $data)
	{
		// Simpan data input aplikasi ke properti private agar bisa diakses oleh semua method
		$this->data = $data;

		// Ambil semua pengaturan theme (status post, limit screenshot, shortener, dll)
		// Pengaturan ini akan menentukan perilaku import (misal: publish langsung atau draft)
		$this->options = $this->get_theme_options();
	}

	/**
	 * Mengambil semua pengaturan theme yang relevan untuk proses import
	 *
	 * Fungsi ini mengakses variabel global $opt_themes yang biasanya diisi oleh:
	 * - Framework theme (Redux, Codestar, Option Tree, dll)
	 * - Atau langsung dari get_option() di functions.php theme
	 *
	 * @return array Pengaturan theme dalam format array (selalu array meskipun kosong)
	 */
	private function get_theme_options()
	{
		// Akses variabel global yang menyimpan semua opsi theme
		global $opt_themes;

		// Casting ke array untuk memastikan format selalu array (aman jika null atau bukan array)
		return (array) $opt_themes;
	}

	 
	/**
	 * Fungsi utama untuk menjalankan seluruh proses import aplikasi ke WordPress
	 *
	 * Mengembalikan array hasil lengkap untuk AJAX/frontend:
	 * - success: bool
	 * - post_id: int
	 * - edit_link: string (URL edit post di wp-admin)
	 * - message: string (pesan akhir informatif untuk user)
	 * - notices: array (detail pesan untuk log)
	 * - is_duplicate: bool
	 * - missing_gp_id: bool
	 * - missing_featured: bool
	 */
	public function import()
	{
		$result = [
			'success'           => false,
			'post_id'           => 0,
			'edit_link'         => '',
			'message'           => '',
			'notices'           => [],
			'is_duplicate'      => false,
			'missing_gp_id'     => false,
			'missing_featured'  => false,
		];
		 
		// ─── 1. Validasi poster wajib ────────────────────────────────────────
		$poster_url = trim($this->data['poster_GP'] ?? $this->data['poster_sc'] ?? '');
		if (empty($poster_url) || !filter_var($poster_url, FILTER_VALIDATE_URL)) {
			$result['notices'][] = 'Poster image is missing or URL is invalid.';
			$result['missing_featured'] = true;
		} else {
			// Tes ringan apakah gambar bisa diakses
			$test_response = wp_remote_head($poster_url, ['timeout' => 10]);
			if (is_wp_error($test_response) || wp_remote_retrieve_response_code($test_response) !== 200) {
				$result['notices'][] = 'Poster image cannot be reached (HTTP error).';
				$result['missing_featured'] = true;
			}
		}

		// ─── 2. Proses pembuatan/update post ─────────────────────────────────
		$post_result = $this->process_title_and_post();
		if (is_wp_error($post_result)) {
			$result['message'] = $post_result->get_error_message() ?: 'Failed to create/update post.';
			return $result;
		}

		$this->post_id = (int) $post_result;

		// Cek duplikat/update
		$result['is_duplicate'] = $this->is_update;
		if ($this->is_update) {
			$result['notices'][] = 'Post already existed and was successfully updated. (ID: ' . $this->post_id . ').';
		}

		// Cek GP_ID
		$gp_id_input  = trim($this->data['GP_ID'] ?? '');
		$gp_id_stored = get_post_meta($this->post_id, 'wp_GP_ID', true);
		if (empty($gp_id_input) && empty($gp_id_stored)) {
			$result['missing_gp_id'] = true;
			$result['notices'][] = 'No App ID / Package Name → post forced to draft.';
		}

		// ─── 3. Proses gambar, meta, taxonomy ────────────────────────────────
		$this->process_featured_image();
		$this->process_background_image();
		$this->process_screenshots();
		$this->save_all_meta();
		$this->save_taxonomies();

		// ─── 4. Finalisasi hasil sukses ─────────────────────────────────────
		$result['success']   = true;
		$result['post_id']   = $this->post_id;

		// Tambahkan edit link (frontend AJAX)
		$result['edit_link'] = '';
		if ($this->post_id && current_user_can('edit_post', $this->post_id)) {
			$result['edit_link'] = get_edit_post_link($this->post_id, 'url') ?: admin_url("post.php?post={$this->post_id}&action=edit");
		}

		// ─── 5. Bangun pesan akhir & notifikasi admin ────────────────────────
		$messages = [];

		if ($result['missing_featured']) {
			$messages[] = 'featured image missing/failed';
		}
		if ($this->is_update) {
			$messages[] = 'post updated (duplicate)';
		}
		if ($result['missing_gp_id']) {
			$messages[] = 'No App ID → forced draft';
		}

		$base_message = $this->is_update
			? 'Post updated successfully'
			: 'Post imported successfully';

		if (!empty($messages)) {
			$base_message .= ' (with notes: ' . implode(', ', $messages) . ')';
		}

		$result['message'] = $base_message . '. Post ID: ' . $this->post_id . '.';

		if (!$result['success']) {
			$result['message'] = 'Import failed: ' . ($result['message'] ?: 'Unknown error');
		}

		// ─── 6. Notifikasi admin (khusus halaman admin) ──────────────────────
		if (is_admin()) {
			if ($result['missing_featured']) {
				add_settings_error(
					'apk_importer',
					'missing_featured',
					'Warning: Featured image not found or failed to download.',
					'warning'
				);
			}

			if ($this->is_update) {
				add_settings_error(
					'apk_importer',
					'duplicate',
					'Note: Post already exists and was successfully updated. (ID: ' . $this->post_id . ').',
					'info'
				);
			}

			if ($result['missing_gp_id']) {
				add_settings_error(
					'apk_importer',
					'no_gp_id',
					'Warning: Missing App ID / Package Name. Post status changed to draft.',
					'warning'
				);
			}
		}
		
		return $result;
	}


	public function import221122()
	{
		$result = [
			'success'         => false,
			'post_id'         => 0,
			'edit_link'       => '',
			'message'         => '',
			'notices'         => [],
			'is_duplicate'    => false,
			'missing_gp_id'   => false,
			'missing_featured' => false,
		];
		 
		// ─── 1. Validasi poster wajib ────────────────────────────────────────
		$poster_url = trim($this->data['poster_GP'] ?? $this->data['poster_sc'] ?? '');
		if (empty($poster_url) || !filter_var($poster_url, FILTER_VALIDATE_URL)) {
			$result['notices'][] = 'Poster image is missing or URL is invalid.';
			$result['missing_featured'] = true;
		} else {
			// Tes ringan apakah gambar bisa diakses
			$test_response = wp_remote_head($poster_url, ['timeout' => 10]);
			if (is_wp_error($test_response) || wp_remote_retrieve_response_code($test_response) !== 200) {
				$result['notices'][] = 'Poster image cannot be reached (HTTP error).';
				$result['missing_featured'] = true;
			}
		}

		// ─── 2. Proses pembuatan/update post ─────────────────────────────────
		$post_result = $this->process_title_and_post();
		if (is_wp_error($post_result)) {
			$result['message'] = $post_result->get_error_message() ?: 'Failed to create/update post.';
			return $result;
		}

		$this->post_id = (int) $post_result;

		// Cek duplikat/update
		$result['is_duplicate'] = $this->is_update;
		if ($this->is_update) {
			$result['notices'][] = 'Post already existed and was successfully updated. (ID: ' . $this->post_id . ').';
		}

		// Cek GP_ID
		$gp_id_input  = trim($this->data['GP_ID'] ?? '');
		$gp_id_stored = get_post_meta($this->post_id, 'wp_GP_ID', true);
		if (empty($gp_id_input) && empty($gp_id_stored)) {
			$result['missing_gp_id'] = true;
			$result['notices'][] = 'No App ID / Package Name → post forced to draft.';
		}

		// ─── 3. Proses gambar, meta, taxonomy ────────────────────────────────
		$this->process_featured_image();
		$this->process_background_image();
		$this->process_screenshots();
		$this->save_all_meta();
		$this->save_taxonomies();

		// ─── 4. Finalisasi hasil sukses ─────────────────────────────────────
		$result['success']   = true;
		$result['post_id']   = $this->post_id;

		// Tambahkan edit link (wajib untuk frontend!)
		$result['edit_link'] = get_edit_post_link($this->post_id, 'url') ?: '';
		$edit_link = get_edit_post_link($post_id, ''); // false jika user tidak punya akses

		// ─── 5. Bangun pesan akhir & notifikasi admin ────────────────────────
		$messages = [];

		// Kumpulkan catatan untuk pesan user
		if ($result['missing_featured']) {
			$messages[] = 'featured image missing/failed';
		}
		if ($this->is_update) {
			$messages[] = 'post updated (duplicate)';
		}
		if ($result['missing_gp_id']) {
			$messages[] = 'No App ID  → forced draft';
		}

		// Pesan dasar
		$base_message = $this->is_update
			? 'Post updated successfully'
			: 'Post imported successfully';

		// Tambahkan catatan jika ada
		if (!empty($messages)) {
			$base_message .= ' (with notes: ' . implode(', ', $messages) . ')';
		}

		// Final pesan
		$result['message'] = $base_message . '. Post ID: ' . $this->post_id . '.';

		// Override jika gagal (meskipun jarang sampai sini)
		if (!$result['success']) {
			$result['message'] = 'Import failed: ' . ($result['message'] ?: 'Unknown error');
		}

		// ─── 6. Notifikasi admin (khusus halaman admin) ──────────────────────
		if (is_admin()) {
			// Featured image gagal
			if ($result['missing_featured']) {
				add_settings_error(
					'apk_importer',
					'missing_featured',
					'Warning: Featured image not found or failed to download.',
					'warning'
				);
			}

			// Duplikat / update
			if ($this->is_update) {
				add_settings_error(
					'apk_importer',
					'duplicate',
					'Note: Post already exists and was successfully updated. (ID: ' . $this->post_id . ').',
					'info'
				);
			}

			// Tidak ada GP_ID
			if ($result['missing_gp_id']) {
				add_settings_error(
					'apk_importer',
					'no_gp_id',
					'Warning: Missing App ID / Package Name. Post status changed to draft.',
					'warning'
				);
			}
		}

		// Kembalikan hasil lengkap
		return $result;
	}

	/**
	 * Mengambil judul utama aplikasi untuk digunakan sebagai judul postingan WordPress
	 * 
	 * Fungsi ini menentukan judul berdasarkan urutan prioritas sumber data:
	 * 1. title_GP      → Judul resmi dari Google Play (sumber paling diutamakan)
	 * 2. app_name      → Nama aplikasi dari sumber scraper atau situs lain
	 * 3. Fallback      → 'Untitled App' jika tidak ada data judul sama sekali
	 *
	 * Semua judul yang dikembalikan sudah melalui trim() untuk menghilangkan spasi berlebih.
	 *
	 * @return string Judul aplikasi yang sudah dibersihkan dan siap digunakan
	 */
	private function get_primary_title()
	{
		// Prioritas tertinggi: judul resmi dari Google Play Store
		if (!empty($this->data['title_GP'])) {
			return trim($this->data['title_GP']);
		}

		// Jika title_GP tidak ada, gunakan nama aplikasi dari sumber alternatif
		if (!empty($this->data['app_name'])) {
			return trim($this->data['app_name']);
		}

		// Catatan: baris berikut pernah digunakan untuk membersihkan tanda kutip/apostrof
		// tapi sekarang sudah tidak dipakai (mungkin dipindah ke fungsi lain)
		// return $this->clean_apostrophe($title);

		// Fallback terakhir jika semua sumber judul kosong
		return 'Untitled App';
	}
	
	/**
	 * Membersihkan judul aplikasi agar aman dan rapi untuk ditampilkan
	 *
	 * Fungsi ini menghapus tag HTML, mengubah entitas HTML menjadi karakter asli,
	 * dan membersihkan spasi berlebih. Cocok untuk:
	 * - Judul asli yang disimpan di meta (wp_title_GP, dll)
	 * - Teks yang akan ditampilkan di frontend (excerpt, deskripsi)
	 *
	 * @param string $title Judul mentah yang mungkin mengandung HTML atau entitas
	 * @return string Judul yang sudah dibersihkan dan di-trim
	 */
	private function clean_title($title)
	{
		// Hapus semua tag HTML yang mungkin ada (keamanan utama)
		$clean = wp_strip_all_tags($title);

		// Ubah entitas HTML menjadi karakter normal (contoh: &amp; → &, &#039; → ')
		$clean = html_entity_decode($clean, ENT_QUOTES, 'UTF-8');

		// Hilangkan spasi berlebih di awal dan akhir
		return trim($clean);
	}

	/**
	 * Menyiapkan string judul menjadi format slug yang valid untuk post_name di WordPress
	 *
	 * Proses sanitasi ini menghasilkan slug yang:
	 * - Hanya berisi huruf kecil, angka, dan tanda hubung
	 * - Tanpa karakter khusus yang tidak diizinkan
	 * - Tanpa spasi (diganti tanda hubung)
	 * - Tanpa tanda hubung berulang atau di ujung string
	 *
	 * Contoh: "Pokémon GO! 2025 Update" → "pokemon-go-2025-update"
	 *
	 * @param string $title Judul aplikasi yang akan dijadikan slug
	 * @return string Slug yang sudah disanitasi, lowercase, dan siap digunakan sebagai post_name
	 */
	private function sanitize_title_for_post(string $title): string
	{
		// Ubah entitas HTML menjadi karakter asli terlebih dahulu
		$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');

		// Hapus semua tag HTML (jaga-jaga jika masih ada)
		$title = wp_strip_all_tags($title);

		// Ganti semua karakter selain huruf, angka, spasi, dan tanda minus menjadi spasi
		// Flag /u untuk mendukung karakter multibyte (UTF-8)
		$title = preg_replace('/[^a-zA-Z0-9\s-]/u', ' ', $title);

		// Ganti banyak spasi menjadi satu spasi saja
		$title = preg_replace('/\s+/', ' ', $title);

		// Hilangkan spasi di awal dan akhir
		$title = trim($title);

		// Ganti spasi menjadi tanda hubung
		$title = str_replace(' ', '-', $title);

		// Ganti tanda hubung berulang menjadi satu saja (contoh: --- → -)
		$title = preg_replace('/-+/', '-', $title);

		// Ubah seluruh huruf menjadi lowercase
		// Hilangkan tanda hubung di awal/akhir (jika ada)
		return strtolower(trim($title, '-'));
	}
    
	/**
	 * Mencari apakah postingan aplikasi yang sama sudah ada di WordPress
	 *
	 * Fungsi ini memeriksa duplikat dengan urutan prioritas ketat:
	 * 1. Berdasarkan GP_ID / Package Name (paling akurat & unik)
	 * 2. Berdasarkan source_url (URL asli sumber scraper)
	 * 3. Fallback: judul persis (exact title match) – kurang akurat, hanya jika di atas tidak ada
	 *
	 * Digunakan untuk mencegah pembuatan postingan duplikat saat import.
	 *
	 * @param string $clean_title Judul yang sudah dibersihkan (hanya digunakan sebagai fallback)
	 * @return int|null ID post yang sudah ada (jika ditemukan duplikat), atau null jika belum ada
	 */
	private function check_existing_post(string $clean_title): ?int
	{
		// ──────────────────────────────────────────────────────────────
		// Prioritas 1: Cek berdasarkan GP_ID / Package Name (paling akurat)
		// ──────────────────────────────────────────────────────────────
		if (!empty($this->data['GP_ID'])) {
			$gp_id = trim($this->data['GP_ID']);

			$existing = get_posts([
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending', 'future'],
				'meta_key'       => 'wp_GP_ID',
				'meta_value'     => $gp_id,
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true, // percepat query, tidak perlu hitung total
			]);

			if (!empty($existing)) {
				return (int) $existing[0];
			}
		}

		// ──────────────────────────────────────────────────────────────
		// Prioritas 2: Cek berdasarkan source_url (URL asli sumber data)
		// ──────────────────────────────────────────────────────────────
		if ($source_url = $this->get_source_url()) {
			$existing = get_posts([
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending', 'future'],
				'meta_key'       => 'source_url',
				'meta_value'     => $source_url,
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
			]);

			if (!empty($existing)) {
				return (int) $existing[0];
			}
		}

		// ──────────────────────────────────────────────────────────────
		// Fallback: Cek berdasarkan judul persis (exact match)
		// Hanya digunakan jika GP_ID dan source_url tidak ada/tidak cocok
		// ──────────────────────────────────────────────────────────────
		if (!empty(trim($clean_title))) {
			$args = [
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending', 'future'],
				'title'          => $clean_title,
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
				'exact'          => true, // pastikan pencarian judul persis, bukan partial
			];

			$posts = get_posts($args);

			if (!empty($posts)) {
				return (int) $posts[0];
			}
		}

		// Tidak ditemukan duplikat sama sekali
		return null;
	}

	/**
	 * Menentukan status post dengan aturan tambahan:
	 * - Jika ada GP_ID (dari input atau meta wp_GP_ID) → ikuti $options['apk_status']
	 * - Jika TIDAK ADA GP_ID sama sekali → paksa jadi 'draft'
	 *   (untuk mencegah publish konten tanpa identitas paket aplikasi yang jelas)
	 *
	 * @return string Status post final ('publish', 'draft', dll)
	 */
	private function determine_post_status()
	{
		$status = $this->options['apk_status'] ?? 'draft';

		$gp_id = trim($this->data['GP_ID'] ?? '');
		
		// Jika ini update, cek juga meta yang sudah ada
		if (empty($gp_id) && $this->is_update && $this->post_id > 0) {
			$gp_id = trim(get_post_meta($this->post_id, 'wp_GP_ID', true));
		}

		if (empty($gp_id)) {
			$status = 'draft';
			error_log("Post status forced to 'draft' because there is no App ID (Package Name)");
		}

		return $status;
	}

	/**
	 * Menentukan judul, slug, konten, cek duplikat, lalu membuat atau memperbarui post aplikasi
	 *
	 * Fungsi ini juga menerapkan aturan khusus:
	 * - Jika ADA GP_ID (dari data impor ATAU meta wp_GP_ID) → ikuti status dari theme options
	 * - Jika TIDAK ADA GP_ID sama sekali → paksa status menjadi 'draft' (untuk keamanan & review manual)
	 *
	 * @return int|WP_Error ID post yang berhasil dibuat/diupdate, atau WP_Error jika gagal
	 */
	private function process_title_and_post()
	{
		// 1. Ambil judul utama yang human-readable
		$display_title = $this->get_primary_title();
		if (empty($display_title)) {
			$display_title = 'Untitled App ' . date('Ymd');
		}

		// 2. Buat slug yang bersih dan SEO-friendly
		$slug = $this->sanitize_title_for_post($display_title);

		// 3. Cek apakah aplikasi ini sudah ada (duplikat)
		$duplicate = $this->check_for_duplicate();
		if ($duplicate) {
			$this->is_update = true;
			$this->post_id   = $duplicate['post_id'];
			error_log("Duplicates found: Post ID {$this->post_id} - Reason : {$duplicate['reason']}");
		} else {
			$this->is_update = false;
			$this->post_id   = 0;
		}

		// 4. Siapkan konten postingan dan sanitasi dengan aman
		$post_content = wp_kses_post($this->get_post_content());

		// 5. Tentukan status post dengan logika tambahan GP_ID
		$desired_status = $this->options['apk_status'] ?? 'draft';

		// Cek keberadaan GP_ID (Package Name)
		$has_gp_id = false;

		// Prioritas 1: dari data impor saat ini
		if (!empty(trim($this->data['GP_ID'] ?? ''))) {
			$has_gp_id = true;
		}
		// Prioritas 2: jika ini update post yang sudah ada, cek meta wp_GP_ID
		elseif ($this->is_update && $this->post_id > 0) {
			$existing_gp_id = get_post_meta($this->post_id, 'wp_GP_ID', true);
			if (!empty(trim($existing_gp_id))) {
				$has_gp_id = true;
			}
		}

		// Aturan khusus: tanpa GP_ID → paksa draft (hindari publish konten tanpa identitas paket)
		if (!$has_gp_id) {
			$desired_status = 'draft';
			error_log("Post ID " . ($this->post_id ?: '[baru]') . "forced to draft: no App ID");
		}

		// 6. Siapkan argumen untuk wp_insert_post atau wp_update_post
		$post_args = [
			'post_title'   => $display_title,
			'post_name'    => $slug,
			'post_content' => $post_content,
			'post_status'  => $desired_status,          // ← status yang sudah dimodifikasi
			'post_type'    => 'post',
			'post_author'  => get_current_user_id() ?: 1,
			'post_date'    => current_time('mysql'),
		];

		// 7. Create atau Update post
		if ($this->is_update && $this->post_id > 0) {
			$post_args['ID'] = $this->post_id;
			$result = wp_update_post($post_args, true);

			if (is_wp_error($result)) {
				error_log("Failed to update post ID {$this->post_id}: " . $result->get_error_message());
				return $result;
			}

			$this->post_id = (int) $result;
		} else {
			// Insert post baru
			$result = wp_insert_post($post_args, true);

			if (is_wp_error($result)) {
				error_log("Failed to insert new post: " . $result->get_error_message());
				return $result;
			}

			$this->post_id = (int) $result;
		}

		// 8. Optional: Buat excerpt otomatis (membantu SEO dan preview)
		wp_update_post([
			'ID'           => $this->post_id,
			'post_excerpt' => wp_trim_words(strip_tags($post_content), 55, '...'),
		], true);

		return $this->post_id;
	}

	/**
	 * Menentukan judul, cek duplikat (lebih akurat), lalu create atau update post
	 *
	 * @return int|WP_Error Post ID atau error
	 */
	private function process_title_and_post2()
	{
		// 1. Ambil judul utama
		$display_title = $this->get_primary_title();
		if (empty($display_title)) {
			$display_title = 'Untitled App ' . date('Ymd');
		}

		// 2. Buat slug
		$slug = $this->sanitize_title_for_post($display_title);

		// 3. Cek duplikat dengan function baru
		$already_exists = $this->handle_already_exists();

		if ($already_exists) {
			// Kembalikan info duplikat untuk AJAX
			return $already_exists; // array dengan post_id, edit_link, message
		}

		// Jika bukan duplikat → lanjut buat post baru
		$this->is_update = false;
		$this->post_id = 0;

		$post_content = wp_kses_post($this->get_post_content());

		$post_args = [
			'post_title'   => $display_title,
			'post_name'    => $slug,
			'post_content' => $post_content,
			'post_status'  => $this->options['apk_status'] ?? 'draft',
			'post_type'    => 'post',
			'post_author'  => get_current_user_id() ?: 1,
			'post_date'    => current_time('mysql'),
		];

		$result = wp_insert_post($post_args, true);
		if (is_wp_error($result)) {
			error_log("Insert new post failed: " . $result->get_error_message());
			return $result;
		}

		$this->post_id = (int) $result;

		// Set excerpt
		wp_update_post([
			'ID'           => $this->post_id,
			'post_excerpt' => wp_trim_words(strip_tags($post_content), 55, '...'),
		], true);

		return $this->post_id;
	}

	/**
	 * Membuat postingan baru atau memperbarui postingan aplikasi yang sudah ada
	 *
	 * Fungsi ini menangani dua skenario:
	 * - Jika $this->is_update = true dan $this->post_id valid → UPDATE post
	 * - Jika tidak → INSERT post baru
	 *
	 * Langkah-langkah:
	 * 1. Bersihkan judul agar aman dan beri fallback jika kosong
	 * 2. Generate slug jika belum disediakan
	 * 3. Ambil dan sanitasi konten postingan (keamanan XSS)
	 * 4. Siapkan argumen post lengkap
	 * 5. Lakukan insert atau update menggunakan fungsi WordPress
	 *
	 * @param string $display_title Judul aplikasi yang akan ditampilkan (human-readable)
	 * @param string $slug          Slug/post_name yang sudah disanitasi (opsional)
	 * @return int|WP_Error         ID post yang berhasil dibuat/diupdate,
	 *                              atau WP_Error jika gagal
	 */
	private function create_or_update_post(string $display_title, string $slug = '')
	{
		// 1. Pastikan judul bersih, aman, dan tidak kosong
		$post_title = $this->clean_title($display_title);

		// Fallback jika judul benar-benar kosong setelah dibersihkan
		if (empty($post_title)) {
			$post_title = 'Untitled Application ' . date('YmdHis');
		}

		// 2. Generate slug jika parameter slug kosong atau tidak diberikan
		if (empty($slug)) {
			$slug = $this->sanitize_title_for_post($post_title);
		}

		// 3. Ambil konten postingan dari data
		$post_content = $this->get_post_content();

		// Sanitasi konten dengan ketat (hanya izinkan tag HTML yang aman sesuai aturan WordPress)
		// Ini sangat penting untuk mencegah serangan XSS dari data scraper
		$post_content = wp_kses_post($post_content);

		// 4. Siapkan argumen lengkap untuk wp_insert_post atau wp_update_post
		$post_args = [
			'post_title'   => $post_title,                    // Judul yang sudah dibersihkan
			'post_name'    => $slug,                          // Slug yang valid untuk URL
			'post_content' => $post_content,                  // Konten yang sudah disanitasi
			'post_status'  => $this->options['apk_status'] ?? 'draft', // Dari pengaturan theme
			'post_type'    => 'post',
			'post_author'  => get_current_user_id() ?: 1,     // Gunakan user yang sedang login, fallback ke admin (ID 1)
			'post_date'    => current_time('mysql'),          // Set waktu sekarang (opsional tapi membantu sorting)
		];

		// 5. Tentukan apakah ini update atau insert baru
		if ($this->is_update && $this->post_id > 0) {
			// Mode UPDATE: tambahkan ID post yang sudah ada
			$post_args['ID'] = $this->post_id;

			$result = wp_update_post($post_args, true);

			if (is_wp_error($result)) {
				error_log("Failed to update post - ID {$this->post_id}: " . $result->get_error_message());
				return $result;
			}

			// Jika berhasil update, kembalikan ID post
			return (int) $this->post_id;
		}

		// Mode INSERT: buat postingan baru
		$result = wp_insert_post($post_args, true);

		if (is_wp_error($result)) {
			error_log("Failed to insert new post: " . $result->get_error_message());
			return $result;
		}

		// Simpan ID post yang baru dibuat ke properti class
		$this->post_id = (int) $result;

		// Set status internal menjadi update (untuk proses selanjutnya di class ini)
		$this->is_update = true;

		// Kembalikan ID post sebagai hasil sukses
		return $this->post_id;
	}

	/**
	 * Ambil isi konten postingan berdasarkan prioritas sumber
	 *
	 * @return string Konten untuk post_content
	 */
	private function get_post_content()
	{
		// Prioritas 1: Jika opsi theme mengizinkan, gunakan article_content
		if (!empty($this->options['content_post'])) {
			return $this->data['article_content'] ?? '';
		}

		// Prioritas 2: Deskripsi lengkap dari Google Play
		if (!empty($this->data['articlebody_GP'])) {
			return $this->data['articlebody_GP'];
		}

		// Fallback: article_content jika ada
		return $this->data['article_content'] ?? '';
	}

	/**
	 * Memproses dan menetapkan gambar utama (featured image / thumbnail) untuk postingan
	 *
	 * Prioritas:
	 * 1. poster_GP (dari Google Play) atau poster_sc
	 * 2. Jika tidak ada → gunakan screenshot pertama (images_GP[0])
	 * Jika tetap tidak ada → tandai meta _missing_featured_image
	 *
	 * Gambar di-download, di-attach ke media library, lalu dijadikan featured image.
	 */
	private function process_featured_image()
	{
		// Ambil URL poster dari sumber utama (Google Play atau scraper)
		$poster_url = trim($this->data['poster_GP'] ?? $this->data['poster_sc'] ?? '');

		// Jika poster utama tidak ada atau bukan URL valid → coba fallback ke screenshot pertama
		if (empty($poster_url) || !filter_var($poster_url, FILTER_VALIDATE_URL)) {
			$poster_url = trim($this->data['images_GP'][0] ?? '');
			
			// Jika tetap tidak ada gambar yang valid → tandai post ini kekurangan featured image
			if (empty($poster_url) || !filter_var($poster_url, FILTER_VALIDATE_URL)) {
				update_post_meta($this->post_id, '_missing_featured_image', true);
				return;
			}
		}

		// Buat nama file yang rapi dan unik berdasarkan judul + ID post
		$title = sanitize_title($this->data['title_GP'] ?? 'app') . '-' . $this->post_id;

		// Download dan attach gambar ke media library (format .webp)
		$attach_id = $this->download_and_attach_image($poster_url, $title . '.webp', $this->post_id);

		// Jika berhasil → jadikan featured image postingan
		if ($attach_id && !is_wp_error($attach_id)) {
			set_post_thumbnail($this->post_id, $attach_id);
		} else {
			// Jika gagal → tandai meta agar admin tahu ada masalah
			update_post_meta($this->post_id, '_missing_featured_image', true);
		}
	}

	/**
	 * Memproses gambar latar belakang (background image) jika tersedia
	 *
	 * Hanya diproses jika URL backgrounds_GP valid.
	 * Gambar di-download dan ID attachment disimpan ke dua meta key.
	 */
	private function process_background_image()
	{
		// Ambil URL background dari data Google Play
		$bg_url = isset($this->data['backgrounds_GP']) ? $this->data['backgrounds_GP'] : '';

		// Jika kosong atau bukan URL valid → skip proses
		if (empty($bg_url) || !filter_var($bg_url, FILTER_VALIDATE_URL)) {
			return;
		}

		// Buat nama file unik
		$title = sanitize_title(isset($this->data['title_GP']) ? $this->data['title_GP'] : 'app') . '-' . $this->post_id;

		// Download dan attach ke media library
		$attach_id = $this->download_and_attach_image($bg_url, $title . '-bg.webp', $this->post_id);

		// Jika berhasil → simpan ID ke meta (dua key untuk kompatibilitas theme)
		if ($attach_id && !is_wp_error($attach_id)) {
			update_post_meta($this->post_id, '_background_image_id', $attach_id);
			update_post_meta($this->post_id, 'background_images', $attach_id);
		}
	}

	/**
	 * Memproses kumpulan screenshot aplikasi dan menyimpannya sebagai galeri
	 *
	 * - Batas jumlah screenshot diambil dari opsi theme (import_ss_limit, default 5)
	 * - Jika import_screenshots aktif → download & upload ulang ke media library
	 * - Jika tidak aktif → hanya simpan URL asli (hemat ruang server)
	 * - Hasil disimpan ke meta gallery_data dalam format array
	 */
	private function process_screenshots()
	{
		// Jika tidak ada array screenshot → langsung keluar
		if (empty($this->data['images_GP']) || !is_array($this->data['images_GP'])) {
			return;
		}

		// Ambil batas jumlah screenshot dari pengaturan theme (default 5)
		$limit = (int) (isset($this->options['import_ss_limit']) ? $this->options['import_ss_limit'] : 5);

		// Siapkan struktur galeri
		$gallery = ['image_url' => []];

		if (!empty($this->options['import_screenshots'])) {
			// Mode: Upload ulang screenshot ke media library (direkomendasikan untuk performa & kontrol)
			for ($i = 0; $i < $limit && isset($this->data['images_GP'][$i]); $i++) {
				$url = trim($this->data['images_GP'][$i]);
				if (empty($url)) continue;

				// Buat nama file unik untuk screenshot
				$name = $this->generate_screenshot_name($i + 1);

				// Download dan attach
				$attach_id = $this->download_and_attach_image($url, $name . '.webp', $this->post_id);

				if ($attach_id && !is_wp_error($attach_id)) {
					$gallery['image_url'][] = wp_get_attachment_url($attach_id);
				}
			}
		} else {
			// Mode: Hanya simpan URL asli (tidak upload, hemat storage)
			for ($i = 0; $i < $limit && isset($this->data['images_GP'][$i]); $i++) {
				$url = trim($this->data['images_GP'][$i]);
				if (!empty($url)) {
					$gallery['image_url'][] = $url;
				}
			}
		}

		// Jika ada setidaknya satu gambar → simpan ke meta
		if (!empty($gallery['image_url'])) {
			update_post_meta($this->post_id, 'gallery_data', $gallery);
		}
	}

	/**
	 * Membuat nama file unik untuk screenshot yang diupload
	 *
	 * Format: [judul-sanitized]-[random-6char]-screenshot-[nomor]
	 * Contoh: my-app-abc123-screenshot-1.webp
	 *
	 * @param int $index Nomor urut screenshot (mulai dari 1)
	 * @return string Nama file tanpa ekstensi
	 */
	private function generate_screenshot_name($index)
	{
		// Ambil judul aplikasi dan sanitasi agar aman untuk nama file
		$title = sanitize_title(isset($this->data['title_GP']) ? $this->data['title_GP'] : 'app');

		// Buat string random 6 karakter untuk menghindari konflik nama file
		$rand = substr(md5(uniqid(rand(), true)), 0, 6);

		// Gabungkan semua bagian
		return $title . '-' . $rand . '-screenshot-' . $index;
	}

	/**
	 * Mengunduh gambar dari URL eksternal, menyimpannya ke media library WordPress,
	 * memvalidasi bahwa itu benar-benar gambar, lalu membuat attachment post lengkap.
	 *
	 * Fungsi ini digunakan untuk featured image, background, dan screenshot.
	 * Proses dilakukan secara aman menggunakan API WordPress resmi.
	 *
	 * @param string $url      URL gambar yang akan diunduh
	 * @param string $filename Nama file yang diinginkan (termasuk ekstensi, biasanya .webp)
	 * @param int    $post_id  ID post yang akan menjadi parent attachment
	 * @return int|WP_Error    ID attachment jika berhasil, atau WP_Error jika gagal
	 */
	private function download_and_attach_image(string $url, string $filename, int $post_id)
	{
		// 1. Download gambar menggunakan WP HTTP API (lebih aman, reliable, dan terintegrasi)
		$response = wp_remote_get($url, [
			'timeout'     => 25,                           // batas waktu 25 detik
			'sslverify'   => false,                        // matikan verifikasi SSL (opsional, sering diperlukan untuk scraper)
			'redirection' => 5,                            // ikuti redirect maksimal 5 kali
			'user-agent'  => 'WordPress/APK-Importer/' . get_bloginfo('version'),
			'headers'     => ['Referer' => home_url()],    // tambah referer agar tidak diblok beberapa server
		]);

		// Jika request gagal (timeout, koneksi error, dll)
		if (is_wp_error($response)) {
			return $response;
		}

		// Ambil isi file dan status HTTP
		$image_data  = wp_remote_retrieve_body($response);
		$status_code = wp_remote_retrieve_response_code($response);

		// Pastikan status 200 dan ada data yang diunduh
		if (200 !== $status_code || empty($image_data)) {
			return new WP_Error(
				'download_failed',
				"Failed to download image (HTTP {$status_code}): {$url}"
			);
		}

		// 2. Simpan file ke folder uploads WordPress (handle konflik nama otomatis)
		$upload = wp_upload_bits($filename, null, $image_data);

		if ($upload['error']) {
			return new WP_Error('upload_failed', $upload['error']);
		}

		// 3. Validasi ketat: pastikan file benar-benar gambar
		$file_info = wp_check_filetype_and_ext($upload['file'], $filename);

		if (!$file_info['ext'] || !$file_info['type'] || false === strpos($file_info['type'], 'image/')) {
			// Hapus file yang tidak valid agar tidak menumpuk sampah di server
			@unlink($upload['file']);
			return new WP_Error('invalid_image', 'File bukan gambar yang valid');
		}

		// 4. Buat entri attachment di database WordPress
		$attachment = [
			'guid'           => $upload['url'],
			'post_mime_type' => $file_info['type'],
			'post_title'     => preg_replace('/\.[^.]+$/', '', $filename), // judul tanpa ekstensi
			'post_content'   => '',
			'post_status'    => 'inherit',
			'post_parent'    => $post_id, // hubungkan ke post terkait
		];

		$attach_id = wp_insert_attachment($attachment, $upload['file'], $post_id);

		if (is_wp_error($attach_id)) {
			return $attach_id;
		}

		// 5. Generate metadata dan thumbnails (penting untuk ukuran berbeda di frontend)
		require_once ABSPATH . 'wp-admin/includes/image.php';
		$attach_data = wp_generate_attachment_metadata($attach_id, $upload['file']);
		wp_update_attachment_metadata($attach_id, $attach_data);

		// Sukses: kembalikan ID attachment
		return $attach_id;
	}

	/**
	 * Mengambil ID attachment dari featured image (thumbnail) suatu post
	 *
	 * Fungsi helper ini berguna untuk:
	 * - Mengecek apakah post sudah punya featured image
	 * - Mendapatkan ID gambar untuk ditampilkan atau diproses lebih lanjut
	 *
	 * @param int|null $post_id ID post yang ingin dicek (default: gunakan $this->post_id)
	 * @return int|null         ID attachment featured image, atau null jika tidak ada
	 */
	public function get_featured_image_attachment_id(?int $post_id = null): ?int
	{
		// Jika tidak diberi parameter, gunakan post ID dari instance class ini
		if ($post_id === null) {
			$post_id = $this->post_id;
		}

		// Jika post_id tidak valid/kosong → kembalikan null
		if (!$post_id) {
			return null;
		}

		// Ambil ID featured image dari post
		$thumb_id = get_post_thumbnail_id($post_id);

		// Kembalikan ID jika ada, jika tidak → null
		return $thumb_id ?: null;
	}
	 
	 /**
	 * Menyimpan semua informasi aplikasi ke dalam custom fields (post meta) WordPress
	 *
	 * Fungsi ini bertugas:
	 * - Mengumpulkan dan mengelompokkan semua data penting dari $this->data
	 * - Membersihkan & mensanitasi data sesuai jenisnya (URL, teks, HTML, dll)
	 * - Hanya menyimpan meta yang tidak kosong (dengan pengecualian nilai 0/false yang valid)
	 * - Update meta hanya jika nilainya berbeda dari yang sudah ada (optimasi database)
	 * - Menambahkan timestamp import terakhir untuk tracking
	 *
	 * Semua key meta diawali 'wp_' agar mudah dibedakan dari meta bawaan WordPress.
	 */
	private function save_all_meta()
	{
		// Kelompokkan data agar lebih mudah dikelola dan dikembangkan di masa depan
		$meta_data = [];

		// === Informasi Dasar dari Google Play ===
		$meta_data['wp_GP_ID']          = trim($this->data['GP_ID'] ?? ''); // Package Name unik
		$meta_data['wp_title_GP']       = trim($this->data['title_GP'] ?? $this->data['app_name'] ?? '');
		$meta_data['wp_poster_GP']      = esc_url_raw($this->data['poster_GP'] ?? $this->data['poster_sc'] ?? '');
		$meta_data['wp_backgrounds_GP'] = esc_url_raw($this->data['backgrounds_GP'] ?? '');

		// Buat link Play Store otomatis jika GP_ID tersedia
		$meta_data['wp_playstore_link'] = $meta_data['wp_GP_ID']
			? 'https://play.google.com/store/apps/details?id=' . $meta_data['wp_GP_ID']
			: '';

		// === Gambar & Media ===
		$meta_data['wp_images_GP'] = !empty($this->data['images_GP']) && is_array($this->data['images_GP'])
			? array_map('esc_url_raw', $this->data['images_GP']) // sanitasi semua URL screenshot
			: [];

		// === Statistik & Informasi Update ===
		$meta_data['wp_version_GP']   = sanitize_text_field($this->data['version_GP'] ?? $this->data['version_sc'] ?? '');
		$meta_data['wp_sizes']        = sanitize_text_field($this->data['sizes_GP'] ?? $this->data['size_sc'] ?? ''); // satu key utama
		$meta_data['wp_installs_GP']  = sanitize_text_field($this->data['installs_GP'] ?? '');
		$meta_data['wp_updates_GP']   = sanitize_text_field($this->data['updates_GP'] ?? $this->data['updated_sc'] ?? '');
		$meta_data['wp_requires_GP']  = sanitize_text_field($this->data['requires_GP'] ?? '');
		$meta_data['wp_rated_GP']     = sanitize_text_field($this->data['numberrates'] ?? ''); // jumlah rating
		$meta_data['wp_ratings_GP']   = sanitize_text_field($this->data['numbervoters'] ?? ''); // jumlah voter

		// === Deskripsi & Konten ===
		$meta_data['wp_desck_GP']       = wp_kses_post($this->data['desck_GP'] ?? $this->data['summary_sc'] ?? ''); // ringkasan aman HTML
		$meta_data['wp_articlebody_GP'] = wp_kses_post($this->data['articlebody_GP'] ?? ''); // deskripsi panjang GP
		$meta_data['wp_article_content']= wp_kses_post($this->data['article_content'] ?? ''); // konten dari scraper
		$meta_data['article_gp']        = $meta_data['wp_articlebody_GP']; // alias untuk kemudahan akses

		// === Informasi MOD (jika aplikasi versi modifikasi) ===
		$meta_data['wp_mods']         = wp_kses_post($this->data['mods'] ?? '');
		$meta_data['wp_title_wp_mods']= sanitize_text_field($this->data['full_mod_title'] ?? '');
		$meta_data['wp_mods_post']    = wp_kses_post($this->data['full_mod'] ?? '');
		$meta_data['wp_mods2']        = $meta_data['wp_mods_post']; // alias

		// === Video & Catatan Update ===
		$meta_data['wp_youtube_GP']   = $this->data['youtube_GP'] ?? ''; // link YouTube (trailer/video)
		$meta_data['wp_whatnews_GP']  = wp_kses_post($this->data['whatnews_GP'] ?? ''); // changelog / what's new

		// ──────────────────────────────────────────────────────────────
		// Proses penyimpanan semua meta yang valid
		// ──────────────────────────────────────────────────────────────
		foreach ($meta_data as $key => $value) {
			// Lewati jika nilai benar-benar kosong (kecuali 0 atau false yang valid)
			if (empty($value) && $value !== '0' && $value !== 0 && $value !== false) {
				continue;
			}

			// Ambil nilai meta saat ini untuk perbandingan
			$current = get_post_meta($this->post_id, $key, true);

			// Hanya update jika nilai berbeda (menghemat query database)
			if ($current !== $value) {
				$updated = update_post_meta($this->post_id, $key, $value);

				if (!$updated) {
					error_log("Meta update failed {$key} for post {$this->post_id}");
				}
			}
		}

		// Catat waktu import terakhir (berguna untuk tracking update otomatis)
		update_post_meta($this->post_id, '_last_imported', current_time('mysql'));
	}
	 
	 /**
	 * Menyimpan semua taxonomy terkait postingan aplikasi
	 *
	 * Taxonomy yang ditangani:
	 * 1. 'developer'     → custom taxonomy untuk nama pengembang aplikasi
	 * 2. 'post_tag'      → tag bawaan WordPress
	 * 3. 'category'      → kategori hierarkis (parent-child)
	 *
	 * Semua data dibersihkan dan difilter agar tidak ada duplikat atau nilai kosong.
	 */
	private function save_taxonomies()
	{
		// ──────────────────────────────────────────────────────────────
		// 1. Developer (custom taxonomy 'developer')
		// ──────────────────────────────────────────────────────────────
		$developer_raw = trim($this->data['developers_GP'] ?? $this->data['developer_sc'] ?? '');

		if ($developer_raw !== '') {
			// Bersihkan input: hapus tag, normalisasi spasi
			$developer_clean = sanitize_text_field($developer_raw);
			$developer_clean = preg_replace('/\s+/', ' ', $developer_clean);

			// Pecah string menjadi array jika ada pemisah (koma, titik, spasi berlebih)
			$developers = array_map('trim', preg_split('/[\s,.;]+/', $developer_clean));

			// Filter nilai kosong dan hilangkan duplikat
			$developers = array_filter($developers);
			$developers = array_unique($developers);

			if (!empty($developers)) {
				$result = wp_set_post_terms($this->post_id, $developers, 'developer', true); // true = append

				if (is_wp_error($result)) {
					error_log("Failed to set developer terms for post {$this->post_id}: " . $result->get_error_message());
				}
			}
		}

		// ──────────────────────────────────────────────────────────────
		// 2. Tags (taxonomy bawaan 'post_tag')
		// ──────────────────────────────────────────────────────────────
		$tags_input = $this->data['tag'] ?? '';

		if (!empty($tags_input)) {
			// Jika input berupa array → sanitasi langsung
			// Jika string → pecah berdasarkan koma
			$tags = is_array($tags_input)
				? array_map('sanitize_text_field', array_map('trim', $tags_input))
				: array_map('sanitize_text_field', array_map('trim', explode(',', $tags_input)));

			// Hilangkan nilai kosong dan duplikat
			$tags = array_filter($tags);
			$tags = array_unique($tags);

			if (!empty($tags)) {
				$result = wp_set_post_tags($this->post_id, $tags, true); // true = append (tidak hapus tag lama)

				if (is_wp_error($result)) {
					error_log("Failed to set tags for post {$this->post_id}: " . $result->get_error_message());
				}
			}
		}

		// ──────────────────────────────────────────────────────────────
		// 3. Kategori hierarkis (parent → child)
		// ──────────────────────────────────────────────────────────────
		$this->set_hierarchical_categories();
	}

	/**
	 * Mengatur kategori hierarkis (parent dan child) untuk postingan
	 *
	 * Logika:
	 * - Parent diambil dari 'p_category'
	 * - Child diambil dari 'genres_GP', 'SubCategory', atau 'genre_sc'
	 * - Jika parent belum ada → buat baru
	 * - Jika child belum ada → buat sebagai anak dari parent (jika parent ada)
	 * - Hanya child yang di-assign ke post (parent tidak di-assign)
	 */
	private function set_hierarchical_categories()
	{
		// Ambil nama parent dan child dari data
		$parent_name = trim($this->data['p_category'] ?? '');
		$child_name = trim(
			$this->data['genres_GP'] ??
			($this->data['SubCategory'] ?? ($this->data['genre_sc'] ?? ''))
		);

		// Jika keduanya kosong → tidak perlu diproses
		if (empty($parent_name) && empty($child_name)) {
			return;
		}

		$parent_id = 0;

		// ──────────────────────────────────────────────────────────────
		// Proses Parent Category
		// ──────────────────────────────────────────────────────────────
		if ($parent_name) {
			$parent_slug = sanitize_title($parent_name);
			$parent = get_term_by('slug', $parent_slug, 'category');

			if ($parent) {
				$parent_id = $parent->term_id;
			} else {
				// Buat term parent baru jika belum ada
				$result = wp_insert_term($parent_name, 'category', ['slug' => $parent_slug]);

				if (!is_wp_error($result)) {
					$parent_id = $result['term_id'];
				}
			}
		}

		// ──────────────────────────────────────────────────────────────
		// Proses Child Category
		// ──────────────────────────────────────────────────────────────
		if ($child_name) {
			$child_slug = sanitize_title($child_name);
			$child = get_term_by('slug', $child_slug, 'category');

			if ($child) {
				// Jika sudah ada → assign langsung ke post
				wp_set_post_categories($this->post_id, [$child->term_id], false);
			} elseif ($parent_id) {
				// Jika belum ada → buat sebagai child dari parent
				$result = wp_insert_term(
					$child_name,
					'category',
					[
						'parent' => $parent_id,
						'slug'   => $child_slug
					]
				);

				if (!is_wp_error($result)) {
					wp_set_post_categories($this->post_id, [$result['term_id']], false);
				}
			}
		}

		// Catatan: Parent category TIDAK di-assign ke post
		// (sesuai desain kode asli - hanya child yang digunakan)
	}

	/**
	 * Menambahkan link direct APK dari Google Play (via shortener) 
	 * ke repeatable_fields dan group_downloads_links
	 * 
	 * Hanya ditambahkan jika:
	 * - Belum ada duplikat (berdasarkan URL atau title + size)
	 * - Package ID (GP_ID) tersedia
	 */
	public function add_direct_apk_download(): void
	{
		if (empty($this->data['GP_ID'])) {
			return;
		}

		$package = trim($this->data['GP_ID']);

		// Ambil data yang dibutuhkan
		$size = trim($this->data['sizes_GP'] ?? $this->data['size_sc'] ?? '');
		$app_title = trim($this->data['title_GP'] ?? $this->data['app_name'] ?? 'Application');
		$download_title = $app_title . ' APK';

		// Buat URL sumber
		$base_url = 'https://exthem.es/apk.php?pkg=' . urlencode($package);

		// Shorten link menggunakan method yang sudah ada di class
		$short_url = $this->ouo_io_exhemes($base_url);

		// Fallback jika shortener gagal atau hasilnya tidak valid
		if (empty($short_url) || $short_url === $base_url || !filter_var($short_url, FILTER_VALIDATE_URL)) {
			$short_url = $base_url;
			error_log("[APK Importer] Shortener failed for GP_ID {$package} - fallback to direct URL");
		}

		// Siapkan entry baru
		$new_entry = [
			'name'   => sanitize_text_field($download_title),
			'title'  => sanitize_text_field($download_title),
			'url'    => esc_url_raw($short_url),
			'links'  => esc_url_raw($short_url),
			'size'   => $size,
			'sizes1' => $size,
			'sizes'  => $size,
			'tipes'  => 'APK',
			'notes'  => 'Direct from Google Play',
		];

		// ───────────────────────────────────────────────────────────
		// Ambil data existing dari primary key
		// ───────────────────────────────────────────────────────────
		$downloads = (array) get_post_meta($this->post_id, 'repeatable_fields', true);

		// Jika repeatable_fields kosong, coba ambil dari group_downloads_links sebagai fallback
		if (empty($downloads)) {
			$fallback = (array) get_post_meta($this->post_id, 'group_downloads_links', true);
			if (!empty($fallback)) {
				$downloads = $fallback;
				error_log("[APK Importer] Using group_downloads_links as fallback for post {$this->post_id}");
			}
		}

		// Cek duplikat (lebih ketat)
		$is_duplicate = false;
		foreach ($downloads as $item) {
			if (
				// Cek berdasarkan URL (paling akurat)
				(isset($item['url']) && $item['url'] === $short_url) ||
				// Cek title + size (untuk kompatibilitas data lama)
				(
					isset($item['title'], $item['size']) &&
					$item['title'] === $download_title &&
					$item['size'] === $size
				)
			) {
				$is_duplicate = true;
				break;
			}
		}

		// Jika duplikat → skip
		if ($is_duplicate) {
			// Optional: bisa uncomment jika ingin tahu berapa sering terjadi duplikat
			// error_log("[APK Importer] Direct APK link skipped (duplicate) for post {$this->post_id}");
			return;
		}

		// Tambahkan link baru di akhir list
		$downloads[] = $new_entry;

		// Simpan ke KEDUA meta seperti permintaanmu
		$success1 = update_post_meta($this->post_id, 'repeatable_fields', $downloads);
		$success2 = update_post_meta($this->post_id, 'group_downloads_links', $downloads);

		// Logging hasil
		if ($success1 === false || $success2 === false) {
			error_log("[APK Importer] Failed to update download links for post {$this->post_id} (repeatable_fields: " . ($success1 ? 'OK' : 'FAILED') . ", group_downloads_links: " . ($success2 ? 'OK' : 'FAILED') . ")");
		} else {
			// Optional: log sukses untuk monitoring
			// error_log("[APK Importer] Added direct Google Play APK link to post {$this->post_id}: {$download_title}");
		}
	}

	/**
	 * Memproses dan memperpendek URL menggunakan service shortener yang dipilih
	 * Jika fitur dimatikan atau gagal, akan mengembalikan URL asli
	 *
	 * @param string $url URL yang akan di-short
	 * @return string URL yang sudah di-short (jika berhasil) atau URL asli
	 */
	public function shorten_links(string $url): string
	{
		// Validasi input dasar
		if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) {
			return $url;
		}

		// Ambil pengaturan dari options
		$enable_auto = (bool) ($this->options['enable_shortlinks'] ?? false);

		// Jika fitur dimatikan → langsung return URL asli
		if (!$enable_auto) {
			return $url;
		}

		// Ambil service yang dipilih (default ke ouo jika tidak ada setting)
		$service = trim($this->options['auto_shortlink'] ?? 'ouo_sl');

		// Mapping service ke method (lebih mudah ditambah/dikelola di masa depan)
		$service_handlers = [
			'ouo_sl'       => [$this, 'ouo_io_user'],
			'shrinkearn_sl' => [$this, 'shrinkearn_user'],
			'paid4link_sl'  => [$this, 'paid4link_user'],
			'adurl_sl'      => [$this, 'adurl_user'],
		];

		$shortened_url = $url; // default fallback

		if (isset($service_handlers[$service]) && is_callable($service_handlers[$service])) {
			try {
				$handler = $service_handlers[$service];
				$result = call_user_func($handler, $url);

				// Pastikan hasil adalah URL valid
				if (is_string($result) && filter_var($result, FILTER_VALIDATE_URL)) {
					$shortened_url = $result;
				} else {
					error_log("[Shortener] Service {$service} returned invalid URL for: {$url}");
				}
			} catch (Exception $e) {
				error_log("[Shortener] Exception in {$service} for URL {$url}: " . $e->getMessage());
			}
		} else {
			error_log("[Shortener] Unknown or invalid service selected: {$service}");
		}

		// Pastikan hasil akhir adalah URL yang aman & valid
		return esc_url_raw($shortened_url) ?: $url;
	}
		
	/**
	 * Fungsi pembantu untuk melakukan request ke API shortener menggunakan wp_remote_get
	 * 
	 * @param string $api_url       URL API lengkap yang sudah disiapkan
	 * @param string $expected_domain Domain yang harus ada di hasil shortlink
	 * @return string URL pendek yang valid atau string kosong jika gagal
	 */
	private function shorten_via_wp_remote(string $api_url, string $expected_domain): string
	{
		$response = wp_remote_get($api_url, [
			'timeout'     => 12,
			'sslverify'   => false,           // ubah ke true jika server punya CA cert yang bagus
			'redirection' => 5,
			'user-agent'  => 'WordPress/APK-Importer/' . get_bloginfo('version'),
			'headers'     => [
				'Referer' => home_url(),
			],
		]);

		if (is_wp_error($response)) {
			error_log("[Shortener] wp_remote_get failed: " . $response->get_error_message() . " for URL: " . $api_url);
			return '';
		}

		$code = wp_remote_retrieve_response_code($response);
		$body = trim(wp_remote_retrieve_body($response));

		if ($code !== 200 || empty($body)) {
			error_log("[Shortener] Invalid response - HTTP {$code} - Body: " . substr($body, 0, 100) . "...");
			return '';
		}

		if (
			filter_var($body, FILTER_VALIDATE_URL) &&
			stripos($body, $expected_domain) !== false
		) {
			return $body;
		}

		error_log("[Shortener] Response not a valid short URL (expected {$expected_domain}): " . $body);
		return '';
	}

	/* ──────────────────────────────────────────────────────────────
	   Versi hard-coded (exhemes) - sebaiknya hanya untuk testing
	   atau backward compatibility, jangan digunakan di production
	─────────────────────────────────────────────────────────────── */

		
	/**
	 * Shortener khusus OUO.io menggunakan API key hard-coded (versi exhemes/legacy)
	 * 
	 * @param string $url URL yang akan di-short
	 * @return string URL yang sudah di-short atau URL asli jika gagal
	 */

	/**
	 * Shortener OUO.io versi legacy/hard-coded (exhemes style)
	 */
	private function ouo_io_exhemes(string $url): string
	{
		$apikey = 'mH4aOrLs';
		$api_url = "http://ouo.io/api/{$apikey}?s=" . urlencode($url);

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $api_url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_TIMEOUT, 12);          // tambahan rekomendasi: batasi waktu tunggu
		$response = curl_exec($ch);
		curl_close($ch);

		if ($response !== false) {
			$short = trim($response);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'ouo.io') !== false) {
				return $short;
			}
		}

		// fallback file_get_contents seperti kode asli
		$html = @file_get_contents($api_url);
		if ($html !== false) {
			$short = trim($html);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'ouo.io') !== false) {
				return $short;
			}
		}

		return $url;
	}

	/**
	 * Shortener ShrinkEarn versi legacy/hard-coded (exhemes style)
	 */
	private function shrinkearn_exhemes(string $url): string
	{
		$apikey = '3e0539585e9287452cd46529df428fd8f166a6f8';
		$api_url = "https://shrinkearn.com/api?api={$apikey}&url=" . urlencode($url) . "&format=text";

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $api_url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_TIMEOUT, 12);
		$response = curl_exec($ch);
		curl_close($ch);

		if ($response !== false) {
			$short = trim($response);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'shrinkearn.com') !== false) {
				return $short;
			}
		}

		$html = @file_get_contents($api_url);
		if ($html !== false) {
			$short = trim($html);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'shrinkearn.com') !== false) {
				return $short;
			}
		}

		return $url;
	}

	/**
	 * Shortener AdURL versi legacy/hard-coded (exhemes style)
	 */
	private function adurl_exhemes(string $url): string
	{
		$apikey = 'd14c59dfb644f1f9f9aa0e4ad120fa7245db255b';
		$api_url = "https://adurl.io/api?api={$apikey}&url=" . urlencode($url) . "&format=text";

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $api_url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_TIMEOUT, 12);
		$response = curl_exec($ch);
		curl_close($ch);

		if ($response !== false) {
			$short = trim($response);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'adurl.io') !== false) {
				return $short;
			}
		}

		$html = @file_get_contents($api_url);
		if ($html !== false) {
			$short = trim($html);
			if (filter_var($short, FILTER_VALIDATE_URL) && stripos($short, 'adurl.io') !== false) {
				return $short;
			}
		}

		return $url;
	}

	private function ouo_io_user(string $url): string
	{
		$apikey = trim($this->options['ouo_apikey'] ?? '');
		if (empty($apikey)) {
			return $url;
		}

		$api_url = "http://ouo.io/api/{$apikey}?s=" . urlencode($url);
		$short = $this->shorten_via_wp_remote($api_url, 'ouo.io');

		return $short ?: $url;
	}

	private function shrinkearn_user(string $url): string
	{
		$apikey = trim($this->options['shrinkearn_apikey'] ?? '');
		if (empty($apikey)) {
			return $url;
		}

		$api_url = "https://shrinkearn.com/api?api={$apikey}&url=" . urlencode($url) . "&format=text";
		$short = $this->shorten_via_wp_remote($api_url, 'shrinkearn.com');

		return $short ?: $url;
	}

	private function paid4link_user(string $url): string
	{
		$apikey = trim($this->options['paid4link_apikey'] ?? '');
		if (empty($apikey)) {
			return $url;
		}

		$api_url = "https://paid4link.com/api?api={$apikey}&url=" . urlencode($url) . "&format=text";
		$short = $this->shorten_via_wp_remote($api_url, 'paid4link.com');

		return $short ?: $url;
	}

	private function adurl_user(string $url): string
	{
		$apikey = trim($this->options['adurl_apikey'] ?? '');
		if (empty($apikey)) {
			return $url;
		}

		$api_url = "https://adurl.io/api?api={$apikey}&url=" . urlencode($url) . "&format=text";
		$short = $this->shorten_via_wp_remote($api_url, 'adurl.io');

		return $short ?: $url;
	}
   
	/**
	 * Cek apakah aplikasi ini sudah pernah diimport sebelumnya
	 * Menggunakan beberapa kriteria dengan prioritas ketat
	 *
	 * @return array|null [ 'post_id' => int, 'reason' => string ] atau null jika bukan duplikat
	 */
	public function check_for_duplicate(): ?array
	{
		// --- Prioritas 1: Cek berdasarkan Google Play ID / Package Name (paling akurat) ---
		$gp_id = trim($this->data['GP_ID'] ?? '');
		if ($gp_id !== '') {
			$existing = get_posts([
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending', 'future'],
				'meta_key'       => 'wp_GP_ID',
				'meta_value'     => $gp_id,
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
			]);

			if (!empty($existing)) {
				return [
					'post_id' => (int) $existing[0],
					'reason'  => 'Duplicate by Google Play Package ID (GP_ID)',
				];
			}
		}

		// --- Prioritas 2: Cek berdasarkan source_url (URL asli dari 5play/apkmody/dll) ---
		$source_url = $this->get_source_url();
		if ($source_url) {
			$existing = get_posts([
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending', 'future'],
				'meta_key'       => 'source_url',
				'meta_value'     => $source_url,
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
			]);

			if (!empty($existing)) {
				return [
					'post_id' => (int) $existing[0],
					'reason'  => 'Duplicate by Source URL',
				];
			}
		}

		// --- Prioritas 3: Kombinasi Title bersih + Version + Size (sangat kuat untuk fallback) ---
		$clean_title = $this->clean_title($this->get_primary_title());
		$version     = $this->get_version();
		$size        = $this->get_size(); // Anda perlu buat method ini, lihat contoh di bawah

		if ($clean_title && $version && $size) {
			$existing = get_posts([
				'post_type'      => 'post',
				'post_status'    => ['publish', 'draft', 'pending'],
				'title'          => $clean_title,
				'meta_query'     => [
					'relation' => 'AND',
					[
						'key'     => 'wp_version_GP',
						'value'   => $version,
						'compare' => '=',
					],
					[
						'key'     => 'wp_sizes_GP', // atau wp_sizes tergantung key yang Anda pakai
						'value'   => $size,
						'compare' => '=',
					],
				],
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
			]);

			if (!empty($existing)) {
				return [
					'post_id' => (int) $existing[0],
					'reason'  => 'Duplicate by Clean Title + Version + Size',
				];
			}
		}

		// --- Optional: Fallback paling lemah (title saja) - sebaiknya tidak dipakai kecuali darurat ---
		// if ($clean_title) {
		//     $existing = get_posts([
		//         'post_type'      => 'post',
		//         'post_status'    => ['publish'],
		//         'title'          => $clean_title,
		//         'posts_per_page' => 1,
		//         'fields'         => 'ids',
		//     ]);
		//     if (!empty($existing)) {
		//         return [
		//             'post_id' => (int) $existing[0],
		//             'reason'  => 'Possible duplicate by title only (weak check)',
		//         ];
		//     }
		// }

		// Tidak ditemukan duplikat
		return null;
	}

 
	/**
	 * Ambil ukuran file aplikasi dengan prioritas sumber yang jelas
	 * 
	 * @return string Ukuran dalam format string (misal "120 MB"), kosong jika tidak ada
	 */
	private function get_size(): string
	{
		$candidates = [
			// Prioritas 1: ukuran spesifik per link (jika ada array)
			$this->data['size_downloadlinks_gma'][0] ?? null,
			// Prioritas 2: ukuran dari Google Play
			$this->data['sizes_GP'] ?? null,
			// Prioritas 3: ukuran dari scraper umum
			$this->data['size_sc'] ?? null,
		];

		foreach ($candidates as $value) {
			if (!empty($value)) {
				// Bersihkan tag HTML, trim spasi, dan normalisasi
				$clean = trim(wp_strip_all_tags((string) $value));
				if ($clean !== '') {
					return $clean;
				}
			}
		}

		return '';
	}

	/**
	 * Ambil informasi versi aplikasi dengan prioritas sumber
	 * 
	 * @return string Versi aplikasi (contoh: "2.1.5"), kosong jika tidak ada
	 */
	private function get_version(): string
	{
		$candidates = [
			// Prioritas 1: versi dari Google Play (paling akurat)
			$this->data['version_GP'] ?? null,
			// Prioritas 2: versi dari scraper
			$this->data['version_sc'] ?? null,
		];

		foreach ($candidates as $value) {
			if (!empty($value)) {
				$clean = trim((string) $value);
				// Opsional: normalisasi format versi (hapus spasi ekstra, dll)
				$clean = preg_replace('/\s+/', ' ', $clean);
				if ($clean !== '') {
					return $clean;
				}
			}
		}

		return '';
	}
	
	private function get_source_url(): ?string
	{
		// Sesuaikan key sesuai struktur data scraper kamu
		return isset($this->data['source_url'])
			? esc_url_raw(trim($this->data['source_url']))
			: (isset($this->data['url']) ? esc_url_raw(trim($this->data['url'])) : null);
	}
	
	/**
	 * Memproses dan menyimpan daftar link download dari sumber 5play
	 * 
	 * @return bool True jika berhasil menyimpan setidaknya satu link valid, false jika tidak ada
	 */
	public function List_Download_5play(): bool
	{
		if (empty($this->data['downloadlinks_gma']) || !is_array($this->data['downloadlinks_gma'])) {
			return false;
		}

		$download_links = [];

		foreach ($this->data['downloadlinks_gma'] as $i => $raw_url) {
			$original_url = trim($raw_url ?? '');
			
			// Skip jika URL tidak valid
			if (empty($original_url) || !filter_var($original_url, FILTER_VALIDATE_URL)) {
				continue;
			}

			$original_url = esc_url_raw($original_url);

			// 1. Tentukan ukuran (priority chain)
			$size = trim(wp_strip_all_tags(
				$this->data['size_downloadlinks_gma'][$i] ??
				$this->data['size_sc'] ??
				($this->data['sizes_GP'][0] ?? '') ??
				''
			));

			// 2. Tentukan tipe/ekstensi
			$type_input = $this->data['types_downloadlinks_gma'][$i] ?? $original_url;
			$filename = basename(parse_url($type_input, PHP_URL_PATH) ?: '');
			$ext = strtoupper(pathinfo($filename, PATHINFO_EXTENSION) ?: 'APK');

			// 3. Proses URL final (upload jika diaktifkan)
			$final_url = $original_url;

			if (!empty($this->options['import_apk'])) {
				$attach_id = $this->upload_remote_file_apk($original_url, $this->post_id);
				
				if (!is_wp_error($attach_id)) {
					$final_url = wp_get_attachment_url($attach_id);
					error_log("APK uploaded successfully [post:{$this->post_id}] → {$final_url}");
				} else {
					error_log("APK upload failed [post:{$this->post_id}]: " . $attach_id->get_error_message());
					// tetap pakai original sebagai fallback
				}
			}

			// 4. Shorten link (jika fitur aktif)
			$short_url = $this->shorten_links($final_url) ?: $final_url;

			// 5. Nama & judul link
			$link_name = trim($this->data['name_downloadlinks_gma'][$i] ?? '');
			$link_name = $link_name ?: 'Download APK ' . ($i + 1);

			// 6. Bangun entry
			$download_links[] = [
				'name'   => sanitize_text_field($link_name),
				'title'  => sanitize_text_field($link_name),
				'url'    => $short_url,
				'links'  => $short_url,
				'size'   => $size,
				'sizes1' => $size,
				'sizes'  => $size,
				'tipes'  => $ext,
				'notes'  => sanitize_text_field($this->data['notes_downloadlinks_gma'][$i] ?? ''),
			];
		}

		// Jika tidak ada link yang berhasil diproses
		if (empty($download_links)) {
			return false;
		}

		// Simpan ke kedua meta key (sesuai kebutuhan theme kamu)
		$success1 = update_post_meta($this->post_id, 'repeatable_fields', $download_links);
		$success2 = update_post_meta($this->post_id, 'group_downloads_links', $download_links);

		// Logging hasil (opsional tapi sangat membantu untuk debug)
		if ($success1 === false || $success2 === false) {
			error_log("[List_Download_5play] Gagal update meta download links post {$this->post_id} " .
					  "(repeatable: " . ($success1 ? 'OK' : 'FAILED') . ", group: " . ($success2 ? 'OK' : 'FAILED') . ")");
		} else {
			error_log("[List_Download_5play] Berhasil menyimpan " . count($download_links) . " link untuk post {$this->post_id}");
		}

		return true;
	}
	
	/**
	 * Memproses dan menyimpan daftar link download dari sumber GetModSAPK
	 *
	 * @return bool True jika berhasil menyimpan setidaknya satu link valid, false jika tidak ada
	 */
	public function List_Download_Getmodsapk(): bool
	{
		if (
			empty($this->data['name_downloadlinks_gma']) ||
			!is_array($this->data['name_downloadlinks_gma']) ||
			empty($this->data['apklink']) ||
			!is_array($this->data['apklink'])
		) {
			return false;
		}

		$download_links = [];

		foreach ($this->data['name_downloadlinks_gma'] as $i => $raw_name) {
			$name_link = trim($raw_name ?? '');
			$apk_link  = trim($this->data['apklink'][$i] ?? '');

			// Skip jika nama atau link kosong
			if (empty($name_link) || empty($apk_link)) {
				continue;
			}

			$original_url = esc_url_raw($apk_link);

			// Validasi URL
			if (!filter_var($original_url, FILTER_VALIDATE_URL)) {
				continue;
			}

			$final_url = $original_url;

			// Upload APK ke media library jika fitur diaktifkan
			if (!empty($this->options['import_apk'])) {
				$attach_id = $this->upload_remote_file_apk($original_url, $this->post_id);

				if (!is_wp_error($attach_id)) {
					$final_url = wp_get_attachment_url($attach_id);
					error_log("APK uploaded successfully [GetModSAPK] [post:{$this->post_id}] → {$final_url}");
				} else {
					error_log("APK upload failed [GetModSAPK] [post:{$this->post_id}]: " . $attach_id->get_error_message());
					// tetap pakai original link sebagai fallback
				}
			}

			// Shorten link jika fitur aktif
			$short_url = $this->shorten_links($final_url) ?: $final_url;

			// Tentukan ukuran (priority chain)
			$size = trim(wp_strip_all_tags(
				$this->data['size_downloadlinks_gma'][$i] ??
				$this->data['sizes_GP'] ??
				$this->data['size_sc'] ??
				''
			));

			// Tentukan tipe file (ekstensi)
			$type_input = $this->data['types_downloadlinks_gma'][$i] ?? $original_url;
			preg_match('/\w+$/i', $type_input, $matches);
			$tipes = strtoupper($matches[0] ?? 'APK');

			// Nama link (fallback jika kosong)
			$display_name = $name_link ?: 'Download APK ' . ($i + 1);

			// Bangun entry download
			$download_links[] = [
				'name'   => sanitize_text_field($display_name),
				'title'  => sanitize_text_field($display_name),
				'url'    => $short_url,
				'links'  => $short_url,
				'size'   => $size,
				'sizes1' => $size,
				'sizes'  => $size,
				'tipes'  => $tipes,
				'notes'  => sanitize_text_field($this->data['notes_downloadlinks_gma'][$i] ?? ''),
			];
		}

		// Jika tidak ada link yang berhasil diproses
		if (empty($download_links)) {
			return false;
		}

		// Simpan ke kedua meta key (sesuai kebutuhan theme)
		$success1 = update_post_meta($this->post_id, 'repeatable_fields', $download_links);
		$success2 = update_post_meta($this->post_id, 'group_downloads_links', $download_links);

		// Logging hasil penyimpanan (sangat membantu untuk debug)
		if ($success1 === false || $success2 === false) {
			error_log("[List_Download_Getmodsapk] Gagal update download links post {$this->post_id} " .
					  "(repeatable: " . ($success1 ? 'OK' : 'FAILED') . ", group: " . ($success2 ? 'OK' : 'FAILED') . ")");
		} else {
			error_log("[List_Download_Getmodsapk] Berhasil menyimpan " . count($download_links) . " link untuk post {$this->post_id}");
		}

		return true;
	}
	
	/**
	 * Memproses dan menyimpan daftar link download dari sumber APKMODY
	 *
	 * @return bool True jika berhasil menyimpan minimal 1 link valid
	 */
	public function List_Download_Apkmody(): bool
	{
		if (
			empty($this->data['name_downloadlinks_gma']) ||
			!is_array($this->data['name_downloadlinks_gma']) ||
			empty($this->data['apklink']) ||
			!is_array($this->data['apklink'])
		) {
			return false;
		}

		$download_links = [];

		foreach ($this->data['name_downloadlinks_gma'] as $i => $raw_name) {
			$name_link = trim($raw_name ?? '');
			$apk_link  = trim($this->data['apklink'][$i] ?? '');

			if (empty($name_link) || empty($apk_link)) {
				continue;
			}

			$original_url = esc_url_raw($apk_link);
			if (!filter_var($original_url, FILTER_VALIDATE_URL)) {
				continue;
			}

			$final_url = $original_url;

			// Upload ke media library jika diaktifkan
			if (!empty($this->options['import_apk'])) {
				$attach_id = $this->upload_remote_file_apk($original_url, $this->post_id);
				if (!is_wp_error($attach_id)) {
					$final_url = wp_get_attachment_url($attach_id);
					error_log("APK uploaded [APKMODY] [post:{$this->post_id}] → {$final_url}");
				} else {
					error_log("APK upload failed [APKMODY] [post:{$this->post_id}]: " . $attach_id->get_error_message());
				}
			}

			// Shorten link
			$short_url = $this->shorten_links($final_url) ?: $final_url;

			// Ukuran (priority chain)
			$size = trim(wp_strip_all_tags(
				$this->data['size_downloadlinks_gma'][$i] ??
				$this->data['sizes_GP'] ??
				$this->data['size_sc'] ??
				''
			));

			// Tipe file
			$tipes = strtoupper(trim($this->data['types_downloadlinks_gma'][$i] ?? 'APK'));

			// Nama & title (fallback ke nama jika apkname kosong)
			$display_name = trim($this->data['apkname'][$i] ?? $name_link);
			$display_name = $display_name ?: 'Download APK ' . ($i + 1);

			$download_links[] = [
				'name'   => sanitize_text_field($display_name),
				'title'  => sanitize_text_field($display_name),
				'url'    => $short_url,
				'links'  => $short_url,
				'size'   => $size,
				'sizes1' => $size,
				'sizes'  => $size,
				'tipes'  => $tipes,
				'notes'  => sanitize_text_field($this->data['notes_downloadlinks_gma'][$i] ?? ''),
			];
		}

		if (empty($download_links)) {
			return false;
		}

		// Simpan ke kedua key (sesuai kebutuhan theme)
		$success1 = update_post_meta($this->post_id, 'repeatable_fields', $download_links);
		$success2 = update_post_meta($this->post_id, 'group_downloads_links', $download_links);

		// Logging hasil
		if ($success1 === false || $success2 === false) {
			error_log("[List_Download_Apkmody] Gagal update meta download post {$this->post_id} " .
					  "(repeatable: " . ($success1 ? 'OK' : 'FAILED') . ", group: " . ($success2 ? 'OK' : 'FAILED') . ")");
		} else {
			error_log("[List_Download_Apkmody] Berhasil simpan " . count($download_links) . " link");
		}

		return true;
	}

	/**
	 * Memproses dan menyimpan daftar link download dari sumber LiteAPKs
	 * (link biasanya dalam format base64)
	 *
	 * @return bool True jika berhasil menyimpan minimal 1 link valid
	 */
	public function List_Download_Liteapks(): bool
	{
		if (empty($this->data['apklink']) || !is_array($this->data['apklink'])) {
			return false;
		}

		$download_links = [];

		foreach ($this->data['apklink'] as $i => $encoded_link) {
			$encoded = trim($encoded_link ?? '');
			if (empty($encoded)) {
				continue;
			}

			$decoded = base64_decode($encoded, true);
			if ($decoded === false) {
				error_log("[LiteAPKs] Base64 decode failed for index {$i} on post {$this->post_id}");
				continue;
			}

			$original_url = esc_url_raw($decoded);
			if (!filter_var($original_url, FILTER_VALIDATE_URL)) {
				continue;
			}

			$final_url = $original_url;

			// Upload jika diaktifkan
			if (!empty($this->options['import_apk'])) {
				$attach_id = $this->upload_remote_file_apk($original_url, $this->post_id);
				if (!is_wp_error($attach_id)) {
					$final_url = wp_get_attachment_url($attach_id);
					error_log("APK uploaded [LiteAPKs] [post:{$this->post_id}] → {$final_url}");
				} else {
					error_log("APK upload failed [LiteAPKs] [post:{$this->post_id}]: " . $attach_id->get_error_message());
				}
			}

			// Shorten link
			$short_url = $this->shorten_links($final_url) ?: $final_url;

			// Ukuran (priority chain)
			$size = trim(wp_strip_all_tags(
				$this->data['size_downloadlinks_gma'][$i] ??
				$this->data['sizes_GP'] ??
				$this->data['size_sc'] ??
				''
			));

			// Nama link (prioritas apkname, fallback default)
			$display_name = trim($this->data['apkname'][$i] ?? '');
			$display_name = $display_name ?: 'Download APK ' . ($i + 1);

			$download_links[] = [
				'name'   => sanitize_text_field($display_name),
				'title'  => sanitize_text_field($display_name),
				'url'    => $short_url,
				'links'  => $short_url,
				'size'   => $size,
				'sizes1' => $size,
				'sizes'  => $size,
				'tipes'  => 'APK',
				'notes'  => sanitize_text_field($this->data['notes_downloadlinks_gma'][$i] ?? ''),
			];
		}

		if (empty($download_links)) {
			return false;
		}

		// Simpan ke kedua key
		$success1 = update_post_meta($this->post_id, 'repeatable_fields', $download_links);
		$success2 = update_post_meta($this->post_id, 'group_downloads_links', $download_links);

		// Logging
		if ($success1 === false || $success2 === false) {
			error_log("[List_Download_Liteapks] Gagal update meta post {$this->post_id}");
		} else {
			error_log("[List_Download_Liteapks] Berhasil simpan " . count($download_links) . " link");
		}

		return true;
	}

	/**
	 * Fungsi baru: Handle kasus aplikasi sudah ada (already exists)
	 * Dipanggil otomatis oleh process_title_and_post() jika duplikat ditemukan
	 *
	 * @return array [ 'post_id' => int, 'edit_link' => string, 'message' => string ]
	 *               atau null jika bukan duplikat
	 */
	public function handle_already_exists(): ?array
	{
		// Cek duplikat dengan prioritas tertinggi
		$duplicate = $this->check_for_duplicate();

		if (!$duplicate) {
			return null; // bukan duplikat → lanjut proses normal
		}

		$post_id    = $duplicate['post_id'];
		$reason     = $duplicate['reason'];
		$edit_link  = admin_url("post.php?post={$post_id}&action=edit");

		// Optional: Update status post menjadi publish jika sebelumnya draft
		if ($this->options['apk_status'] === 'publish') {
			$current_status = get_post_status($post_id);
			if ($current_status === 'draft') {
				wp_update_post([
					'ID'          => $post_id,
					'post_status' => 'publish'
				]);
				error_log("Post ID {$post_id} diubah dari draft menjadi publish karena setting auto-publish aktif");
			}
		}

		// Logging detail
		error_log("Duplicate found: Post ID {$post_id} - Reason: {$reason} - Edit: {$edit_link}");

		// Return data untuk AJAX response
		return [
			'post_id'   => $post_id,
			'edit_link' => $edit_link,
			'message'   => 'This app already exists! (Reason: ' . $reason . ')',
		];
	}

	// buat tambah function baru
		
		
}