<?php
/*-----------------------------------------------*/
/*  EXTHEM.ES
/*  PREMIUM WORDRESS THEMES
/*
/*  STOP DON'T TRY EDIT
/*  IF YOU DON'T KNOW PHP 
/*
/*  As Errors In Your Themes
/*  Are Not The Responsibility
/*  Of The DEVELOPERS
/*  @EXTHEM.ES
/*  
/*  Follow Social Media Exthem.es
/*  Youtube : https://www.youtube.com/channel/UCpcZNXk6ySLtwRSBN6fVyLA
/*  Facebook : https://www.facebook.com/groups/exthem.es
/*  Twitter : https://x.com/bangreyblog
/*  Instagram : https://www.instagram.com/exthem.es/
/*  Telegram : https://t.me/exthemes_helps
/*  TikTok : https://www.tiktok.com/@exthemes
/*	More Premium Themes Visit Now On https://exthem.es/
/*-----------------------------------------------*/
if ( ! defined( 'ABSPATH' ) ) exit; 
// Silence is golden.

if ( ! function_exists( 'app_update_debug_notice_animation' ) ) 
{
	function app_update_debug_notice_animation() 
	{
		global $opt_themes;
		
		// Hanya jalankan jika debug diaktifkan
			
		// 3. Cek apakah fitur notice di-app-list diaktifkan
		$show_notice_on_list = get_option('app_update_show_notice_on_app_list', '1') === '1';

		// Jika notice dimatikan → langsung keluar (tidak perlu lanjut proses)
		if (!$show_notice_on_list) {
			return;
		}
		
		//if ( empty( $opt_themes['show_debug'] ) ) return;

		?>

<style>
@keyframes slideInFromRight {from {transform: translateX(100%);opacity: 0;}to {transform: translateX(0);opacity: 1;}}@keyframes slideOutToRight {from {transform: translateX(0);opacity: 1;}to {transform: translateX(100%);opacity: 0;}}.notice.app-update-debug-notice {animation: slideInFromRight 0.6s ease-out forwards;overflow: hidden;margin-bottom: 10px !important;transition: all 0.3s ease;}.notice.app-update-debug-notice.closing {animation: slideOutToRight 0.8s ease-in forwards;}.notice.app-update-debug-notice:hover {animation-play-state: paused;}.app-update-debug-notice {font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", Consolas, "Courier New", monospace;font-size: 13px;line-height: 1.45;color: #c9d1d9;background: #161b22;border: 1px solid #30363d;border-radius: 6px;padding: 12px 16px;margin: 1em 0;white-space: pre-wrap;word-break: break-word;box-shadow: 0 2px 8px rgba(0,0,0,0.35);}.app-update-debug-notice .skip {color: #f85149;font-weight: 600;}.app-update-debug-notice .title {color: #d2a8ff;}


.wrap #apps-updates-form {
  /* font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", Consolas, "Courier New", monospace;   */
  
}

</style>

		<!--
		<script>
			document.addEventListener('DOMContentLoaded', () => {
				const notices = document.querySelectorAll('.notice.app-update-debug-notice');
				
				notices.forEach((notice, i) => {
					// Staggered entry animation
					notice.style.animationDelay = (i * 0.5) + 's';

					let timer = null;

					const scheduleClose = () => {
						clearTimeout(timer);
						timer = setTimeout(() => closeNotice(notice), 30000); // 35 detik auto-close
					};

					scheduleClose();

					// Hover: pause animasi & timer
					notice.addEventListener('mouseenter', () => {
						clearTimeout(timer);
						notice.style.animationPlayState = 'paused';
					});

					// Mouse leave: lanjutkan animasi & beri tambahan 4 detik
					notice.addEventListener('mouseleave', () => {
						notice.style.animationPlayState = 'running';
						timer = setTimeout(() => closeNotice(notice), 4000);
					});

					// Tombol close manual (jika ada)
					const btn = notice.querySelector('button.notice-dismiss');
					if (btn) {
						btn.addEventListener('click', e => {
							e.preventDefault();
							clearTimeout(timer);
							closeNotice(notice);
						});
					}
				});

				// Fungsi tutup dengan animasi slideOutToRight
				function closeNotice(notice) {
					if (!notice || notice.classList.contains('closing')) return;
					
					notice.classList.add('closing');
					
					const onAnimationEnd = () => {
						notice.parentNode?.removeChild(notice);
						notice.removeEventListener('animationend', onAnimationEnd);
					};
					
					notice.addEventListener('animationend', onAnimationEnd);
				}
			});
		</script>
		-->
		
		<?php
	}
	add_action('admin_footer', 'app_update_debug_notice_animation');
}

