<?php

if (! defined('ABSPATH')) {
    exit;
} final class PufferGo_Polylang
{
    public const STRING_GROUP = 'PufferGo';

    public const TRANSLATABLE_POST_TYPES = array('puffergo_product', 'puffergo_solution', 'puffergo_case', 'puffergo_testimonial', );

    public const TRANSLATABLE_TAXONOMIES = array('puffergo_product_cat', 'puffergo_solution_cat', 'puffergo_case_cat', 'puffergo_testimonial_industry', );

    public const URL_CONFIG_VERSION = 1;

    public const URL_CONFIG_VERSION_OPTION = 'puffergo_polylang_url_config_version';

    public static function init(): void
    {
        add_filter('pll_get_post_types', array(__CLASS__, 'register_post_types'), 10, 2);
        add_filter('pll_get_taxonomies', array(__CLASS__, 'register_taxonomies'), 10, 2);
        add_action('init', array(__CLASS__, 'register_strings'));
        add_action('pll_pre_init', array(__CLASS__, 'configure_url_settings'), 1, 1);
        add_filter('pll_get_new_post_translation_link', array(__CLASS__, 'rewrite_add_translation_link'), 10, 3);
        add_filter('the_content', array(__CLASS__, 'localize_content_links'), 20);
    }

    public static function is_active(): bool
    {
        return function_exists('pll_current_language');
    }

    public static function is_language_admin_ready(): bool
    {
        return self::is_active() && class_exists('PLL_Admin_Model');
    }

    public static function default_language(): string
    {
        if (! function_exists('pll_default_language')) {
            return '';
        }

return (string) pll_default_language();
    }

    public static function current_language(): string
    {
        if (! function_exists('pll_current_language')) {
            return '';
        }

return (string) pll_current_language('slug');
    }

    public static function home_url(string $lang = ''): string
    {
        $lang = '' !== $lang ? $lang : self::current_language();

        if ('' === $lang || ! function_exists('pll_home_url')) {
            return trailingslashit(home_url('/'));
        }

return trailingslashit((string) pll_home_url($lang));
    }

    public static function languages(): array
    {
        if (! function_exists('pll_languages_list')) {
            return array();
        } $slugs = (array) pll_languages_list(array('fields' => 'slug'));
        $names = (array) pll_languages_list(array('fields' => 'name'));
        $locales = (array) pll_languages_list(array('fields' => 'locale'));
        $languages = array();

        foreach (array_values($slugs) as $i => $slug) {
            $languages[] = array('slug' => (string) $slug, 'name' => isset($names[ $i ]) ? (string) $names[ $i ] : (string) $slug, 'locale' => isset($locales[ $i ]) ? (string) $locales[ $i ] : (string) $slug, );
        }

return $languages;
    }

    public static function locale_for(string $slug): string
    {
        if ('' === $slug) {
            return '';
        }

foreach (self::languages() as $language) {
            if ($language['slug'] === $slug) {
                return '' !== $language['locale'] ? $language['locale'] : $slug;
            }
        }

return $slug;
    }

    public static function get_post_language(int $post_id): string
    {
        if (! function_exists('pll_get_post_language')) {
            return '';
        }

return (string) pll_get_post_language($post_id, 'slug');
    }

    public static function set_post_language(int $post_id, string $slug): void
    {
        if ('' === $slug || ! function_exists('pll_set_post_language')) {
            return;
        } pll_set_post_language($post_id, $slug);
    }

    public static function get_translation(int $post_id, string $slug): int
    {
        if ('' === $slug || ! function_exists('pll_get_post')) {
            return 0;
        }

return (int) pll_get_post($post_id, $slug);
    }

    public static function save_translations(array $map): void
    {
        if (empty($map) || ! function_exists('pll_save_post_translations')) {
            return;
        } pll_save_post_translations(array_map('intval', $map));
    }

    public static function get_term_language(int $term_id): string
    {
        if (! function_exists('pll_get_term_language')) {
            return '';
        }

return (string) pll_get_term_language($term_id, 'slug');
    }

    public static function set_term_language(int $term_id, string $slug): void
    {
        if ('' === $slug || ! function_exists('pll_set_term_language')) {
            return;
        } pll_set_term_language($term_id, $slug);
    }

    public static function get_term_translation(int $term_id, string $slug): int
    {
        if ('' === $slug || ! function_exists('pll_get_term')) {
            return 0;
        }

return (int) pll_get_term($term_id, $slug);
    }

    public static function get_term_language_fresh(int $term_id): string
    {
        global $wpdb;

        if ($term_id <= 0) {
            return '';
        } $slug = self::query_var_with_retry($wpdb->prepare("SELECT t.slug FROM {$wpdb->term_relationships} tr
				INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
				INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
				WHERE tr.object_id = %d AND tt.taxonomy = 'term_language'", $term_id), true);

        return is_string($slug) && 0 === strpos($slug, 'pll_') ? substr($slug, 4) : '';
    }

    public static function get_term_translation_fresh(int $term_id, string $slug): int
    {
        global $wpdb;

        if ($term_id <= 0 || '' === $slug) {
            return 0;
        } $description = self::query_var_with_retry($wpdb->prepare("SELECT tt.description FROM {$wpdb->term_relationships} tr
				INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
				WHERE tr.object_id = %d AND tt.taxonomy = 'term_translations'", $term_id), false);

        if (! is_string($description) || '' === $description) {
            return 0;
        } $map = maybe_unserialize($description);

        return (is_array($map) && isset($map[ $slug ])) ? (int) $map[ $slug ] : 0;
    }

    private static function query_var_with_retry(string $sql, bool $retry_empty_string)
    {
        global $wpdb;
        $is_empty = static function ($value) use ($retry_empty_string) { return null === $value || ($retry_empty_string && '' === $value); };
        $value = $wpdb->get_var($sql);
        for ($attempt = 0; $is_empty($value) && $attempt < 4; $attempt++) {
            usleep(50000);
            $value = $wpdb->get_var($sql);
        }

return $value;
    }

    public static function merge_term_translations(array $map): void
    {
        if (empty($map) || ! function_exists('pll_get_term_translations') || ! function_exists('pll_save_term_translations')) {
            return;
        } $translations = array();

        foreach ($map as $term_id) {
            $term_id = (int) $term_id;

            if ($term_id <= 0) {
                continue;
            }

foreach ((array) pll_get_term_translations($term_id) as $slug => $existing_id) {
                $existing_id = (int) $existing_id;

                if ('' !== (string) $slug && $existing_id > 0) {
                    $translations[ (string) $slug ] = $existing_id;
                }
            }
        }

foreach ($map as $slug => $term_id) {
            $term_id = (int) $term_id;

            if ('' !== (string) $slug && $term_id > 0) {
                $translations[ (string) $slug ] = $term_id;
            }
        }

if (count($translations) >= 2) {
            pll_save_term_translations($translations);
        }
    }

    public static function get_nav_menu_location(string $location, string $lang): int
    {
        if (! self::is_active() || ! function_exists('PLL') || empty(PLL()->options)) {
            return 0;
        } $nav_menus = PLL()->options->get('nav_menus');
        $theme = get_stylesheet();

        if (! is_array($nav_menus) || empty($nav_menus[ $theme ][ $location ][ $lang ])) {
            return 0;
        }

return (int) $nav_menus[ $theme ][ $location ][ $lang ];
    }

    public static function set_nav_menu_location(string $location, string $lang, int $menu_id): bool
    {
        if (! self::is_active() || ! function_exists('PLL') || empty(PLL()->options)) {
            return false;
        } $theme = get_stylesheet();
        $nav_menus = PLL()->options->get('nav_menus');

        if (! is_array($nav_menus)) {
            $nav_menus = array();
        } $nav_menus[ $theme ][ $location ][ $lang ] = (int) $menu_id;
        $result = PLL()->options->set('nav_menus', $nav_menus);
        PLL()->options->save();

        return ! (is_wp_error($result) && $result->has_errors());
    }

    public static function rewrite_add_translation_link(string $link, $language, int $post_id): string
    {
        if ('' === $link) {
            return $link;
        } $post = get_post($post_id);

        if (! $post instanceof WP_Post) {
            return $link;
        } $managed_types = array_merge(array('page'), self::TRANSLATABLE_POST_TYPES);

        if (! in_array($post->post_type, $managed_types, true)) {
            return $link;
        }

return add_query_arg(array('post' => $post_id, 'action' => 'edit', 'puffergo_tab' => 'translate', ), admin_url('post.php'));
    }

    public static function register_post_types($post_types, $is_settings = false): array
    {
        unset($is_settings);
        $post_types = is_array($post_types) ? $post_types : array();

        foreach (self::TRANSLATABLE_POST_TYPES as $slug) {
            $post_types[ $slug ] = $slug;
        }

return $post_types;
    }

    public static function register_taxonomies($taxonomies, $is_settings = false): array
    {
        unset($is_settings);
        $taxonomies = is_array($taxonomies) ? $taxonomies : array();

        foreach (self::TRANSLATABLE_TAXONOMIES as $slug) {
            $taxonomies[ $slug ] = $slug;
        }

return $taxonomies;
    }

    public static function configure_url_settings($polylang): void
    {
        $options = is_object($polylang) && isset($polylang->options) ? $polylang->options : null;
        $model = is_object($polylang) && isset($polylang->model) ? $polylang->model : null;

        if (! is_object($options) || ! is_callable(array($options, 'get')) || ! is_callable(array($options, 'set')) || ! is_callable(array($options, 'save')) || ! is_object($model) || ! is_callable(array($model, 'clean_languages_cache'))) {
            return;
        } $changed = false;

        if (1 !== (int) $options->get('redirect_lang')) {
            $result = $options->set('redirect_lang', 1);

            if (is_wp_error($result) && $result->has_errors()) {
                return;
            } $options->save();
            $changed = true;
        } $installed_version = (int) get_option(self::URL_CONFIG_VERSION_OPTION, 0);

        if (! $changed && self::URL_CONFIG_VERSION === $installed_version) {
            return;
        } $model->clean_languages_cache();
        delete_option('rewrite_rules');
        update_option(self::URL_CONFIG_VERSION_OPTION, self::URL_CONFIG_VERSION, false);
    }

    public static function localize_url(string $url, string $lang): string
    {
        if ('' === $url || '' === $lang) {
            return $url;
        } static $cache = array();
        $cache_key = $lang . '|' . $url;

        if (isset($cache[ $cache_key ])) {
            return $cache[ $cache_key ];
        } $absolute = self::absolute_internal_url($url);

        if ('' === $absolute) {
            $cache[ $cache_key ] = $url;

            return $url;
        } $url_path = untrailingslashit((string) wp_parse_url($absolute, PHP_URL_PATH));
        $home_path = untrailingslashit((string) wp_parse_url(home_url('/'), PHP_URL_PATH));

        if ($url_path === $home_path) {
            $cache[ $cache_key ] = self::append_url_suffix(self::home_url($lang), $absolute);

            return $cache[ $cache_key ];
        } $post_id = url_to_postid($absolute);

        if ($post_id > 0) {
            $translated = self::get_translation($post_id, $lang);

            if ($translated > 0 && 'publish' === get_post_status($translated)) {
                $base = (int) get_option('page_on_front', 0) === $post_id ? self::home_url($lang) : (string) get_permalink($translated);
                $cache[ $cache_key ] = self::append_url_suffix($base, $absolute);

                return $cache[ $cache_key ];
            } $cache[ $cache_key ] = $url;

            return $url;
        } $language = self::language_object($lang);

        if (null === $language || ! function_exists('PLL') || empty(PLL()->links_model) || ! is_callable(array(PLL()->links_model, 'switch_language_in_link'))) {
            $cache[ $cache_key ] = $url;

            return $url;
        } $localized = PLL()->links_model->switch_language_in_link($absolute, $language);
        $cache[ $cache_key ] = is_string($localized) && '' !== $localized ? $localized : $url;

        return $cache[ $cache_key ];
    }

    public static function localize_content_links(string $content): string
    {
        if (is_admin() || '' === $content) {
            return $content;
        } $lang = self::current_language();

        if ('' === $lang || $lang === self::default_language()) {
            return $content;
        } $localized = preg_replace_callback('/(\bhref\s*=\s*)(["\'])([^"\']*)\2/i', static function (array $matches) use ($lang): string {
            $url = $matches[3];

            if ('' === $url || '#' === $url[0]) {
                return $matches[0];
            } $localized_url = self::localize_url(html_entity_decode($url, ENT_QUOTES, 'UTF-8'), $lang);

            if ($localized_url === $url) {
                return $matches[0];
            } $safe_url = esc_url($localized_url);

            return '' !== $safe_url ? $matches[1] . $matches[2] . $safe_url . $matches[2] : $matches[0];
        }, $content);

        return is_string($localized) ? $localized : $content;
    }

    private static function absolute_internal_url(string $url): string
    {
        if (preg_match('#^(?:mailto|tel|javascript|data):#i', $url)) {
            return '';
        }

if (0 === strpos($url, '/') && 0 !== strpos($url, '//')) {
            $url = home_url($url);
        } elseif (0 === strpos($url, '//')) {
            $url = (is_ssl() ? 'https:' : 'http:') . $url;
        } elseif (! preg_match('#^https?://#i', $url)) {
            return '';
        } $site_host = strtolower((string) wp_parse_url(home_url('/'), PHP_URL_HOST));
        $url_host = strtolower((string) wp_parse_url($url, PHP_URL_HOST));

        return '' !== $site_host && $site_host === $url_host ? $url : '';
    }

    private static function append_url_suffix(string $base, string $source): string
    {
        $query = (string) wp_parse_url($source, PHP_URL_QUERY);
        $fragment = (string) wp_parse_url($source, PHP_URL_FRAGMENT);

        if ('' !== $query) {
            $base .= (false === strpos($base, '?') ? '?' : '&') . $query;
        }

if ('' !== $fragment) {
            $base .= '#' . $fragment;
        }

return $base;
    }

    public static function register_strings(): void
    {
        if (! function_exists('pll_register_string')) {
            return;
        } $copyright = (string) get_option('puffergo_site_footer_copyright', '');

        if ('' === $copyright) {
            return;
        } pll_register_string('Footer copyright', $copyright, self::STRING_GROUP);
    }

    public static function translate(string $value): string
    {
        if ('' === $value || ! function_exists('pll__')) {
            return $value;
        }

return (string) pll__($value);
    }

    public static function languages_html(array $args = array()): string
    {
        unset($args);

        if (! function_exists('pll_the_languages') || ! function_exists('pll_current_language')) {
            return '';
        } $languages = pll_the_languages(array('raw' => 1, 'hide_if_no_translation' => 0, 'echo' => 0, ));

        if (! is_array($languages) || count($languages) < 2) {
            return '';
        } $current_lang = (string) pll_current_language();
        $queried_id = is_singular() ? (int) get_queried_object_id() : 0;
        $queried_term = is_tax() ? get_queried_object() : null;
        $queried_term = $queried_term instanceof WP_Term ? $queried_term : null;
        $stay_url = null !== $queried_term ? self::term_url($queried_term) : self::stay_on_page_url($queried_id);

        if ('' === $stay_url) {
            $stay_url = home_url('/');
        } static $instance = 0;
        $select_id = 'puffergo-lang-choice-' . (++$instance);
        $options = '';

        foreach ($languages as $language) {
            $slug = isset($language['slug']) ? (string) $language['slug'] : '';

            if ('' === $slug) {
                continue;
            } $name = isset($language['name']) ? (string) $language['name'] : $slug;
            $is_current = ($slug === $current_lang) || ! empty($language['current_lang']);

            if ($is_current) {
                $url = isset($language['url']) ? (string) $language['url'] : $stay_url;
            } elseif ($queried_id > 0) {
                $translated = function_exists('pll_get_post') ? (int) pll_get_post($queried_id, $slug) : 0;
                $url = ($translated > 0 && 'publish' === get_post_status($translated)) ? (string) get_permalink($translated) : $stay_url;
            } elseif (null !== $queried_term) {
                $translated = self::get_term_translation($queried_term->term_id, $slug);
                $translated_url = $translated > 0 ? self::term_url($translated, $queried_term->taxonomy) : '';
                $url = '' !== $translated_url ? $translated_url : $stay_url;
            } else {
                $url = isset($language['url']) ? (string) $language['url'] : $stay_url;
            }

if ('' === $url) {
                $url = $stay_url;
            } $options .= sprintf('<option value="%s"%s>%s</option>', esc_url($url), $is_current ? ' selected="selected"' : '', esc_html($name));
        }

if ('' === $options) {
            return '';
        }

return sprintf('<select id="%1$s" aria-label="%2$s" onchange="if(this.value){window.location.href=this.value;}">%3$s</select>', esc_attr($select_id), esc_attr__('Choose a language', 'puffergo'), $options);
    }

    private static function term_url($term, string $taxonomy = ''): string
    {
        $url = get_term_link($term, $taxonomy);

        return is_wp_error($url) ? '' : (string) $url;
    }

    private static function stay_on_page_url(int $queried_id): string
    {
        if ($queried_id <= 0) {
            return home_url('/');
        }

if (function_exists('pll_get_post')) {
            $default = self::default_language();

            if ('' !== $default) {
                $default_id = (int) pll_get_post($queried_id, $default);

                if ($default_id > 0 && 'publish' === get_post_status($default_id)) {
                    return (string) get_permalink($default_id);
                }
            }
        }

return (string) get_permalink($queried_id);
    }

    public static function add_language(array $args): bool
    {
        if (! function_exists('PLL')) {
            return false;
        } $model = PLL()->model ?? null;

        if (! is_object($model) || ! is_callable(array($model, 'add_language'))) {
            return false;
        } $result = $model->add_language($args);

        return ! is_wp_error($result) && false !== $result;
    }

    public static function predefined_languages(): array
    {
        if (! class_exists('PLL_Settings') || ! method_exists('PLL_Settings', 'get_predefined_languages')) {
            return array();
        } set_error_handler(static function () { return true; });
        $catalog = PLL_Settings::get_predefined_languages();
        restore_error_handler();

        if (! is_array($catalog)) {
            return array();
        } $languages = array();

        foreach ($catalog as $locale => $data) {
            $languages[] = array('slug' => isset($data['code']) ? (string) $data['code'] : (string) $locale, 'locale' => isset($data['locale']) ? (string) $data['locale'] : (string) $locale, 'name' => isset($data['name']) ? (string) $data['name'] : (string) $locale, );
        } usort($languages, static function ($a, $b) { return strcasecmp($a['name'], $b['name']); });

        return $languages;
    }

    public static function language_term_id(string $slug): int
    {
        if ('' === $slug || ! function_exists('PLL')) {
            return 0;
        } $model = PLL()->model ?? null;

        if (! is_object($model) || ! is_callable(array($model, 'get_language'))) {
            return 0;
        } $language = $model->get_language($slug);

        if (! is_object($language) || empty($language->term_id)) {
            return 0;
        }

return (int) $language->term_id;
    }

    public static function language_object(string $slug): ?object
    {
        if ('' === $slug || ! function_exists('PLL')) {
            return null;
        } $model = PLL()->model ?? null;

        if (! is_object($model) || ! is_callable(array($model, 'get_language'))) {
            return null;
        } $language = $model->get_language($slug);

        return is_object($language) ? $language : null;
    }

    public static function delete_language(int $term_id): bool
    {
        if ($term_id <= 0 || ! function_exists('PLL')) {
            return false;
        } $model = PLL()->model ?? null;

        if (! is_object($model) || ! is_callable(array($model, 'delete_language'))) {
            return false;
        } $result = $model->delete_language($term_id);

        return ! is_wp_error($result) && false !== $result;
    }

    public static function untagged_localizable_post_ids(): array
    {
        if (! self::is_active()) {
            return array();
        } $base = array('post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'lang' => '', 'no_found_rows' => true, 'tax_query' => array(array('taxonomy' => 'language', 'operator' => 'NOT EXISTS', ), ), );
        $managed = get_posts(array_merge($base, array('post_type' => 'any', 'meta_query' => array(array('key' => '_puffergo_managed', 'compare' => 'EXISTS', ), ), )));
        $cpts = get_posts(array_merge($base, array('post_type' => self::TRANSLATABLE_POST_TYPES)));
        $ids = array_map('intval', array_merge((array) $managed, (array) $cpts));

        return array_values(array_unique(array_filter($ids)));
    }
}
