<?php

namespace Clonable\Services;

use Clonable\Helpers\Locales;
use Clonable\Objects\ClonableConfig;
use Clonable\Helpers\Html;
use Clonable\Helpers\UrlTranslator;
use Clonable\Helpers\Functions;
use stdClass;
use Throwable;

defined('ABSPATH') || exit;

/**
 * Handles the rendering of the language switcher on the page.
 * Uses the values set by the language switcher settings.
 */
class LanguageSwitcherService
{
    /**
     * Prints out the language switcher script tag and the corresponding JavaScript settings.
     * @return void echos out the scripts.
     */

    // Define constants for menu item classes
    private const MENU_ITEM_CLASS = 'clonable-language-switcher';
    private const MENU_ITEM_LI_CLASS = 'menu-item clonable-menu-item-language-switcher';

    public function __construct()
    {

        add_shortcode('clonable_language_switcher', [$this, 'language_switcher_shortcode']);

        $plugin_language_switcher_settings = $this->get_language_switcher_settings();

        if (!$plugin_language_switcher_settings['showFlag'] && !$plugin_language_switcher_settings['showTargetLanguage']) {
            // If both flag and text are disabled, no need to render the switcher
            return;
        }

        add_action("wp_head", [$this, 'print_language_switcher']);
        add_action("wp_head", [$this, 'include_language_switcher_assets']);
        add_action('admin_head', [$this, 'include_language_switcher_assets']);

        // Register the meta box for Appearance → Menus
        add_action('admin_head-nav-menus.php', [$this, 'register_nav_menu_meta_box']);
        // Hook into menu objects to inject language items from native menu items
        add_filter('wp_nav_menu_objects', [$this, 'inject_language_menu_items'], 10, 2);
        // Customize the output of injected language child menu items
        add_filter('walker_nav_menu_start_el', [$this, 'customize_language_menu_item_output'], 10, 4);

        add_filter(
            'render_block_core/navigation-submenu',
            [$this, 'submenu_block_language_switcher'],
            10,
            2
        );
    }

    /**
     * New entry point for hooking into wp_nav_menu_items.
     * Matches the signature expected by add_action/add_filter('wp_nav_menu_items', ...).
     *
     * @param string $items The HTML list content for the menu items.
     * @param stdClass $args An object containing wp_nav_menu() arguments.
     *
     * @return string
     */
    public function add_language_switcher_to_menu($items, $menu_args): string
    {
        $plugin_language_switcher_settings = $this->get_language_switcher_settings();
        return $this->build_language_switcher($plugin_language_switcher_settings, $items, $menu_args, 'traditional_menu');
    }

    public function include_language_switcher_assets()
    {
        /* 
        Load assets for the language switcher preview. 
        If we are in admin, only load when current page is a clonable page, to avoid loading unnecessary assets in the admin.
        */
        $is_clonable_page = Functions::is_clonable_page();
        if ( is_admin() && !$is_clonable_page ) {
            return;
        }

        Html::flags();
        Html::include_js("language-switcher.js", CLONABLE_VERSION);

        //check if the user has disabled the default styles of the language switcher, if not include the default css.
        $disable_styles = (get_option('clonable_disable_language_switcher_styles', 'on') === 'on') ? 1 : 0;
        if ($disable_styles) {
            return;
        }

        Html::include_css("clonable-language-switcher.css", CLONABLE_VERSION);
        $this->add_menu_language_switcher_css();
    }

    public function add_menu_language_switcher_css(): void
    {
        $plugin_language_switcher_settings = $this->get_language_switcher_settings();
        if (!$plugin_language_switcher_settings) {
            return;
        }
        $bg_color       = $plugin_language_switcher_settings['backgroundColor'] ?? '';
        $bg_hover_color = $plugin_language_switcher_settings['hoverBackgroundColor'] ?? '';
?>
        <style class="clonable-language-switcher-style" type="text/css">
            /* Clonable Language Switcher Menu Styles */
            .clonable-menu-item-language-switcher .sub-menu {
                background-color: <?php echo esc_attr($bg_color); ?>;
            }

            .clonable-menu-item-language-switcher .sub-menu .cl-language-switcher-menu-item:hover {
                background-color: <?php echo esc_attr($bg_hover_color); ?> !important;
            }
        </style>
    <?php
    }

