<?php
/**
 * Handlers for Languages:
 *  - Save active/default (awxct_handle_languages_save)
 *  - Add new language    (awxct_handle_language_add)
 *  - Delete language     (awxct_handle_language_delete) [optional]
 *
 * Notes for PluginCheck:
 * - We do NOT use %i (identifier placeholder) to stay compatible with WP < 6.2.
 * - Table identifiers are strictly validated (alnum + underscore) and then interpolated.
 * - Value placeholders ALWAYS use $wpdb->prepare() (inline, no SQL temp vars passed to get_*).
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* -------------------------------------------------------
 * Helpers (load gently so this file can run standalone)
 * ------------------------------------------------------- */

/**
 * Cache helper: stable group + blog-aware key.
 *
 * @param string $suffix Key suffix.
 * @return string
 */
function awxct_cache_key( $suffix ) {
	$blog_id = function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 0;
	return 'awxct_' . $blog_id . '_' . (string) $suffix;
}

/**
 * Invalidate language-related caches.
 *
 * @return void
 */
function awxct_invalidate_languages_cache() {
	wp_cache_delete( awxct_cache_key( 'default_lang_code' ), 'awxct' );

	// "ids->codes" keys are dynamic; bump a simple version token.
	$ver = (int) wp_cache_get( awxct_cache_key( 'langs_ver' ), 'awxct' );
	if ( $ver < 0 ) {
		$ver = 0;
	}
	wp_cache_set( awxct_cache_key( 'langs_ver' ), $ver + 1, 'awxct', HOUR_IN_SECONDS );
}

/**
 * Return the plugin languages table (validated identifier).
 *
 * @return string
 */
function awxct_languages_table_name() {
	global $wpdb;

	$table = (string) ( $wpdb->prefix . 'awxct_languages' );

	// Hard safety: table identifiers are always alnum/underscore in WP.
	if ( ! preg_match( '/^[A-Za-z0-9_]+$/', $table ) ) {
		return '';
	}

	return $table;
}

/**
 * Cached getter helper.
 *
 * @param string   $cache_suffix Cache key suffix.
 * @param int      $ttl          Cache TTL.
 * @param callable $callback     Computes the value when not cached.
 * @return mixed
 */
function awxct_cached( $cache_suffix, $ttl, $callback ) {
	$key = awxct_cache_key( 'c_' . (string) $cache_suffix );
	$val = wp_cache_get( $key, 'awxct' );

	if ( false !== $val ) {
		return $val;
	}

	$val = is_callable( $callback ) ? call_user_func( $callback ) : null;

	wp_cache_set( $key, $val, 'awxct', (int) $ttl );
	return $val;
}

/**
 * Determine default code from DB (cached).
 *
 * @return string|null
 */
function awxct_db_default_lang_code_for_handler() {
	$ck = awxct_cache_key( 'default_lang_code' );
	$cv = wp_cache_get( $ck, 'awxct' );
	if ( false !== $cv ) {
		return $cv ? (string) $cv : null;
	}

	global $wpdb;

	$table = awxct_languages_table_name();
	if ( '' === $table ) {
		return null;
	}

	$code = awxct_cached(
		'default_code',
		HOUR_IN_SECONDS,
		static function () use ( $wpdb, $table ) {
			// Identifier is validated; no placeholder exists for identifiers.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			return $wpdb->get_var( "SELECT LOWER(code) FROM `" . esc_sql( $table ) . "` WHERE COALESCE(is_default,0)=1 LIMIT 1" );
		}
	);

	$code = $code ? strtolower( (string) $code ) : null;

	wp_cache_set( $ck, $code ? $code : '', 'awxct', HOUR_IN_SECONDS );

	return $code;
}

/**
 * IDs -> Codes map (cached).
 * Uses fixed-size IN() prepares (10er chunks) with LITERAL placeholders,
 * so PluginCheck does not flag "UnfinishedPrepare".
 *
 * @param array $ids Language IDs.
 * @return array<int,string>
 */
