<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Normalizes language codes to a BCP-47-like format (ll or ll-CC).
 * Example: de → de-DE, EN_us → en-US
 */
if ( ! function_exists( 'awxct_normalize_code' ) ) {
	function awxct_normalize_code( string $code ): string {
		$code = trim( $code );
		if ( $code === '' ) {
			return '';
		}

		$code  = str_replace( '_', '-', $code );
		$code  = preg_replace( '/-+/', '-', $code );
		$parts = explode( '-', $code );
		$lang  = strtolower( $parts[0] ?? '' );
		$reg   = strtoupper( $parts[1] ?? '' );

		if ( ! preg_match( '/^[a-z]{2}$/', $lang ) ) {
			return '';
		}

		if ( $reg !== '' && ! preg_match( '/^[A-Z]{2}$/', $reg ) ) {
			$reg = '';
		}

		return $reg ? "{$lang}-{$reg}" : $lang;
	}
}

/** Returns the base language code (ll). */
if ( ! function_exists( 'awxct_lang_base' ) ) {
	function awxct_lang_base( string $code ): string {
		$n = awxct_normalize_code( $code );
		return strtolower( explode( '-', $n )[0] ?? '' );
	}
}

/**
 * Blog-aware cache key helper.
 *
 * @param string $suffix
 * @return string
 */
if ( ! function_exists( 'awxct_lang_cache_key' ) ) {
	function awxct_lang_cache_key( string $suffix ): string {
		$blog_id = function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 0;
		return 'awxct_lang_' . $blog_id . '_' . $suffix;
	}
}

/**
 * Validate a table identifier (only [A-Za-z0-9_]).
 *
 * @param string $table
 * @return string Valid identifier or empty string.
 */
if ( ! function_exists( 'awxct_validate_table_ident' ) ) {
	function awxct_validate_table_ident( string $table ): string {
		$table = (string) $table;
		if ( $table === '' ) {
			return '';
		}
		if ( ! preg_match( '/^[A-Za-z0-9_]+$/', $table ) ) {
			return '';
		}
		return $table;
	}
}