    /**
     * Appends the language switcher item to a specific menu (by menu slug).
     *
     * @param array    $plugin_language_switcher_settings Language switcher settings.
     * @param string   $items      The HTML list content for the menu items.
     * @param stdClass $menu_args  An object containing wp_nav_menu() arguments.
     * @param string   $context    Render context: 'traditional_menu', 'block_menu_submenu', or 'shortcode'.
     *
     * @return string
     */
    public function build_language_switcher(array $plugin_language_switcher_settings, $items, $menu_args, string $context): string
    {
        if (empty($plugin_language_switcher_settings)) {
            return $items;
        }

        $menu_languages = $this->build_menu_languages($plugin_language_switcher_settings);

        if (empty($menu_languages)) {
            return $items . $this->render_fallback_item();
        }

        $flag_dimensions_class = $this->get_flag_dimensions_class($plugin_language_switcher_settings);

        switch ($context) {
            case 'shortcode':
                //case 'traditional_menu':
                $html = $this->render_traditional_menu($plugin_language_switcher_settings, $menu_languages, $flag_dimensions_class, $context);
                break;

            case 'block_menu_submenu':
                $html = $this->render_block_submenu($plugin_language_switcher_settings, $menu_languages, $flag_dimensions_class);
                break;

            default:
                $html = '';
                break;
        }

        if ($html === '') {
            $html = $this->render_fallback_item();
        }

        return $items . $html;
    }

    /**
     * Returns the CSS class for flag dimensions based on settings.
     *
     * @param array $settings Language switcher settings.
     * @return string
     */
    private function get_flag_dimensions_class(array $settings): string
    {
        return !empty($settings['roundedFlag']) ? 'ff-rounded' : 'ff-rectangle';
    }

    /**
     * Returns the current request path.
     *
     * @return string
     */
    private function get_request_path(): string
    {
        return parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
    }

    /**
     * Builds the array of menu languages with translated URLs.
     *
     * @param array $settings Language switcher settings.
     * @return array
     */
    private function build_menu_languages(array $settings): array
    {
        $menu_languages = $this->get_language_switcher_languages();

        // Resolve display names from settings
        $languages_map = $this->parse_languages_setting($settings['languages'] ?? '[]');

        foreach ($menu_languages as &$lang) {
            // in case country code is enabled, we want to show the country code instead of the language name.
            // We can determine the country code by taking the display region of the locale code.
            // For example, if the locale code is en_US, we want to show US as the country code.
            if (!empty($settings['showCountry'])) {
                $lang['display_name'] = $lang['flag_code'];
                continue;
            }

            $lang['display_name'] = $languages_map[$lang['lang_code']] ?? $lang['lang_code'];
        }

        unset($lang);
        return $menu_languages;
    }

    /**
     * Parses the languages setting JSON into a lang_code => display_name map.
     *
     * @param mixed $languages_setting JSON string or array.
     * @return array<string, string>
     */
    private function parse_languages_setting($languages_setting): array
    {
        try {
            if (is_array($languages_setting)) {
                $json = json_encode($languages_setting);
            } else {
                $json = (string) $languages_setting;
            }

            $json = str_replace('U+002E', '.', $json);
            $arr  = json_decode($json, true);

            if (!is_array($arr)) {
                return [];
            }

            $map = [];
            foreach ($arr as $entry) {
                if (
                    is_array($entry)
                    && isset($entry['clonableLocaleCode'], $entry['clonableDisplayLanguage'])
                ) {
                    $map[(string) $entry['clonableLocaleCode']] = (string) $entry['clonableDisplayLanguage'];
                }
            }

            return $map;
        } catch (Throwable) {
            return [];
        }
    }