/* --------------------------------------------------------------------
 * Helpers
 * ------------------------------------------------------------------ */
if ( ! function_exists( 'normalize_version' ) ) 
{
	function normalize_version( $version ) 
	{
		if ( empty( $version ) ) {
			return '';
		}
		// Keep only alphanumeric, dots, and allow common suffixes like (build)
		// But strip everything after space or parenthesis for reliable comparison
		$version = preg_replace( '/\s*\(.*\)|[\+\-].*$/', '', (string) $version );
		// Remove all non-alphanumeric/dot characters
		return preg_replace( '/[^A-Za-z0-9.]/', '', $version );
		return preg_replace( '/[^A-Za-z0-9.]/', '', (string) $version );
	}
}

if ( ! function_exists( 'clear_app_update_flags' ) ) 
{
	function clear_app_update_flags( $post_id ) 
	{
		delete_post_meta( $post_id, 'available_updated' );
		delete_post_meta( $post_id, 'available_version' );
		delete_post_meta( $post_id, 'available_whatsnew' );
		delete_post_meta( $post_id, 'available_update_version' );
		delete_post_meta( $post_id, 'app_last_update_found' );
	}
}

/**
 * Clear update flags when admin manually saves post
*/
if ( ! function_exists( 'app_update_clear_flag' ) ) 
{
	add_action( 'save_post', 'app_update_clear_flag', 10, 3 );
	function app_update_clear_flag( $post_id, $post, $update ) 
	{
		
		/* --------------------------------------------------------------------
		 * Safety checks
		 * ------------------------------------------------------------------ 
		*/	
		if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
			return;
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		/* --------------------------------------------------------------------
		 * Only proceed if this is a real save from edit screen (not cron, import, etc.)
		 * ------------------------------------------------------------------ 
		*/	
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		// Optional: only for your specific post type if needed
		// if ( $post->post_type !== 'post' ) return;

		/* --------------------------------------------------------------------
		 * Check if the version field was submitted (even if empty — means admin saved it)
		 * ------------------------------------------------------------------ 
		*/	 
		if ( isset( $_POST['wp_version_GP'] ) || isset( $_POST['_wpnonce'] ) ) {

			/* --------------------------------------------------------------------
			 * Clear update flags — admin has acknowledged/updated the version
			 * ------------------------------------------------------------------ 
			*/	
			 
			delete_post_meta( $post_id, 'available_update_version' );
			delete_post_meta( $post_id, 'available_updated' );
			delete_post_meta( $post_id, 'available_version' );
			delete_post_meta( $post_id, 'available_whatsnew' );
			delete_post_meta( $post_id, 'app_last_update_found' ); // optional

			/* --------------------------------------------------------------------
			 * Optional: add a log that admin cleared it app_update_detected
			 * ------------------------------------------------------------------ 
			*/	
	 
			add_post_meta( $post_id, 'app_update_cleared_by_admin', current_time('mysql'), true );
		}
	}
}

/**
 * Jalankan pengecekan update aplikasi dalam batch kecil
 * Dipanggil oleh cron atau manual trigger
 *
 * @return void
 */