/**
 * Checks if a language code is supported (active/default).
 *
 * Reason for PHPCS disable:
 * - Table identifiers cannot be placeholders in $wpdb->prepare()
 * - Identifier is strictly validated (alnum/_), then esc_sql() is applied
 * - Values are passed via placeholders
 * - WP sniffer produces false positives for identifier interpolation
 */
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
if ( ! function_exists( 'awxct_is_supported_lang' ) ) {
	function awxct_is_supported_lang( string $code ): bool {
		$code = strtolower( str_replace( '_', '-', trim( $code ) ) );
		if ( $code === '' ) {
			return false;
		}

		static $cache = [];
		if ( isset( $cache[ $code ] ) ) {
			return (bool) $cache[ $code ];
		}

		// Preferred: helper, if present.
		if ( function_exists( 'awxct_available_locales' ) ) {
			$locs = awxct_available_locales();
			$norm = [];
			foreach ( $locs as $lc ) {
				$norm[] = strtolower( str_replace( '_', '-', trim( (string) $lc ) ) );
			}
			$norm           = array_values( array_unique( $norm ) );
			$cache[ $code ] = in_array( $code, $norm, true );
			return (bool) $cache[ $code ];
		}

		// Fallback: read directly from the language table.
		global $wpdb;

		$tbl_raw = defined( 'AWXCT_TABLE_LANGUAGES' )
			? (string) AWXCT_TABLE_LANGUAGES
			: (string) ( $wpdb->prefix . 'awxct_languages' );

		$tbl_valid = awxct_validate_table_ident( $tbl_raw );
		if ( $tbl_valid === '' ) {
			$cache[ $code ] = false;
			return false;
		}
		$tbl_sql = esc_sql( $tbl_valid );

		$cache_group   = 'awxct';
		$key_supported = awxct_lang_cache_key( 'supported_' . md5( $tbl_valid . '|' . $code ) );
		$cached_val    = wp_cache_get( $key_supported, $cache_group );
		if ( $cached_val !== false ) {
			$cache[ $code ] = (bool) (int) $cached_val;
			return (bool) $cache[ $code ];
		}

		// Column detection (cached).
		$key_cols = awxct_lang_cache_key( 'cols_' . md5( $tbl_valid ) );
		$cols     = wp_cache_get( $key_cols, $cache_group );

		if ( $cols === false || ! is_array( $cols ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$cols = array_map(
				'strtolower',
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				(array) $wpdb->get_col( 'SHOW COLUMNS FROM `' . $tbl_sql . '`', 0 )
			);
			wp_cache_set( $key_cols, $cols, $cache_group, 300 );
		}

		$has_code_col   = in_array( 'code', $cols, true );
		$has_locale_col = in_array( 'locale', $cols, true );
		$use_code       = $has_code_col || ! $has_locale_col; // default to "code" if in doubt.

		// Does it exist at all?
		if ( $use_code ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$exists = (int) $wpdb->get_var(
				$wpdb->prepare(
					'SELECT COUNT(*) FROM `' . $tbl_sql . '` WHERE LOWER(`code`) = %s LIMIT 1',
					$code
				)
			);
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$exists = (int) $wpdb->get_var(
				$wpdb->prepare(
					'SELECT COUNT(*) FROM `' . $tbl_sql . '` WHERE LOWER(`locale`) = %s LIMIT 1',
					$code
				)
			);
		}

		if ( $exists <= 0 ) {
			wp_cache_set( $key_supported, 0, $cache_group, 300 );
			$cache[ $code ] = false;
			return false;
		}

		$has_active     = in_array( 'active', $cols, true ) || in_array( 'is_active', $cols, true );
		$has_is_default = in_array( 'is_default', $cols, true );
		$has_default    = in_array( 'default', $cols, true );

		if ( ! $has_active && ! $has_is_default && ! $has_default ) {
			wp_cache_set( $key_supported, 1, $cache_group, 300 );
			$cache[ $code ] = true;
			return true;
		}

		// Fixed query variants (branches only).
		if ( $use_code ) {
			if ( in_array( 'is_active', $cols, true ) && $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND (COALESCE(`is_active`,0)=1 OR COALESCE(`is_default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'is_active', $cols, true ) && $has_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND (COALESCE(`is_active`,0)=1 OR COALESCE(`default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) && $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND (COALESCE(`active`,0)=1 OR COALESCE(`is_default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) && $has_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND (COALESCE(`active`,0)=1 OR COALESCE(`default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'is_active', $cols, true ) ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND COALESCE(`is_active`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND COALESCE(`active`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} elseif ( $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND COALESCE(`is_default`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} else {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`code`) = %s
						   AND COALESCE(`default`,0)=1
						 LIMIT 1',
						$code
					)
				);
			}
		} else {
			if ( in_array( 'is_active', $cols, true ) && $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND (COALESCE(`is_active`,0)=1 OR COALESCE(`is_default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'is_active', $cols, true ) && $has_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND (COALESCE(`is_active`,0)=1 OR COALESCE(`default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) && $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND (COALESCE(`active`,0)=1 OR COALESCE(`is_default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) && $has_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND (COALESCE(`active`,0)=1 OR COALESCE(`default`,0)=1)
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'is_active', $cols, true ) ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND COALESCE(`is_active`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} elseif ( in_array( 'active', $cols, true ) ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND COALESCE(`active`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} elseif ( $has_is_default ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND COALESCE(`is_default`,0)=1
						 LIMIT 1',
						$code
					)
				);
			} else {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$ok = (int) $wpdb->get_var(
					$wpdb->prepare(
						'SELECT COUNT(*) FROM `' . $tbl_sql . '`
						 WHERE LOWER(`locale`) = %s
						   AND COALESCE(`default`,0)=1
						 LIMIT 1',
						$code
					)
				);
			}
		}

		$supported = ( $ok > 0 );

		wp_cache_set( $key_supported, $supported ? 1 : 0, $cache_group, 300 );
		$cache[ $code ] = $supported;
		return $supported;
	}
}
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared

/**
 * Returns the default language code (lowercase, with dash).
 * Example: de-DE -> de-de
 *
 * PHPCS note:
 * - Table/column identifiers below are not user input; they are derived from
 *   validated schema names and discovered columns.
 * - WordPress.DB.PreparedSQL.NotPrepared is suppressed here because identifiers
 *   cannot be safely passed as value placeholders.
 */
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
if ( ! function_exists( 'awxct_default_lang' ) ) {
	function awxct_default_lang(): string {
		static $def = null;
		if ( null !== $def ) {
			return (string) $def;
		}

		global $wpdb;

		$tbl_raw = defined( 'AWXCT_TABLE_LANGUAGES' )
			? (string) AWXCT_TABLE_LANGUAGES
			: (string) ( $wpdb->prefix . 'awxct_languages' );

		$tbl_valid = awxct_validate_table_ident( $tbl_raw );
		if ( '' !== $tbl_valid ) {
			$tbl_sql = esc_sql( $tbl_valid );
			$key     = awxct_lang_cache_key( 'default_lang_' . md5( $tbl_valid ) );
			$cached  = wp_cache_get( $key, 'awxct' );
			if ( false !== $cached && is_string( $cached ) && '' !== $cached ) {
				$def = $cached;
				return (string) $def;
			}

			$cols = array_map(
				'strtolower',
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				(array) $wpdb->get_col( 'SHOW COLUMNS FROM `' . $tbl_sql . '`', 0 )
			);

			$use_code     = in_array( 'code', $cols, true ) || ! in_array( 'locale', $cols, true );
			$lang_col_sql = $use_code ? '`code`' : '`locale`';
			$default_col  = in_array( 'is_default', $cols, true ) ? '`is_default`' : ( in_array( 'default', $cols, true ) ? '`default`' : '' );
			$active_col   = in_array( 'active', $cols, true ) ? '`active`' : ( in_array( 'is_active', $cols, true ) ? '`is_active`' : '' );

			$candidates = array();
			if ( '' !== $default_col ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$candidates[] = (string) $wpdb->get_var( 'SELECT ' . $lang_col_sql . ' FROM `' . $tbl_sql . '` WHERE COALESCE(' . $default_col . ',0)=1 ORDER BY ' . $lang_col_sql . ' LIMIT 1' );
			}
			if ( '' !== $active_col ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				$candidates[] = (string) $wpdb->get_var( 'SELECT ' . $lang_col_sql . ' FROM `' . $tbl_sql . '` WHERE COALESCE(' . $active_col . ',0)=1 ORDER BY ' . $lang_col_sql . ' LIMIT 1' );
			}
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$candidates[] = (string) $wpdb->get_var( 'SELECT ' . $lang_col_sql . ' FROM `' . $tbl_sql . '` ORDER BY ' . $lang_col_sql . ' LIMIT 1' );

			foreach ( $candidates as $candidate ) {
				$candidate = strtolower( str_replace( '_', '-', trim( (string) $candidate ) ) );
				if ( '' !== $candidate ) {
					$def = $candidate;
					wp_cache_set( $key, $def, 'awxct', 300 );
					return (string) $def;
				}
			}
		}

		$def = 'de-de';
		return (string) $def;
	}
}
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared

/**
 * Returns the currently active language (lowercase, with dash).
 */
if ( ! function_exists( 'awxct_current_lang' ) ) {
	function awxct_current_lang(): string {
		$qv = get_query_var( 'awxct_lang' );
		if ( is_string( $qv ) && '' !== trim( $qv ) ) {
			$c = strtolower( str_replace( '_', '-', trim( $qv ) ) );
			if ( awxct_is_supported_lang( $c ) ) {
				return $c;
			}
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only language detection from query string.
		$set_lang = isset( $_GET['awxct_set_lang'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['awxct_set_lang'] ) ) : '';
		if ( '' !== $set_lang ) {
			$c = strtolower( str_replace( '_', '-', trim( $set_lang ) ) );
			if ( awxct_is_supported_lang( $c ) ) {
				return $c;
			}
		}

		if ( isset( $_COOKIE['awxct_lang'] ) ) {
			$c = strtolower( str_replace( '_', '-', trim( sanitize_text_field( wp_unslash( (string) $_COOKIE['awxct_lang'] ) ) ) ) );
			if ( awxct_is_supported_lang( $c ) ) {
				return $c;
			}
		}
		$req_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/';
		$path    = wp_parse_url( $req_uri, PHP_URL_PATH );
		$path    = is_string( $path ) && '' !== $path ? $path : '/';

		$home       = (string) get_option( 'home' );
		$home_parts = wp_parse_url( $home );
		$home_path  = ( isset( $home_parts['path'] ) && is_string( $home_parts['path'] ) ) ? rtrim( $home_parts['path'], '/' ) : '';

		$rel = $path;
		if ( $home_path && '/' !== $home_path && 0 === stripos( $rel, $home_path ) ) {
			$rel = substr( $rel, strlen( $home_path ) );
		}
		$rel = '/' . ltrim( (string) $rel, '/' );

		if ( preg_match( '#^/([a-z]{2}-[a-z]{2})(?:/|$)#i', $rel, $m ) ) {
			$c = strtolower( str_replace( '_', '-', (string) $m[1] ) );
			if ( awxct_is_supported_lang( $c ) ) {
				return $c;
			}
		}

		return awxct_default_lang();
	}
}

/**
 * Canonical locale helper, e.g. de-de -> de-DE.
 */
if ( ! function_exists( 'awxct_canon_locale' ) ) {
	function awxct_canon_locale( string $code ): string {
		$norm = awxct_normalize_code( $code );
		if ( '' === $norm ) {
			return '';
		}
		if ( false === strpos( $norm, '-' ) ) {
			$base = strtolower( $norm );
			return '' !== $base ? $base . '-' . strtoupper( $base ) : '';
		}
		list( $lang, $reg ) = explode( '-', $norm, 2 );
		return strtolower( (string) $lang ) . '-' . strtoupper( (string) $reg );
	}
}

/**
 * Current locale in canonical form, e.g. de-DE.
 */
if ( ! function_exists( 'awxct_current_locale' ) ) {
	function awxct_current_locale(): string {
		$lc = awxct_canon_locale( awxct_current_lang() );
		return '' !== $lc ? $lc : 'de-DE';
	}
}

/**
 * Default locale in canonical form, e.g. de-DE.
 */
if ( ! function_exists( 'awxct_default_locale' ) ) {
	function awxct_default_locale(): string {
		$lc = awxct_canon_locale( awxct_default_lang() );
		return '' !== $lc ? $lc : 'de-DE';
	}
}

/**
 * Legacy compatibility bridge for Plus/Pro.
 *
 * Important: do not declare awx_plg_ct_current_locale() here because Pro
 * already defines that function and would fatal on redeclare. Use filters
 * instead so Plus/Pro can obtain the active language/locale without a hard
 * function collision.
 */
if ( ! function_exists( 'awx_plg_ct_current_lang' ) ) {
	function awx_plg_ct_current_lang(): string {
		return awxct_current_lang();
	}
}

add_filter(
	'awx_plg_ct_current_lang',
	static function ( $lang ) {
		$lang = is_string( $lang ) ? trim( $lang ) : '';
		return '' !== $lang ? $lang : awxct_current_lang();
	},
	1
);

add_filter(
	'awx_plg_ct_current_locale',
	static function ( $lang ) {
		$lang = is_string( $lang ) ? trim( $lang ) : '';
		return '' !== $lang ? $lang : awxct_current_locale();
	},
	1
);

add_filter(
	'pre_option_awx_plg_ct_default_language',
	static function ( $value ) {
		$value = is_string( $value ) ? trim( $value ) : '';
		return '' !== $value ? $value : awxct_default_locale();
	},
	1
);