    /**
     * Renders a single language item's flag and name HTML fragments.
     *
     * @param array  $settings             Plugin language switcher settings.
     * @param array  $language             Single language entry.
     * @param string $flag_dimensions_class CSS class for flag dimensions.
     * @param string $name_class           CSS class for the name span.
     *
     * @return array{flag: string, name: string}
     */
    private function render_language_parts(array $settings, array $language, string $flag_dimensions_class, string $name_class = 'cl-language-switcher-lan-name'): array
    {
        $flag_html = '';
        $name_html = '';

        if (!empty($settings['showFlag'])) {
            $flag_code = esc_attr(strtoupper($language['flag_code']));
            $flag_html = '<i class="cl-language-switcher-lan-flag mr-2 fflag-' . $flag_code
                . ' fflag ' . esc_attr($flag_dimensions_class) . ' ff-md"></i>';
        }

        if (!empty($settings['showTargetLanguage'])) {
            $flag_html_safe = esc_html($language['display_name']);
            $name_html  = '<span class="' . esc_attr($name_class) . ' notranslate">' . $flag_html_safe . '</span>';
        }

        return ['flag' => $flag_html, 'name' => $name_html];
    }

    /**
     * Renders the chevron SVG icon.
     *
     * @param string $type 'up' for traditional menu, 'down' for block/shortcode.
     * @return string
     */
    private function render_chevron_svg(string $type = 'up'): string
    {
        if ($type === 'down') {
            return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" focusable="false">'
                . '<path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path>'
                . '</svg>';
        }

        return '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width: 1.25rem;" class="clonable-rotate">'
            . '<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5"></path>'
            . '</svg>';
    }

    /**
     * Renders the traditional/shortcode menu HTML.
     *
     * @param array  $settings             Language switcher settings.
     * @param array  $menu_languages       Array of language data.
     * @param string $flag_dimensions_class CSS class for flag shape.
     * @param string $context              'traditional_menu' or 'shortcode'.
     *
     * @return string
     */
    private function render_traditional_menu(array $settings, array $menu_languages, string $flag_dimensions_class, string $context): string
    {
        $li_classes   = esc_attr(self::MENU_ITEM_LI_CLASS . ' menu-item-has-children');
        $a_class      = esc_attr(self::MENU_ITEM_CLASS);
        $toggle_label = 'Languages';

        $html  = '<li data-loading class="' . $li_classes . '">';
        $html .= '<a href="#" rel="nofollow" role="button" class="' . $a_class . '"'
            . ' aria-haspopup="true" aria-expanded="false">'
            . $toggle_label
            . '</a>';

        $html .= '<ul class="sub-menu">';

        foreach ($menu_languages as $language) {

            $parts = $this->render_language_parts($settings, $language, $flag_dimensions_class);

            if ($parts['flag'] === '' && $parts['name'] === '') {
                continue;
            }

            $href         = esc_url($language['href'] ?? '#');
            $hreflang     = esc_attr($language['locale']);
            $href_encoded = isset($language['href']) ? esc_attr(base64_encode($language['href'])) : '';

            $html .= '<li class="menu-item cl-language-switcher-menu-item">'
                . '<a class="cl-language-switcher-link" href="' . $href . '"'
                . ' hreflang="' . $hreflang . '"'
                . ' data-href-encoded="' . $href_encoded . '">'
                . '<span class="cl-language-switcher-lan-container">'
                . $parts['flag']
                . $parts['name']
                . '</span></a>'
                . '</li>';
        }

        $html .= '</ul>';
        $html .= '</li>';

        return $html;
    }