if ( ! function_exists( 'app_update_run_batch_check' ) ) 
{
	function app_update_run_batch_check() 
	{
		// 1. Safety lock - cegah proses ganda
		$lock_key = 'app_update_global_cron_lock';
		if (get_transient($lock_key)) {
			error_log('[App Update] Batch check skipped: another process is already running.');
			return;
		}

		// Lock selama maksimal 30 menit
		set_transient($lock_key, true, 30 * MINUTE_IN_SECONDS);

		try {
			// 2. Ambil pengaturan
			$batch_size = max(1, (int) get_option('app_update_batch_size', 20));

			// 3. Tentukan interval minimum pengecekan ulang (dalam detik)
			$interval_map = [
				'every_1_hours'  => HOUR_IN_SECONDS,
				'every_3_hours'  => 3 * HOUR_IN_SECONDS,
				'every_6_hours'  => 6 * HOUR_IN_SECONDS,
				'every_12_hours' => 12 * HOUR_IN_SECONDS,
				'every_24_hours' => DAY_IN_SECONDS,
			];

			$current_interval = get_option('app_update_cron_interval', 'every_6_hours');
			$min_interval_sec = $interval_map[$current_interval] ?? (6 * HOUR_IN_SECONDS);

			// Batas waktu terakhir pengecekan yang masih valid
			$cutoff_time = time() - $min_interval_sec;

			// 4. Query post yang perlu dicek
			$args = [
				'post_type'      => 'post',
				'post_status'    => 'publish',
				'posts_per_page' => $batch_size,
				'fields'         => 'ids',
				'meta_query'     => [
					'relation' => 'AND',
					[
						'key'     => 'wp_GP_ID',
						'compare' => 'EXISTS',
					],
					[
						'relation' => 'OR',
						[
							'key'     => '_time_scrape_app_update',
							'value'   => $cutoff_time,
							'compare' => '<',
							'type'    => 'NUMERIC',
						],
						[
							'key'     => '_time_scrape_app_update',
							'compare' => 'NOT EXISTS',
						],
					],
				],
				'orderby'        => 'meta_value_num',
				'meta_key'       => '_time_scrape_app_update',
				'order'          => 'ASC', // yang paling lama dulu
			];

			$query = new WP_Query($args);
			$processed_ids = [];
			$checked_count = 0;
			$start_time    = time();
			$max_execution = 120; // detik maksimum per batch

			if ($query->have_posts()) {
				foreach ($query->posts as $post_id) {
					// Cegah timeout
					if ((time() - $start_time) >= $max_execution) {
						break;
					}

					if (function_exists('app_update_available')) {
						// Parameter false = jangan tampilkan notice per app di sini
						app_update_available($post_id, false);
						$processed_ids[] = $post_id;
						$checked_count++;
					} else {
						error_log('[App Update] Function app_update_available() not found!');
						break; // stop jika fungsi inti hilang
					}
				}
			}

			// 5. Logging hasil batch
			$log_message = sprintf(
				'Batch check completed: %d apps processed (batch size: %d). IDs: %s',
				$checked_count,
				$batch_size,
				implode(', ', $processed_ids) ?: 'none'
			);

			error_log('[App Update] ' . $log_message);

			// Simpan log ke option (maks 20 entri terbaru)
			$log_entry = [
				'time'    => current_time('mysql'),
				'message' => $log_message,
			];

			$existing_log = (array) get_option('app_update_cron_last_log', []);
			array_unshift($existing_log, $log_entry);
			update_option('app_update_cron_last_log', array_slice($existing_log, 0, 20));

			// 6. Jika batch penuh → kemungkinan masih banyak yang menunggu → jadwalkan lagi dalam 5 menit
			if ($checked_count === $batch_size) {
				wp_schedule_single_event(time() + 300, 'check_all_apps_cron');
			}

		} catch (Exception $e) {
			error_log('[App Update] Batch check error: ' . $e->getMessage());
		}

		// 7. Lepas lock setelah selesai
		delete_transient($lock_key);
	}
}

/**
 * Handle manual force check for all apps
 * Dipanggil ketika user klik tombol "Check All Apps Now"
 */
if ( ! function_exists( 'app_update_handle_force_check' ) ) 
{
	add_action('admin_init', 'app_update_handle_force_check');
	function app_update_handle_force_check() 
	{
		// Hanya proses jika ada parameter yang tepat
		if (!isset($_GET['app_update_force_check']) || $_GET['app_update_force_check'] !== '1') {
			return;
		}

		// Verifikasi nonce dan hak akses
		if (!check_admin_referer('app_update_force_check_all')) {
			wp_die(esc_html__('Security check failed. Please try again.', TEXT_DOMAIN));
		}

		if (!current_user_can('manage_options')) {
			wp_die(esc_html__('You do not have permission to perform this action.', TEXT_DOMAIN));
		}

		// Opsional: Cek apakah sedang ada proses berjalan
		if (get_transient('app_update_global_cron_lock')) {
			add_settings_error(
				'app_update_force_check',
				'already_running',
				esc_html__('A background check is already running. Please wait a moment.', TEXT_DOMAIN),
				'warning'
			);
			return;
		}

		// Jalankan proses pengecekan
		if (function_exists('app_update_run_batch_check')) {
			// Jika menggunakan synchronous (langsung jalan)
			app_update_run_batch_check();

			// Atau jika menggunakan WP Background Processing (lebih direkomendasikan)
			// $process = new App_Update_Background_Process();
			// $process->push_to_queue_all_checkable(); // custom method untuk queue semua
			// $process->save()->dispatch();

			$message = esc_html__('Full background check has been successfully triggered!', TEXT_DOMAIN);
			$type    = 'success';
		} else {
			$message = esc_html__('Error: Check function not found.', TEXT_DOMAIN);
			$type    = 'error';
		}

		// Simpan notice untuk ditampilkan setelah redirect
		add_settings_error(
			'app_update_force_check',
			'force_check_triggered',
			$message,
			$type
		);

		// Redirect kembali ke halaman Cron Status atau Apps Updates
		// Ganti 'app-update-cron-status' dengan slug halaman yang sesuai 
		$redirect_url = admin_url('admin.php?page=app-update-list');
		wp_safe_redirect(add_query_arg('settings-updated', 'true', $redirect_url));
		exit;
	}
}

