<?php
/**
 * Language Manager for BoostBot.
 *
 * Centralized service for multilingual language detection, enabled-language
 * management, and admin language context. Integrates with Polylang and WPML
 * when available, falls back to WordPress locale.
 *
 * @package BoostBot
 * @since   1.2.0
 */

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

class BoostBot_Language_Manager {

    const COOKIE_NAME = 'boostbot_admin_lang';

    const SETTING_KEY = 'enabled_languages';

    private static ?BoostBot_Language_Manager $instance = null;

    /**
     * RTL language codes.
     */
    private static array $rtl_languages = array( 'he', 'ar', 'fa', 'ur' );

    /**
     * Human-readable language labels (native names).
     */
    private static array $language_labels = array(
        'he' => 'עברית',
        'en' => 'English',
        'ar' => 'العربية',
        'fr' => 'Français',
        'es' => 'Español',
        'de' => 'Deutsch',
        'ru' => 'Русский',
    );

    public static function get_instance(): self {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {}

    // ─── Enabled Languages ───────────────────────────────────

    /**
     * Get the list of languages enabled for BoostBot on this site.
     *
     * Priority: Polylang > WPML > boostbot_enabled_languages setting > WP locale.
     *
     * @return string[] e.g. ['he', 'en']
     */
    public function get_enabled_languages(): array {
        // Polylang
        if ( function_exists( 'pll_languages_list' ) ) {
            $langs = pll_languages_list( array( 'fields' => 'slug' ) );
            if ( ! empty( $langs ) ) {
                return array_values( $langs );
            }
        }

        // WPML
        if ( function_exists( 'icl_get_languages' ) ) {
            $wpml_langs = icl_get_languages( 'skip_missing=0' );
            if ( ! empty( $wpml_langs ) ) {
                return array_values( array_keys( $wpml_langs ) );
            }
        }

        // BoostBot setting
        $setting = BoostBot_Settings::get( self::SETTING_KEY, '' );
        if ( ! empty( $setting ) ) {
            $decoded = is_string( $setting ) ? json_decode( $setting, true ) : $setting;
            if ( is_array( $decoded ) && ! empty( $decoded ) ) {
                return array_values( $decoded );
            }
        }

        // WP locale fallback
        $locale = get_locale();
        $lang   = self::locale_to_lang( $locale );
        return array( $lang );
    }

    /**
     * Check whether a language code is among the enabled languages.
     */
    public function is_language_enabled( string $lang ): bool {
        return in_array( $lang, $this->get_enabled_languages(), true );
    }

    // ─── Admin Context ───────────────────────────────────────

    /**
     * Get the current admin language context (from cookie).
     *
     * Falls back to the first enabled language.
     */
    public function get_admin_language(): string {
        if ( isset( $_COOKIE[ self::COOKIE_NAME ] ) ) {
            $lang = sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) );
            if ( $this->is_language_enabled( $lang ) ) {
                return $lang;
            }
        }