    /**
     * Renders the block-based navigation submenu HTML.
     *
     * @param array  $settings             Language switcher settings.
     * @param array  $menu_languages       Array of language data.
     * @param string $flag_dimensions_class CSS class for flag shape.
     *
     * @return string
     */
    private function render_block_submenu(array $settings, array $menu_languages, string $flag_dimensions_class): string
    {
        $toggle_label = 'Languages';

        $wp_context = esc_attr(json_encode([
            'submenuOpenedBy' => ['click' => false, 'hover' => true, 'focus' => false],
            'type'            => 'submenu',
            'modal'           => null,
            'previousFocus'   => null,
        ], JSON_UNESCAPED_SLASHES));

        $html  = '<li'
            . ' data-wp-context=\'' . $wp_context . '\''
            . ' data-wp-interactive="core/navigation"'
            . ' data-wp-on--focusout="actions.handleMenuFocusout"'
            . ' data-wp-on--keydown="actions.handleMenuKeydown"'
            . ' data-wp-on--mouseenter="actions.openMenuOnHover"'
            . ' data-wp-on--mouseleave="actions.closeMenuOnHover"'
            . ' data-wp-watch="callbacks.initMenu"'
            . ' tabindex="-1"'
            . ' class="wp-block-navigation-item has-child open-on-hover-click wp-block-navigation-submenu clonable-language-switcher-block-submenu-item"'
            . ' data-loading>';

        $html .= '<a class="wp-block-navigation-item__content">'
            . '<span class="wp-block-navigation-item__label">' . esc_html($toggle_label) . '</span>'
            . '</a>';

        $html .= '<button'
            . ' data-wp-bind--aria-expanded="state.isMenuOpen"'
            . ' data-wp-on--click="actions.toggleMenuOnClick"'
            . ' aria-label="' . esc_attr($toggle_label . ' submenu') . '"'
            . ' class="wp-block-navigation__submenu-icon wp-block-navigation-submenu__toggle"'
            . ' aria-expanded="false">'
            . $this->render_chevron_svg('down')
            . '</button>';

        $html .= '<ul data-wp-on--focus="actions.openMenuOnFocus"'
            . ' class="wp-block-navigation__submenu-container wp-block-navigation-submenu clonable-language-switcher-block-submenu">';


        foreach ($menu_languages as $language) {
            $parts = $this->render_language_parts(
                $settings,
                $language,
                $flag_dimensions_class,
                'wp-block-navigation-item__label'
            );

            if ($parts['flag'] === '' && $parts['name'] === '') {
                continue;
            }

            $href         = esc_url($language['href'] ?? '#');
            $hreflang     = esc_attr($language['locale']);
            $href_encoded = isset($language['href']) ? esc_attr(base64_encode($language['href'])) : '';

            $html .= '<li class="wp-block-navigation-item wp-block-navigation-link">'
                . '<a class="wp-block-navigation-item__content cl-language-switcher-link"'
                . ' href="' . $href . '"'
                . ' hreflang="' . $hreflang . '"'
                . ' data-href-encoded="' . $href_encoded . '">'
                . $parts['flag']
                . $parts['name']
                . '</a>'
                . '</li>';
        }

        $html .= '</ul>';
        $html .= '</li>';

        return $html;
    }

    /**
     * Renders a fallback empty language switcher item.
     *
     * @return string
     */
    private function render_fallback_item(): string
    {
        return '<li class="' . esc_attr(self::MENU_ITEM_LI_CLASS) . '">'
            . '<div class="' . esc_attr(self::MENU_ITEM_CLASS) . '"></div>'
            . '</li>';
    }

    /**
     * Adds the language switcher to block-based navigation (core/navigation-submenu).
     *
     * @param string $block_content The rendered block content.
     * @param array  $block         The parsed block data.
     *
     * @return string
     */
    public function submenu_block_language_switcher(string $block_content, array $block): string
    {
        if ($block['blockName'] !== 'core/navigation-submenu') {
            return $block_content;
        }

        $attrs = $block['attrs'] ?? [];

        $block_class = $attrs['className'] ?? '';
        // Only render the language switcher in submenu blocks that have the 'clonable-language-switcher' class, to avoid affecting all submenu blocks and allow users to choose where to display the switcher. This also prevents potential issues with duplicate IDs or classes if multiple submenu blocks are used.
        if (!$block_class || strpos($block_class, 'clonable-language-switcher') === false) {
            return $block_content;
        }

        $plugin_language_switcher_settings = $this->get_language_switcher_settings();

        // Build the language switcher HTML using the same method, passing empty items and args
        $output = $this->build_language_switcher($plugin_language_switcher_settings, '', new stdClass(), 'block_menu_submenu');

        if (!$output) {
            return $block_content;
        }

        // replace the whole content of the submenu block with the switcher, without worrying about the existing content, as the submenu block is meant to only contain the language switcher in this case. This also avoids issues with trying to merge the switcher with existing content and potential duplicate IDs or classes. Make sure the block still functions correctly as a submenu with the necessary classes for styling and functionality.
        return $output;
    }