function awxct_map_ids_to_codes_for_handler( $ids ) {
	if ( empty( $ids ) || ! is_array( $ids ) ) {
		return array();
	}

	global $wpdb;

	$table = awxct_languages_table_name();
	if ( '' === $table ) {
		return array();
	}

	$ids = array_values( array_unique( array_map( 'absint', $ids ) ) );
	$ids = array_values(
		array_filter(
			$ids,
			static function ( $v ) {
				return $v > 0;
			}
		)
	);

	if ( empty( $ids ) ) {
		return array();
	}

	$ver = (int) wp_cache_get( awxct_cache_key( 'langs_ver' ), 'awxct' );
	if ( $ver < 0 ) {
		$ver = 0;
	}

	$ck = awxct_cache_key( 'id2code_' . $ver . '_' . md5( wp_json_encode( $ids ) ) );

	$cached = wp_cache_get( $ck, 'awxct' );
	if ( false !== $cached && is_array( $cached ) ) {
		return $cached;
	}

	$map = array();

	// Chunk IDs into groups of 10; pad with zeros so placeholders always match.
	$chunks = array_chunk( $ids, 10 );
	foreach ( $chunks as $chunk ) {
		$chunk = array_values( array_map( 'absint', (array) $chunk ) );
		while ( count( $chunk ) < 10 ) {
			$chunk[] = 0;
		}

		// Table identifiers cannot be placeholders. Use esc_sql() inline for PluginCheck.

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$rows = (array) $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT id, LOWER(code) AS code FROM `" . esc_sql( $table ) . "` WHERE id IN (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
				$chunk[0],
				$chunk[1],
				$chunk[2],
				$chunk[3],
				$chunk[4],
				$chunk[5],
				$chunk[6],
				$chunk[7],
				$chunk[8],
				$chunk[9]
			)
		);

		foreach ( $rows as $r ) {
			if ( isset( $r->id, $r->code ) ) {
				$rid = (int) $r->id;
				if ( $rid > 0 ) {
					$map[ $rid ] = (string) $r->code;
				}
			}
		}
	}

	wp_cache_set( $ck, $map, 'awxct', HOUR_IN_SECONDS );

	return $map;
}

/**
 * Redirect back to the Languages page.
 *
 * @param array $args Query args.
 * @return void
 */
function awxct_languages_redirect( array $args = array() ) {
	$base_args = array( 'page' => 'anywarex-clone-translate-languages' );
	$url       = add_query_arg( array_merge( $base_args, $args ), admin_url( 'admin.php' ) );

	wp_safe_redirect( $url );
	exit;
}

/**
 * Debug logger (only if DEBUG is true).
 *
 * @param string $msg Message.
 * @return void
 */
function awxct_log( $msg ) {
	if ( defined( 'AWXCT_DEBUG' ) && AWXCT_DEBUG ) {
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
		error_log( '[AWXCT] ' . (string) $msg );
	}
}

/* -------------------------------------------------------
 * SAVE: Active + Default
 * POST:
 *  - awxct_lang_active[<id>] = "1"
 *  - awxct_lang_default = <id>
 * ------------------------------------------------------- */
function awxct_handle_languages_save() {
	awxct_log( 'awxct_handle_languages_save() fired' );

	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Forbidden', 'anywarex-clone-translate' ), 403 );
	}
	check_admin_referer( 'awxct_languages_save', 'awxct_nonce' );

	global $wpdb;

	$table = awxct_languages_table_name();
	if ( '' === $table ) {
		awxct_languages_redirect( array( 'err' => 'table_invalid' ) );
	}

	// IMPORTANT for PluginCheck: this field is an array of checkbox values, so we sanitize IDs from its keys.
	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above via check_admin_referer().
	// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via absint() on array keys.
	$post_active = isset( $_POST['awxct_lang_active'] ) ? wp_unslash( $_POST['awxct_lang_active'] ) : array();

	$active_ids = array();
	if ( is_array( $post_active ) ) {
		$active_ids = array_map( 'absint', array_keys( $post_active ) );
		$active_ids = array_values( array_filter( $active_ids ) );
	}

	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above via check_admin_referer().
	// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via absint().
	$default_id_raw = isset( $_POST['awxct_lang_default'] ) ? wp_unslash( $_POST['awxct_lang_default'] ) : '';
	$default_id     = absint( is_string( $default_id_raw ) ? $default_id_raw : '' );

	awxct_log( 'POST active_ids=' . wp_json_encode( $active_ids ) . ' default_id=' . (string) $default_id );

	// Default must be active (do NOT auto-activate silently).
	if ( $default_id && ! in_array( $default_id, $active_ids, true ) ) {
		awxct_languages_redirect( array( 'err' => 'default_must_be_active' ) );
	}

	/* ---------- Persist (robust; never lose the default) ---------- */

	$prev_default_id = (int) awxct_cached(
		'prev_default_id',
		30,
		static function () use ( $wpdb, $table ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		return (int) $wpdb->get_var( "SELECT id FROM `" . esc_sql( $table ) . "` WHERE COALESCE(is_default,0)=1 LIMIT 1" );
		}
	);

	// Reset everything (WRITE -> ignore NoCaching).
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	$wpdb->query( "UPDATE `" . esc_sql( $table ) . "` SET active = 0, is_default = 0" );

	awxct_log( 'reset active/is_default' );

	// Set active.
	if ( ! empty( $active_ids ) ) {
		foreach ( $active_ids as $lang_id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				$table,
				array( 'active' => 1 ),
				array( 'id' => (int) $lang_id ),
				array( '%d' ),
				array( '%d' )
			);
		}
		awxct_log( 'set active for ids=' . wp_json_encode( $active_ids ) );
	}

	// Set default.
	if ( $default_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->update(
			$table,
			array( 'is_default' => 1 ),
			array( 'id' => $default_id ),
			array( '%d' ),
			array( '%d' )
		);
		awxct_log( 'set default id=' . (string) $default_id );
	} else {
		if ( $prev_default_id > 0 ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				$table,
				array(
					'is_default' => 1,
					'active'     => 1,
				),
				array( 'id' => $prev_default_id ),
				array( '%d', '%d' ),
				array( '%d' )
			);
			awxct_log( 'restored previous default id=' . (string) $prev_default_id );
		} elseif ( ! empty( $active_ids ) ) {
			$fallback_default = (int) reset( $active_ids );
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				$table,
				array( 'is_default' => 1 ),
				array( 'id' => $fallback_default ),
				array( '%d' ),
				array( '%d' )
			);
			awxct_log( 'set fallback default id=' . (string) $fallback_default );
		} else {
			$fallback_default = (int) awxct_cached(
				'fallback_default_id',
				30,
				static function () use ( $wpdb, $table ) {
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					return (int) $wpdb->get_var( "SELECT id FROM `" . esc_sql( $table ) . "` ORDER BY code ASC LIMIT 1" );
				}
			);

			if ( $fallback_default > 0 ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->update(
					$table,
					array(
						'is_default' => 1,
						'active'     => 1,
					),
					array( 'id' => $fallback_default ),
					array( '%d', '%d' ),
					array( '%d' )
				);
				awxct_log( 'set emergency default id=' . (string) $fallback_default );
			}
		}
	}

	awxct_invalidate_languages_cache();
	awxct_languages_redirect( array( 'updated' => '1', 'op' => 'update' ) );
}