/**
 * Hitung jumlah app yang butuh update.
 * 
 * @param string $search Optional search term untuk filter berdasarkan title atau package ID.
 * @return int Jumlah app yang butuh update.
*/
if ( ! function_exists( 'apk_update_count_posts_search' ) ) 
{
	function apk_update_count_posts_search( $search = '' ) 
	{
		global $wpdb;

		$post_type = 'post'; // GANTI jika pakai CPT lain

		$where = "
			WHERE p.post_type = %s
			  AND p.post_status = 'publish'
			  AND pm_avail.meta_key = 'available_version'
			  AND pm_avail.meta_value != ''
			  AND pm_avail.meta_value > COALESCE(pm_curr.meta_value, '0.0.0')
			  AND EXISTS (
				  SELECT 1 FROM {$wpdb->postmeta} pm_flag
				  WHERE pm_flag.post_id = p.ID
					AND pm_flag.meta_key = 'available_updated'
					AND pm_flag.meta_value = 'yes'
			  )
		";

		$joins = "
			FROM {$wpdb->posts} p
			INNER JOIN {$wpdb->postmeta} pm_avail ON p.ID = pm_avail.post_id
			LEFT JOIN {$wpdb->postmeta} pm_curr ON p.ID = pm_curr.post_id AND pm_curr.meta_key = 'wp_version_GP'
		";

		$params = [ $post_type ];

		if ( ! empty( $search ) ) {
			$like = '%' . $wpdb->esc_like( $search ) . '%';
			$where .= " AND (
				p.post_title LIKE %s
				OR pm_title.meta_value LIKE %s
				OR EXISTS (
					SELECT 1 FROM {$wpdb->postmeta} pm_pkg
					WHERE pm_pkg.post_id = p.ID
					  AND pm_pkg.meta_key = 'wp_GP_ID'
					  AND pm_pkg.meta_value LIKE %s
				)
			)";
			$params[] = $like;
			$params[] = $like;
			$params[] = $like;

			$joins .= " LEFT JOIN {$wpdb->postmeta} pm_title ON p.ID = pm_title.post_id AND pm_title.meta_key = 'wp_title_GP' ";
		}

		$query = "SELECT COUNT(DISTINCT p.ID) $joins $where";

		return (int) $wpdb->get_var( $wpdb->prepare( $query, $params ) );
	}
}