    /* ------------------------------------------------------------------ */
    /*  Native WordPress Menu Item (Appearance → Menus)                   */
    /* ------------------------------------------------------------------ */
    /*
     * Register a meta box on the WordPress Menus page (Appearance → Menus)
     * so users can add a Language Switcher as a regular, positionable menu item.
     */
    public function register_nav_menu_meta_box(): void
    {
        add_meta_box(
            'clonable-language-switcher',
            'Clonable Language Switcher',
            [$this, 'render_nav_menu_meta_box'],
            'nav-menus',
            'side',
            'default'
        );
    }

    /**
     * Render the meta box content that lets users add the language switcher to a menu.
     */
    public function render_nav_menu_meta_box(): void
    {
        global $_nav_menu_placeholder;
        $_nav_menu_placeholder = $_nav_menu_placeholder < -1 ? $_nav_menu_placeholder - 1 : -1;
    ?>
        <div id="clonable-language-switcher-div" class="posttypediv">
            <div id="tabs-panel-clonable-language-switcher" class="tabs-panel tabs-panel-active">
                <ul id="clonable-language-switcher-checklist" class="categorychecklist form-no-clear">
                    <li>
                        <label class="menu-item-title">
                            <input type="checkbox" class="menu-item-checkbox"
                                name="menu-item[<?php echo esc_attr($_nav_menu_placeholder); ?>][menu-item-object-id]"
                                value="-1"> <?php esc_html_e('Language Switcher'); ?>
                        </label>
                        <input type="hidden" class="menu-item-type"
                            name="menu-item[<?php echo esc_attr($_nav_menu_placeholder); ?>][menu-item-type]"
                            value="custom">
                        <input type="hidden" class="menu-item-title"
                            name="menu-item[<?php echo esc_attr($_nav_menu_placeholder); ?>][menu-item-title]"
                            value="<?php esc_attr_e('Language Switcher'); ?>">
                        <input type="hidden" class="menu-item-url"
                            name="menu-item[<?php echo esc_attr($_nav_menu_placeholder); ?>][menu-item-url]"
                            value="#clonable-language-switcher">
                        <input type="hidden" class="menu-item-classes"
                            name="menu-item[<?php echo esc_attr($_nav_menu_placeholder); ?>][menu-item-classes]"
                            value="clonable-language-switcher">
                    </li>
                </ul>
            </div>
            <p class="button-controls wp-clearfix">
                <span class="add-to-menu">
                    <input type="submit" class="button submit-add-to-menu right"
                        value="<?php esc_attr_e('Add to Menu'); ?>"
                        name="add-clonable-language-switcher-menu-item"
                        id="submit-clonable-language-switcher">
                </span>
                <span class="spinner"></span>
            </p>
        </div>
    <?php
    }

