<?php
/**
 * Per-language storage for merchant-entered texts.
 *
 * @package Vanquish\MultipleAddresses\Com
 */

namespace Vanquish\MultipleAddresses\Com;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Lets the plugin keep a translation of a text per language, right inside its own
 * options. The merchant fills one field per active language on the settings page;
 * the frontend then reads back the value for the active language, falling back to
 * the default text.
 *
 * A translatable value is stored as an array keyed by language code
 * (e.g. array( 'en' => '…', 'it' => '…' )). On a single-language site everything
 * lives under the 'default' key, so the shape never changes.
 */
class Locale {

	/**
	 * The current language code ('default' when no multilingual plugin is active).
	 *
	 * @return string
	 */
	public static function current() {
		if ( defined( 'ICL_LANGUAGE_CODE' ) && constant( 'ICL_LANGUAGE_CODE' ) ) {
			return (string) constant( 'ICL_LANGUAGE_CODE' );
		}

		if ( function_exists( 'pll_current_language' ) ) {
			$lang = pll_current_language();
			if ( $lang ) {
				return (string) $lang;
			}
		}

		return 'default';
	}

	/**
	 * Read the value for the current language, with a fallback.
	 *
	 * Accepts both the per-language array shape and a plain string (a value saved
	 * before this site became multilingual), so upgrades never lose data.
	 *
	 * @param mixed  $stored   Stored value (array keyed by language, or a string).
	 * @param string $fallback Returned when nothing is set for the language.
	 * @return string
	 */
	public static function get( $stored, $fallback = '' ) {
		if ( is_array( $stored ) ) {
			$lang = self::current();

			if ( isset( $stored[ $lang ] ) && '' !== $stored[ $lang ] ) {
				return (string) $stored[ $lang ];
			}

			// Fall back to the single-language bucket before the hardcoded default.
			if ( isset( $stored['default'] ) && '' !== $stored['default'] ) {
				return (string) $stored['default'];
			}

			return (string) $fallback;
		}

		return '' !== (string) $stored ? (string) $stored : (string) $fallback;
	}

	/**
	 * The active languages, normalized to array( 'code', 'name', 'flag' ).
	 *
	 * Empty on a single-language site (or when the list cannot be read), which the
	 * callers treat as "render a single field".
	 *
	 * @return array
	 */
	public static function languages() {
		// WPML — WPML's own filter, not ours.
		if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
			// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's own hook.
			$langs = apply_filters( 'wpml_active_languages', null, array( 'skip_missing' => 0 ) );

			if ( is_array( $langs ) && ! empty( $langs ) ) {
				$out = array();
				foreach ( $langs as $code => $lang ) {
					$out[] = array(
						'code' => isset( $lang['language_code'] ) ? (string) $lang['language_code'] : (string) $code,
						'name' => isset( $lang['native_name'] ) ? (string) $lang['native_name'] : (string) $code,
						'flag' => isset( $lang['country_flag_url'] ) ? (string) $lang['country_flag_url'] : '',
					);
				}
				return $out;
			}
		}

		// Polylang.
		if ( function_exists( 'pll_languages_list' ) ) {
			$codes = pll_languages_list();
			$out   = array();
			foreach ( (array) $codes as $code ) {
				$out[] = array(
					'code' => (string) $code,
					'name' => strtoupper( (string) $code ),
					'flag' => '',
				);
			}
			return $out;
		}

		return array();
	}

	/**
	 * Turn a stored value into one field per active language (or a single field on
	 * a single-language site), so the merchant edits every language at once.
	 *
	 * On a multilingual site the field name is "base[code]" and the value is the raw
	 * text stored for that language (empty when not set), with the default text as
	 * the placeholder.
	 *
	 * @param string $base_name Base field name.
	 * @param mixed  $stored    Stored value (per-language array or a string).
	 * @param string $default   Default text (used as placeholder / mono fallback).
	 * @return array
	 */
	public static function field_variants( $base_name, $stored, $default = '' ) {
		$langs = self::languages();

		if ( empty( $langs ) ) {
			return array(
				array(
					'name'        => $base_name,
					'value'       => self::get( $stored, '' ),
					'placeholder' => $default,
					'code'        => '',
					'label'       => '',
					'flag'        => '',
				),
			);
		}

		$arr = is_array( $stored )
			? $stored
			: ( '' !== (string) $stored ? array( 'default' => (string) $stored ) : array() );

		$variants = array();
		foreach ( $langs as $lang ) {
			$code       = $lang['code'];
			$variants[] = array(
				'name'        => $base_name . '[' . $code . ']',
				'value'       => isset( $arr[ $code ] ) ? (string) $arr[ $code ] : '',
				'placeholder' => $default,
				'code'        => $code,
				'label'       => $lang['name'],
				'flag'        => $lang['flag'],
			);
		}

		return $variants;
	}

	/**
	 * A small language badge (flag + code) for a field variant ('' when mono).
	 *
	 * @param array $variant A field variant from field_variants().
	 * @return string Safe HTML.
	 */
	public static function badge( array $variant ) {
		if ( '' === $variant['code'] ) {
			return '';
		}

		$flag = '' !== $variant['flag']
			? '<img src="' . esc_url( $variant['flag'] ) . '" alt="" /> '
			: '';

		return '<span class="vanmuad-lang-badge">' . $flag . esc_html( strtoupper( $variant['code'] ) ) . '</span>';
	}

	/**
	 * Sanitize a submitted translatable field: a plain value on a single-language
	 * site, or a per-language array on a multilingual one.
	 *
	 * @param mixed    $posted   Submitted value (string or array keyed by language).
	 * @param callable $callback Sanitizer applied to each value.
	 * @return mixed The value ready to store (string or per-language array).
	 */
	public static function sanitize_field( $posted, $callback = 'sanitize_text_field' ) {
		if ( is_array( $posted ) ) {
			$out = array();
			foreach ( $posted as $code => $value ) {
				$code = sanitize_key( (string) $code );
				if ( '' !== $code ) {
					$out[ $code ] = call_user_func( $callback, (string) $value );
				}
			}
			return $out;
		}

		return call_user_func( $callback, (string) $posted );
	}

	/**
	 * An admin notice explaining that the texts on the page are saved per language
	 * ('' on a single-language site, so nothing is shown there).
	 *
	 * @return string Safe HTML (escaped), ready to echo.
	 */
	public static function notice() {
		if ( empty( self::languages() ) ) {
			return '';
		}

		$message = esc_html__( 'Multilingual site detected: fill in the texts for each language. Empty fields fall back to the default.', 'vanquish-multiple-addresses-for-woocommerce' );

		// Spacing lives in the admin stylesheet rather than a style attribute: the
		// caller escapes this with wp_kses_post(), which runs any inline style
		// through safecss_filter_attr, and layout that survives only at the mercy
		// of a filter's allow-list is layout waiting to break.
		return '<div class="notice notice-info inline vanmuad-locale-notice"><p>' . $message . '</p></div>';
	}
}