/* -------------------------------------------------------
 * ADD: new language
 * ------------------------------------------------------- */
function awxct_handle_language_add() {
	awxct_log( 'awxct_handle_language_add() fired' );

	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Forbidden', 'anywarex-clone-translate' ), 403 );
	}
	check_admin_referer( 'awxct_language_add', 'awxct_nonce' );

	global $wpdb;

	$table = awxct_languages_table_name();
	if ( '' === $table ) {
		awxct_languages_redirect( array( 'err' => 'table_invalid' ) );
	}

	$code_raw     = isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : '';
	$english_name = isset( $_POST['english_name'] ) ? sanitize_text_field( wp_unslash( $_POST['english_name'] ) ) : '';
	$native_name  = isset( $_POST['native_name'] ) ? sanitize_text_field( wp_unslash( $_POST['native_name'] ) ) : '';

	$active     = isset( $_POST['active'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['active'] ) ) ? 1 : 0;
	$is_default = isset( $_POST['is_default'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['is_default'] ) ) ? 1 : 0;

	// Normalize code for storage: always lowercase with '-' (e.g. en-us).
	if ( function_exists( 'awxct_normalize_lang_code' ) ) {
		$code = awxct_normalize_lang_code( $code_raw );
	} else {
		$code = strtolower( str_replace( '_', '-', trim( $code_raw ) ) );
	}

	if ( '' === $code ) {
		awxct_languages_redirect( array( 'err' => 'empty_code' ) );
	}

	// Prevent duplicates.
	$dup = (int) awxct_cached(
		'dup_' . md5( $code ),
		30,
		static function () use ( $wpdb, $table, $code ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					"SELECT COUNT(*) FROM `" . esc_sql( $table ) . "` WHERE code=%s",
					$code
				)
			);
		}
	);

	if ( $dup > 0 ) {
		awxct_languages_redirect(
			array(
				'err'  => 'duplicate',
				'code' => rawurlencode( $code ),
			)
		);
	}

	/* ---------- Auto-fill names from includes/languages-data.php ---------- */
	if ( '' === $english_name || '' === $native_name ) {
		$file = defined( 'AWXCT_PATH' )
			? AWXCT_PATH . 'includes/languages-data.php'
			: dirname( __DIR__ ) . '/includes/languages-data.php';

		if ( file_exists( $file ) ) {
			$data = include $file;
			if ( is_array( $data ) ) {
				$lookup_keys   = array();
				$lookup_keys[] = $code;
				$lookup_keys[] = strtoupper( $code );
				$lookup_keys[] = strtolower( $code );

				$dash_pos = strpos( $code, '-' );
				if ( false !== $dash_pos ) {
					$lookup_keys[] = substr( $code, 0, $dash_pos );
				}

				$entry = null;
				foreach ( $lookup_keys as $k ) {
					if ( isset( $data[ $k ] ) ) {
						$entry = $data[ $k ];
						break;
					}
				}

				if ( null !== $entry ) {
					$native_from  = '';
					$english_from = '';

					if ( is_string( $entry ) ) {
						$native_from  = $entry;
						$english_from = $entry;
					} elseif ( is_array( $entry ) ) {
						$native_from  = $entry['native'] ?? ( $entry['native_name'] ?? '' );
						$english_from = $entry['english'] ?? ( $entry['english_name'] ?? '' );
					}

					if ( '' === $native_name && '' !== $native_from ) {
						$native_name = (string) $native_from;
					}
					if ( '' === $english_name && '' !== $english_from ) {
						$english_name = (string) $english_from;
					}
				}
			}
		}
	}

	/* ---------- Fallbacks ---------- */
	if ( '' === $english_name && '' !== $native_name ) {
		$english_name = $native_name;
	}
	if ( '' === $native_name && '' !== $english_name ) {
		$native_name = $english_name;
	}
	if ( '' === $english_name && '' === $native_name ) {
		$english_name = strtoupper( $code );
		$native_name  = strtoupper( $code );
	}

	// If default -> reset all other defaults.
	if ( $is_default ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$wpdb->query( "UPDATE `" . esc_sql( $table ) . "` SET is_default = 0" );
	}

	// Insert.
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	$wpdb->insert(
		$table,
		array(
			'code'         => $code,
			'english_name' => $english_name,
			'native_name'  => $native_name,
			'active'       => (int) $active,
			'is_default'   => (int) $is_default,
		),
		array( '%s', '%s', '%s', '%d', '%d' )
	);

	awxct_invalidate_languages_cache();
	awxct_log( 'inserted code=' . (string) $code . ' active=' . (string) $active . ' default=' . (string) $is_default );

	awxct_languages_redirect( array( 'updated' => '1', 'op' => 'insert' ) );
}