    /**
     * Detect the Clonable Language Switcher placeholder in the menu and
     * dynamically inject child menu items for each available language.
     *
     * Hooked into wp_nav_menu_objects so the theme's walker renders
     * the dropdown natively (the theme controls the sub-menu styling).
     *
     * @param array    $sorted_menu_items The menu items, sorted.
     * @param stdClass $args              An object containing wp_nav_menu() arguments.
     * @return array
     * @see https://developer.wordpress.org/reference/hooks/wp_nav_menu/
     * @see https://developer.wordpress.org/reference/hooks/wp_nav_menu_items/
     * @see https://developer.wordpress.org/reference/hooks/wp_nav_menu_objects/
     * @see https://developer.wordpress.org/reference/classes/walker_nav_menu/ for how the menu items are rendered after we inject our language items here.
     */
    public function inject_language_menu_items(array $sorted_menu_items, stdClass $args): array
    {
        // this should work for all menu locations since we're looking for the placeholder item, but we can also add additional checks here based on $args to only inject into specific menus if needed (similar to the checks in build_language_switcher)

        $switcher_item = null;

        foreach ($sorted_menu_items as $item) {
            if (
                $item->url === '#clonable-language-switcher' ||
                (is_array($item->classes) && in_array('clonable-language-switcher', $item->classes, true))
            ) {
                $switcher_item = $item;
                break;
            }
        }

        if ($switcher_item === null) {
            return $sorted_menu_items;
        }

        // Mark the parent so the theme's walker renders a sub-menu container
        $switcher_item->classes[] = 'menu-item-has-children';
        $switcher_item->classes[] = 'clonable-menu-item-language-switcher';
        $switcher_item->url       = '#';


        $plugin_language_switcher_settings = $this->get_language_switcher_settings();

        $menu_languages = $this->build_menu_languages($plugin_language_switcher_settings);

        if (empty($menu_languages)) {
            return $sorted_menu_items;
        }

        $counter = 900000; // high ID range to avoid collisions

        foreach ($menu_languages as $menu_language) {
            $display_name = $menu_language['display_name'] ?? $menu_language['lang_code'];
            $child                        = new stdClass();
            $child->ID                    = $counter;
            $child->db_id                 = $counter;
            $child->menu_item_parent      = $switcher_item->ID;
            $child->object_id             = $counter;
            $child->object                = 'custom';
            $child->type                  = 'custom';
            $child->type_label            = ''; // can be customized if needed, it adds a label in the admin menu editor, but since this is a dynamic item we can leave it empty or set it to something like 'Language Switcher Item'
            $child->title                 = $display_name;
            $child->url                   = $menu_language['href'] ?? '#';
            $child->target                = '_self';  // can be customized if needed, e.g. '_blank' to open in new tab
            $child->attr_title            = $display_name;
            $child->description           = ''; // adds a title attribute to the link with the display name, can be customized if needed
            $child->classes               = ['menu-item', 'cl-language-switcher-menu-item', 'clonable-language-item'];
            $child->xfn                   = ''; // can be customized if needed, it adds a rel attribute to the link, e.g. rel="alternate" for language switcher items, but some themes may use it for other purposes so leaving it empty for now
            $child->current               = false;
            $child->current_item_ancestor = false;
            $child->current_item_parent   = $switcher_item->ID;
            $child->menu_order            = $counter;
            $child->post_type             = 'nav_menu_item';
            $child->post_status           = 'publish'; // the menu editor only shows items with 'publish' status, but since these are dynamic items we can set it to whatever, it won't affect the menu editor since these items don't exist in the database, but setting it to 'publish' can help with debugging and compatibility with themes that check for post_status when rendering menu items, even though it's not strictly necessary since these items are not stored in the database

            // Custom properties consumed by customize_language_menu_item_output()

            $child->clonable_lang_code    = $menu_language['lang_code'];
            $child->clonable_flag_code    = $menu_language['flag_code'];
            $child->clonable_locale       = $menu_language['locale'] ?? str_replace('_', '-', $menu_language['lang_code']);
            $child->clonable_href_encoded = isset($menu_language['href']) ? base64_encode($menu_language['href']) : '';

            $sorted_menu_items[] = $child;
            $counter++;
        }

        return $sorted_menu_items;
    }

    /**
     * Customize the HTML output of injected language child menu items
     * to include flag icons, hreflang attributes, and proper styling classes.
     *
     * @param string   $item_output The menu item's starting HTML output.
     * @param object   $menu_item   Menu item data object.
     * @param int      $depth       Depth of menu item.
     * @param stdClass $args        An object of wp_nav_menu() arguments.
     * @return string
     */
    public function customize_language_menu_item_output(string $item_output, $menu_item, int $depth, $args): string
    {
        if (!isset($menu_item->clonable_lang_code)) {
            return $item_output;
        }

        $settings = $this->get_language_switcher_settings();
        $flag_dimensions_class = $this->get_flag_dimensions_class($settings);

        $language = [
            'flag_code'    => $menu_item->clonable_flag_code,
            'lang_code'    => $menu_item->clonable_lang_code,
            'display_name' => $menu_item->title,
        ];

        $parts = $this->render_language_parts($settings, $language, $flag_dimensions_class);

        $link_html = '<a class="cl-language-switcher-link" href="' . esc_url($menu_item->url)
            . '" hreflang="' . esc_attr($menu_item->clonable_locale)
            . '" data-href-encoded="' . esc_attr($menu_item->clonable_href_encoded)
            . '"><span class="cl-language-switcher-lan-container">'
            . $parts['flag']
            . $parts['name']
            . '</span></a>';

        // Preserve the existing $args->before / $args->after wrapping if available
        $before = '';
        $after  = '';
        if (is_object($args)) {
            $before = $args->before ?? '';
            $after  = $args->after ?? '';
        }

        return $before . $link_html . $after;
    }