        $enabled = $this->get_enabled_languages();
        return $enabled[0] ?? self::locale_to_lang( get_locale() );
    }

    /**
     * Set the admin language context cookie.
     */
    public function set_admin_language( string $lang ): void {
        if ( ! headers_sent() ) {
            setcookie(
                self::COOKIE_NAME,
                $lang,
                time() + YEAR_IN_SECONDS,
                COOKIEPATH,
                COOKIE_DOMAIN,
                is_ssl(),
                true
            );
        }
        $_COOKIE[ self::COOKIE_NAME ] = $lang;
    }

    // ─── Frontend Language Detection ─────────────────────────

    /**
     * Detect the language of the page currently being viewed on the frontend.
     *
     * For multilingual plugins this returns the page's language; for
     * monolingual sites it returns the WP locale language.
     *
     * @param int $post_id Optional specific post ID.
     * @return string Two-letter language code.
     */
    public function get_frontend_language( int $post_id = 0 ): string {
        // Polylang: post-specific language
        if ( $post_id && function_exists( 'pll_get_post_language' ) ) {
            $lang = pll_get_post_language( $post_id, 'slug' );
            if ( $lang ) {
                return $lang;
            }
        }

        // Polylang: current language
        if ( function_exists( 'pll_current_language' ) ) {
            $lang = pll_current_language( 'slug' );
            if ( $lang ) {
                return $lang;
            }
        }

        // WPML: post-specific language
        if ( $post_id ) {
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Third-party WPML filter/action; not plugin-owned.
            $details = apply_filters( 'wpml_post_language_details', null, $post_id );
            if ( is_array( $details ) && ! empty( $details['language_code'] ) ) {
                return $details['language_code'];
            }
        }

        // WPML: current language
        if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
            return ICL_LANGUAGE_CODE;
        }

        // WP locale fallback
        return self::locale_to_lang( get_locale() );
    }

    // ─── Language Metadata ───────────────────────────────────

    /**
     * Check if a language code is RTL.
     */
    public static function is_rtl_language( string $lang ): bool {
        return in_array( $lang, self::$rtl_languages, true );
    }

    /**
     * Get human-readable label for a language code.
     */
    public static function get_language_label( string $lang ): string {
        return self::$language_labels[ $lang ] ?? strtoupper( $lang );
    }

    /**
     * Get the full language config for JavaScript.
     *
     * @return array{enabled: array, current: string, is_rtl: bool, labels: array}
     */
    public function get_language_config(): array {
        $enabled = $this->get_enabled_languages();
        $current = $this->get_admin_language();

        $labels = array();
        foreach ( $enabled as $lang ) {
            $labels[ $lang ] = self::get_language_label( $lang );
        }

        return array(
            'enabled' => $enabled,
            'current' => $current,
            'is_rtl'  => self::is_rtl_language( $current ),
            'labels'  => $labels,
        );
    }

    // ─── Polylang/WPML Content Helpers ───────────────────────

    /**
     * Filter a WP_Query args array to only return posts in a given language.
     *
     * Works with Polylang, WPML, or returns the args unmodified for monolingual sites.
     *
     * @param array  $args     WP_Query args.
     * @param string $language Language code.
     * @return array Modified args.
     */
    public static function filter_query_by_language( array $args, string $language ): array {
        // Polylang
        if ( function_exists( 'pll_current_language' ) ) {
            $args['lang'] = $language;
            return $args;
        }

        // WPML — suppress its automatic filter and set explicit language
        if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
            $args['suppress_filters'] = false;
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Third-party WPML filter/action; not plugin-owned.
            do_action( 'wpml_switch_language', $language );
            return $args;
        }

        return $args;
    }

    /**
     * Get the language of a specific post.
     *
     * @param int $post_id
     * @return string Language code or empty string if unknown.
     */
    public static function get_post_language( int $post_id ): string {
        if ( function_exists( 'pll_get_post_language' ) ) {
            return pll_get_post_language( $post_id, 'slug' ) ?: '';
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Third-party WPML filter/action; not plugin-owned.
        $details = apply_filters( 'wpml_post_language_details', null, $post_id );
        if ( is_array( $details ) && ! empty( $details['language_code'] ) ) {
            return $details['language_code'];
        }

        return '';
    }

    // ─── Utilities ───────────────────────────────────────────

    /**
     * Convert a WordPress locale (e.g. 'he_IL', 'en_US') to a 2-letter code.
     */
    public static function locale_to_lang( string $locale ): string {
        return substr( $locale, 0, 2 );
    }

    /**
     * Convert a 2-letter language code to a WordPress locale.
     *
     * @param string $lang     Language code (e.g. he, en).
     * @param string $fallback Fallback locale when the language is unknown.
     * @return string
     */
    public static function lang_to_locale( string $lang, string $fallback = 'en_US' ): string {
        $normalized = substr( strtolower( trim( $lang ) ), 0, 2 );

        $map = array(
            'he' => 'he_IL',
            'en' => 'en_US',
            'ar' => 'ar',
            'fr' => 'fr_FR',
            'es' => 'es_ES',
            'de' => 'de_DE',
            'ru' => 'ru_RU',
        );

        return $map[ $normalized ] ?? $fallback;
    }
}