/* -------------------------------------------------------
 * DELETE (optional – currently not used by the view)
 * ------------------------------------------------------- */
function awxct_handle_language_delete() {
	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Forbidden', 'anywarex-clone-translate' ), 403 );
	}
	check_admin_referer( 'awxct_language_delete', 'awxct_nonce' );

	// phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above via check_admin_referer().
	// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via absint().
	$id_raw = isset( $_POST['id'] ) ? wp_unslash( $_POST['id'] ) : '';
	if ( '' === $id_raw ) {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- fallback for legacy delete links; request is nonce-protected above.
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via absint().
		$id_raw = isset( $_GET['id'] ) ? wp_unslash( $_GET['id'] ) : '';
	}
	$id = absint( is_string( $id_raw ) ? $id_raw : '' );

	if ( $id <= 0 ) {
		awxct_languages_redirect( array( 'err' => 'bad_id' ) );
	}

	global $wpdb;

	$table = awxct_languages_table_name();
	if ( '' === $table ) {
		awxct_languages_redirect( array( 'err' => 'table_invalid' ) );
	}

	// Do not delete the default.
	$is_default = (int) awxct_cached(
		'is_default_' . (string) $id,
		30,
		static function () use ( $wpdb, $table, $id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					"SELECT COALESCE(is_default,0) FROM `" . esc_sql( $table ) . "` WHERE id=%d",
					$id
				)
			);
		}
	);

	if ( 1 === $is_default ) {
		awxct_languages_redirect( array( 'err' => 'cannot_delete_default' ) );
	}

	// Do not delete active languages.
	$is_active = (int) awxct_cached(
		'is_active_' . (string) $id,
		30,
		static function () use ( $wpdb, $table, $id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					"SELECT COALESCE(active,0) FROM `" . esc_sql( $table ) . "` WHERE id=%d",
					$id
				)
			);
		}
	);

	if ( 1 === $is_active ) {
		awxct_languages_redirect( array( 'err' => 'cannot_delete_active' ) );
	}

	// Delete.
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	$wpdb->delete( $table, array( 'id' => $id ), array( '%d' ) );

	awxct_invalidate_languages_cache();
	awxct_languages_redirect( array( 'updated' => '1', 'op' => 'delete' ) );
}

/**
 * Register admin-post handlers (keeps the file standalone-friendly).
 */
add_action( 'admin_post_awxct_languages_save', 'awxct_handle_languages_save' );
add_action( 'admin_post_awxct_language_add', 'awxct_handle_language_add' );
add_action( 'admin_post_awxct_language_delete', 'awxct_handle_language_delete' );