    /**
     * Build the array of available languages with translated URLs.
     *
     * @return array Each element contains lang_code, locale, flag_code, and href.
     */
    private function get_language_switcher_languages(): array
    {
        $request_path      = $this->get_request_path();
        $menu_languages    = [];
        $site              = ClonableConfig::get_site();
        $urls_to_translate = [];

        if (!$site) {
            return [];
        }

        $clones = $site->get_clones();
        $locally_set_languages = $this->get_locally_set_languages();

        if ($clones) {
            foreach ($clones as $index => $clone) {

                $href = rtrim($site->get_clone_url($index), '/') . $request_path;

                $urls_to_translate[] = ['o' => $href];

                $menu_languages[$index] = [
                    'lang_code' => $clone['lang_code'],
                    'locale'    => str_replace('_', '-', $clone['lang_code']),
                    'flag_code' => Locales::get_region($clone['lang_code']),
                    'href'      => $href, // default, overwritten if translation succeeds
                ];
            }
        }

        if ($locally_set_languages) {
            foreach ($locally_set_languages as $local_lang) {

                // Avoid duplicates if the locally set language is already included from the clones (matching by lang_code)
                if (array_search($local_lang->clonableLocaleCode, array_column($menu_languages, 'lang_code')) !== false) {
                    continue;
                }

                $href = rtrim($local_lang->clonableUrl, '/') . $request_path; // default to site URL + request path, translation will be attempted in bulk later

                $urls_to_translate[] = ['o' => $href];

                $menu_languages[] = [
                    'lang_code' => $local_lang->clonableLocaleCode,
                    'locale'    => str_replace('_', '-', $local_lang->clonableLocaleCode),
                    'flag_code' => Locales::get_region($local_lang->clonableLocaleCode),
                    'href'      => $href, // default, overwritten if translation succeeds
                ];
            }
        }

        // Translate URLs in bulk
        if ($urls_to_translate) {
            try {
                $url_translator    = new UrlTranslator();
                $translate_results = $url_translator->clonable_get_translated_urls($urls_to_translate);

                if ($translate_results) {
                    foreach ($translate_results as $idx => $result) {
                        if (isset($result['r'], $menu_languages[$idx])) {
                            $menu_languages[$idx]['href'] = $result['r'];
                        }
                    }
                }
            } catch (Throwable) {
                // Silently fail; default URLs are already set
            }
        }

        // Append the default/source language, if not set yet in Clonable languages yet. Check based on url
        $source_href = rtrim($site->get_url(), '/') . $request_path;
        $normalized_source_href = untrailingslashit($source_href);

        $source_url_exists = false;
        foreach ($menu_languages as $language) {
            if (!isset($language['href'])) {
                continue;
            }

            if (untrailingslashit((string) $language['href']) === $normalized_source_href) {
                $source_url_exists = true;
                break;
            }
        }

        if (!$source_url_exists) {
            $menu_languages[] = [
                'lang_code' => $site->get_locale(),
                'locale'    => str_replace('_', '-', $site->get_locale()),
                'flag_code' => Locales::get_region($site->get_locale()),
                'href'      => $source_href,
            ];
        }

        return $menu_languages;
    }

    public function get_locally_set_languages(): array
    {
        $languages = get_option('clonable_langswitch_data', []);
        if ($languages) {
            $languages = json_decode($languages);
            return $languages;
        }
        return [];
    }