if ( ! function_exists( 'apk_update_count_posts' ) ) 
{
	function apk_update_count_posts() 
	{
		global $wpdb;

		// This meta key is set only when an update is detected (see the function above)
		
		/* 
		$count2 = $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->postmeta}
			 WHERE meta_key = 'available_updated'
			 AND meta_value > 'yes'"
		);
		
		$count1 = $wpdb->get_var(
			"SELECT COUNT(*) FROM wp_postmeta WHERE (meta_key = 'available_updated' AND meta_value = 'yes')"
		);
		
		
		$count3 = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->postmeta} 
			 WHERE meta_key = 'available_updated' 
			 AND meta_value = 'yes'"
		);
		*/ 
		$post_type = 'post'; // ← CHANGE TO YOUR CUSTOM POST TYPE IF NEEDED (e.g. 'apk_app')

		global $wpdb;
		$post_type = 'post'; // ← CHANGE TO YOUR CUSTOM POST TYPE IF NEEDED (e.g. 'apk_app')

		// Total count of apps with updates
		$count = (int) $wpdb->get_var($wpdb->prepare("
			SELECT COUNT(p.ID)
			FROM $wpdb->posts p
			INNER JOIN $wpdb->postmeta pm_avail ON p.ID = pm_avail.post_id
				AND pm_avail.meta_key = 'available_version' AND pm_avail.meta_value != ''
			LEFT JOIN $wpdb->postmeta pm_curr ON p.ID = pm_curr.post_id
				AND pm_curr.meta_key = 'wp_version_GP'
			WHERE p.post_type = %s
			  AND p.post_status = 'publish'
			  AND pm_avail.meta_value > IFNULL(pm_curr.meta_value, '0.0.0')
			   AND EXISTS (
				 SELECT 1 FROM {$wpdb->postmeta} pm2 
				 WHERE pm2.post_id = p.ID 
				   AND pm2.meta_key = 'available_updated' 
				   AND pm2.meta_value = 'yes'
			   ) 
		", $post_type));


		// Total count of apps with updates
		$count2 = (int) $wpdb->get_var($wpdb->prepare("
			SELECT COUNT(p.ID)
			FROM $wpdb->posts p
			INNER JOIN $wpdb->postmeta pm_avail ON p.ID = pm_avail.post_id
				AND pm_avail.meta_key = 'available_version' AND pm_avail.meta_value != ''
			LEFT JOIN $wpdb->postmeta pm_curr ON p.ID = pm_curr.post_id
				AND pm_curr.meta_key = 'wp_version_GP'
			WHERE p.post_type = %s
			  AND p.post_status = 'publish'
			  AND pm_avail.meta_value > IFNULL(pm_curr.meta_value, '0.0.0')
		", $post_type));
		
		$count1 = (int) $wpdb->get_var(
			"SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
			 INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
			 WHERE p.post_type = 'post'
			   AND p.post_status = 'publish'
			   AND pm.meta_key = 'wp_GP_ID'
			   AND pm.meta_value != ''
			   AND EXISTS (
				 SELECT 1 FROM {$wpdb->postmeta} pm2 
				 WHERE pm2.post_id = p.ID 
				   AND pm2.meta_key = 'available_updated' 
				   AND pm2.meta_value = 'yes'
			   )"
		);
		
		//$wpdb->get_results("SELECT * FROM wp_postmeta WHERE (meta_key = 'available_updated' AND meta_value = 'yes')");
		
		return (int) $count;
	}
}

if ( ! function_exists( 'apk_get_update_posts_list' ) ) 
{
	function apk_get_update_posts_list( $limit = 100 ) 
	{
		global $wpdb;

		$results = $wpdb->get_results( $wpdb->prepare("
			SELECT 
				p.ID,
				p.post_title,
				COALESCE(pm_title.meta_value, p.post_title, 'Untitled App') AS display_title,
				pm_avail.meta_value AS available_version,
				pm_curr.meta_value AS current_version,
				pm_poster.meta_value AS poster_url
			FROM {$wpdb->posts} p
			INNER JOIN {$wpdb->postmeta} pm_flag
				ON p.ID = pm_flag.post_id
				AND pm_flag.meta_key = 'available_updated'
				AND pm_flag.meta_value = 'yes'
			LEFT JOIN {$wpdb->postmeta} pm_avail
				ON p.ID = pm_avail.post_id AND pm_avail.meta_key = 'available_version'
			LEFT JOIN {$wpdb->postmeta} pm_curr
				ON p.ID = pm_curr.post_id AND pm_curr.meta_key = 'wp_version_GP'
			LEFT JOIN {$wpdb->postmeta} pm_title
				ON p.ID = pm_title.post_id AND pm_title.meta_key = 'wp_title_GP'
			LEFT JOIN {$wpdb->postmeta} pm_poster
				ON p.ID = pm_poster.post_id AND pm_poster.meta_key = 'wp_poster_GP'
			WHERE p.post_type = %s
			  AND p.post_status = 'publish'
			  AND pm_avail.meta_value != ''
			  AND COALESCE(pm_avail.meta_value, '0.0.0') > COALESCE(pm_curr.meta_value, '0.0.0')
			ORDER BY pm_avail.meta_value DESC
			LIMIT %d
		", 'post', $limit ) );

		return $results;
	}
}

if ( ! function_exists( 'apk_update_filter_posts' ) ) 
{
	add_action( 'pre_get_posts', 'apk_update_filter_posts' );
	function apk_update_filter_posts( $query ) 
	{
		global $pagenow;

		if ( ! is_admin() || $pagenow !== 'edit.php' || ! $query->is_main_query() ) {
			return;
		}

		if ( isset( $_GET['apk_update_available'] ) && $_GET['apk_update_available'] == '1' ) {
			$meta_query = array(
				array(
					'key'		=> 'app_last_update_found',
					'value'   	=> 0,
					'compare' 	=> '>',
					'type'		=> 'NUMERIC'
				)
			);

			$query->set( 'meta_query', $meta_query );
		}
	}
}