    /**
     * Build settings passed to the front-end.
     * Uses menu slug instead of theme location.
     * Includes legacy menuLocation for backward compatibility if needed.
     * @return array
     */
    private function get_language_switcher_settings(): array
    {
        return [
            'showFlag'             => (get_option('clonable_show_flag', 'on') === 'on') ? 1 : 0,
            'roundedFlag'          => (get_option('clonable_rounded_flag', 'on') === 'on') ? 1 : 0,
            'showTargetLanguage'   => (get_option('clonable_show_text', 'on') === 'on') ? 1 : 0,
            'showCountry'          => (get_option('clonable_show_country_code', 'off') === 'on') ? 1 : 0,
            'backgroundColor'      => get_option('clonable_background_color', '#ffffff'),
            'hoverBackgroundColor' => get_option('clonable_hover_background_color', '#efefef'),
            'languages'            => get_option('clonable_langswitch_data', []),
        ];
    }

    public function print_language_switcher()
    {
        $build_language_switcher = get_option('clonable_enable_language_switcher', 'off') === 'on';

        if ($build_language_switcher) {
            $endpoint = ClonableConfig::MODULES_ENDPOINT;
            echo "<!-- Start Clonable Language Switcher -->\n";
            echo "<script>\n";
            $config = $this->create_clonable_config();
            echo ent2ncr($config);
            echo "</script>\n";
            echo "<script defer src=\"https://" . esc_attr($endpoint) . "/language-switcher/js/init.js?v=1.2.0\"></script>\n";
            echo "<!-- End Clonable Language Switcher -->\n";
        }
    }

    public function language_switcher_shortcode($items = '', $menu_args = ''): string
    {
        $plugin_language_switcher_settings = $this->get_language_switcher_settings();

        if (empty($plugin_language_switcher_settings)) {
            error_log('Clonable Language Switcher: Missing settings, cannot render shortcode.');
            return '';
        }

        // if showFlags and showTargetLanguage are both disabled, don't render the switcher and return early to avoid rendering empty language switcher containers
        if (!(bool)$plugin_language_switcher_settings['showFlag'] && !(bool)$plugin_language_switcher_settings['showTargetLanguage']) {
            $wp_admin_url = admin_url('admin.php?page=clonable&tab=language-switcher');
            return '<small>Check your <a target="_blank" href="' . $wp_admin_url . '">Clonable Language Switcher settings</a> to display the language switcher.</p>';
        }

        $submenu = $this->build_language_switcher(
            $plugin_language_switcher_settings,
            is_array($items) ? implode('', $items) : $items,
            $menu_args,
            'shortcode'
        );

        ob_start();
    ?>
        <!-- Start Clonable Language Switcher -->
        <div data-loading class="clonable-language-navigation clonable-language-switcher-shortcode">
            <ul id="menu-navlinks" class="menu nav-menu" aria-label="Language Switcher">
                <?php echo $submenu; ?>
            </ul>
        </div>
        <!-- End Clonable Language Switcher -->
<?php
        return ob_get_clean() ?: '';
    }

    private function create_clonable_config()
    {
        // Display information
        $show_flag            = (get_option('clonable_show_flag', 'on') === 'on') ? 1 : 0;
        $rounded_flag         = (get_option('clonable_rounded_flag', 'on') === 'on') ? 1 : 0;
        $show_target_language = (get_option('clonable_show_text', 'on') === 'on') ? 1 : 0;
        // Colors
        $background_color       = get_option('clonable_background_color', '#ffffff');
        $hover_background_color = get_option('clonable_hover_background_color', '#efefef');
        // Placement and size
        $size     = get_option('clonable_size', 'md');
        $position = get_option('clonable_position', 'bottom-left');
        $languages = get_option('clonable_langswitch_data', []);
        $languages = str_replace('.', 'U+002E', $languages);

        return "
            const clonableLSC = {
                'backgroundColor': '$background_color',
                'backgroundHoverColor': '$hover_background_color',
                'textColor': '#000000',
                'menuTextColor': '#000000',
                'hasRoundedFlags': ($rounded_flag === 1),
                'showFlag': ($show_flag === 1),
                'showName': ($show_target_language === 1),
                'size': '$size',
                'position': '$position',
                'isStatic': true,
                'placement': 'before',
                'queryInput': '',
                'hasHoverTrigger': true,
                'hasClickTrigger': true,
                'languages': $languages
            }
        ";
    }
}
