<?php
namespace DevOwl\RealCookieBanner\view\navmenu;

use DevOwl\Multilingual\NavMenuList;
use DevOwl\RealCookieBanner\base\UtilsProvider;
use DevOwl\RealCookieBanner\comp\language\Hooks;
use DevOwl\RealCookieBanner\Core;
use DevOwl\RealCookieBanner\view\Checklist;
use DevOwl\RealCookieBanner\view\checklist\Shortcode;
use Walker_Nav_Menu_Checklist;
use WP_Error;
use WP_Post;

// @codeCoverageIgnoreStart
defined('ABSPATH') or die('No script kiddies please!'); // Avoid direct file request
// @codeCoverageIgnoreEnd

/**
 * Custom navigation menu links for the legal links.
 */
class NavMenuLinks {
    use UtilsProvider;

    const METABOX_ID = 'rcb-nav-menu';

    const META_SUCCESS_MESSAGE_FIELD_ID = 'rcb-menu-item-success-message';
    const META_SUCCESS_MESSAGE_META_NAME = 'rcb-success-message';

    const NAVIGATION_ITEM_ID = 'rcb-nav-item';

    /**
     * C'tor.
     */
    private function __construct() {
        // Silence is golden.
    }

    /**
     * Get a list of available navigation menus so we can add a automatic "addition" process to our "Legal links" section.
     *
     * @param array $arr
     */
    public function revisionCurrent($arr) {
        $arr['nav_menus'] = (new NavMenuList(Core::getInstance()->getCompLanguage()))->get([$this, 'isMenuItem']);
        return $arr;
    }

    /**
     * Add existing links to our current menu.
     *
     * @param string $nav_menu_id
     * @return true|WP_Error
     */
    public function addLinksToMenu($nav_menu_id) {
        $compLanguage = Core::getInstance()->getCompLanguage();
        $nav_menu = (new NavMenuList($compLanguage))->getById($nav_menu_id, [$this, 'isMenuItem']);

        if ($nav_menu !== null) {
            $useLanguage = count($nav_menu['languages']) > 0 ? $nav_menu['languages'][0]['code'] : null;
            $linkElements = $this->getLinkElements(false, $useLanguage);
            $result = true;
            $fnInsert = function () use ($linkElements, $nav_menu, &$result) {
                foreach ($linkElements as $url => $element) {
                    $added = wp_update_nav_menu_item($nav_menu['term_id'], 0, [
                        'menu-item-title' => $element['linkText'],
                        'menu-item-url' => $url,
                        'menu-item-status' => 'publish',
                    ]);

                    if (is_wp_error($added)) {
                        $result = $added;
                        break;
                    } elseif (isset($element['meta'])) {
                        foreach ($element['meta'] as $key => $val) {
                            update_post_meta($added, $key, $val);
                        }
                    }
                }
            };

            if ($compLanguage !== null && $compLanguage->isActive()) {
                $compLanguage->switchToLanguage($useLanguage, $fnInsert);
            } else {
                $fnInsert();
            }

            Checklist::getInstance()->toggle(Shortcode::IDENTIFIER, true);
            return $result;
        } else {
            return new WP_Error(
                'rcb_nav_menu_non_existing',
                __('The navigation menu could not be found.', 'real-cookie-banner')
            );
        }
    }

    /**
     * Get the available link elements.
     *
     * @param boolean $onlyLinks
     * @param string $language
     */
    public function getLinkElements($onlyLinks = false, $language = null) {
        if ($onlyLinks) {
            return ['#consent-change', '#consent-history', '#consent-revoke'];
        }

        $td = Hooks::getInstance()->createTemporaryTextDomain();

        $result = [];
        $fnCalculateResult = function ($temporaryTextDomain) use (&$result) {
            $targetLanguageTexts = $temporaryTextDomain->translate(function () {
                return [
                    _x('Change privacy settings', 'legal-text', 'real-cookie-banner'),
                    _x('Privacy settings history', 'legal-text', 'real-cookie-banner'),
                    _x('Revoke consents', 'legal-text', 'real-cookie-banner'),
                    _x(
                        'You have successfully revoked consent for services with its cookies and personal data processing. The page will be reloaded now!',
                        'legal-text',
                        'real-cookie-banner'
                    ),
                ];
            });

            $result = [
                '#consent-change' => [
                    'label' => _x('Change privacy settings', 'legal-text', 'real-cookie-banner'),
                    'linkText' => $targetLanguageTexts[0],
                ],
                '#consent-history' => [
                    'label' => _x('Privacy settings history', 'legal-text', 'real-cookie-banner'),
                    'linkText' => $targetLanguageTexts[1],
                ],
                '#consent-revoke' => [
                    'label' => _x('Revoke consents', 'legal-text', 'real-cookie-banner'),
                    'linkText' => $targetLanguageTexts[2],
                    'meta' => [
                        self::META_SUCCESS_MESSAGE_META_NAME => $targetLanguageTexts[3],
                    ],
                ],
            ];
        };

        if ($language !== null) {
            $compLanguage = Core::getInstance()->getCompLanguage();
            $compLanguage->switchToLanguage($language, function ($locale, $currentLanguage, $temporaryTextDomain) use (
                $fnCalculateResult,
                $td
            ) {
                $fnCalculateResult($temporaryTextDomain ?? $td);
            });
        } else {
            $fnCalculateResult($td);
        }

        $td->teardown();
        return $result;
    }

    /**
     * Add custom meta fields to the nav menu field.
     *
     * @param int $item_id
     * @param object $item
     */
    public function wp_nav_menu_item_custom_fields($item_id, $item) {
        if ($this->isMenuItem($item) && $item->url === '#consent-revoke') {
            $compLanguage = Core::getInstance()->getCompLanguage();
            $useLanguage =
                $compLanguage !== null && $compLanguage->isActive()
                    ? $compLanguage->getCurrentLanguageFallback()
                    : null;
            $linkElements = $this->getLinkElements(false, $useLanguage);
            $linkElement = $linkElements[$item->url];

            // Insert default values for newly created items
            if (!metadata_exists('post', $item_id, self::META_SUCCESS_MESSAGE_META_NAME)) {
                foreach ($linkElement['meta'] as $key => $val) {
                    update_post_meta($item_id, $key, $val);
                }
            }

            $successMessage = get_post_meta($item_id, self::META_SUCCESS_MESSAGE_META_NAME, true);

            echo sprintf(
                '<p class="description description-wide">
        <label for="%4$s-%1$d" >
            %2$s<br />
            <input type="text" class="widefat" id="%4$s-%1$d" 
                name="%4$s[%1$d]" value="%3$s"
            />
        </label>
    </p>',
                (int) $item_id,
                esc_html__('Success message', 'real-cookie-banner'),
                esc_attr(is_scalar($successMessage) ? (string) $successMessage : ''),
                esc_attr(self::META_SUCCESS_MESSAGE_FIELD_ID)
            );
        }
    }

    /**
     * Save custom meta fields.
     *
     * @param int $menu_id
     * @param int $menu_item_db_id
     * @param array $args
     */
    public function wp_update_nav_menu_item($menu_id, $menu_item_db_id, $args) {
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- navigation menu screen verifies nonce before save hooks fire.
        $successMessage = isset($_POST[self::META_SUCCESS_MESSAGE_FIELD_ID][$menu_item_db_id])
            ? sanitize_text_field(wp_unslash($_POST[self::META_SUCCESS_MESSAGE_FIELD_ID][$menu_item_db_id]))
            : null;
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        if ($successMessage !== null) {
            update_post_meta($menu_item_db_id, self::META_SUCCESS_MESSAGE_META_NAME, $successMessage);
        }

        if (isset($args['menu-item-url']) && in_array($args['menu-item-url'], $this->getLinkElements(true), true)) {
            Checklist::getInstance()->toggle(Shortcode::IDENTIFIER, true);
        }
    }

    /**
     * Add the custom meta fields to the output navigation element.
     *
     * @param array $atts
     * @param WP_Post $item
     */
    public function nav_menu_link_attributes($atts, $item) {
        if ($this->isMenuItem($item)) {
            if ($item->url === '#consent-revoke') {
                $atts['data-success-message'] = get_post_meta($item->ID, self::META_SUCCESS_MESSAGE_META_NAME, true);
            }

            $atts['role'] = 'button';
        }
        return $atts;
    }

    /**
     * Accessibility: Broken same-page link -> we need to add a dummy element with the ID and the rest is done via JavaScript.
     *
     * @param string $item_output
     * @param WP_Post $item
     */
    public function walker_nav_menu_start_el($item_output, $item) {
        if ($this->isMenuItem($item)) {
            $item_output = str_replace(
                '</a>',
                sprintf(
                    '<span id="%s" aria-hidden="true" %s></span></a>',
                    substr($item->url, 1),
                    join(' ', [
                        // [Plugin Comp] Colibri and `spyScroll`
                        'skip-scroll-spy="1"',
                    ])
                ),
                $item_output
            );
        }
        return $item_output;
    }

    /**
     * Register new metabox.
     */
    public function admin_head() {
        add_meta_box(
            self::METABOX_ID,
            'Real Cookie Banner',
            [$this, 'nav_menu_metabox'],
            'nav-menus',
            'side',
            'default'
        );

        // Create a script to automatically hide URL from the menu item
        echo '<script>
    jQuery(document).ready(function($) {
        const fnHide = function() {
            $(\'.edit-menu-item-url[value^="#consent"]\').parents(".field-url").hide();
        };
        fnHide();
        setInterval(fnHide, 1000);
    });
</script>';
    }

    /**
     * Output script to hide the "URL" input field in customizer.
     */
    public function customize_controls_head() {
        echo '<script>
    jQuery(document).ready(function($) {
        const fnHide = function() {
            $("input.edit-menu-item-url").filter(function() {
                return $(this).val().startsWith("#consent");
            }).parents("label").hide();
        };
        fnHide();
        setInterval(fnHide, 1000);
    });
</script>';
    }

    /**
     * Print the metabox.
     */
    public function nav_menu_metabox() {
        global $nav_menu_selected_id;

        $nav_items = [];
        foreach ($this->getLinkElements() as $value => $element) {
            $item = new NavMenuLinkItem();
            $item->object_id = esc_html($value);
            $item->ID = $item->object_id;
            $item->title = esc_html($element['linkText']);
            $item->url = esc_html($value);
            $nav_items[$element['label']] = $item;
        }

        $walker = new Walker_Nav_Menu_Checklist([]);

        echo sprintf(
            '<div id="rcb-links" class="rcblinksdiv">
    <div id="tabs-panel-rcb-links-all" class="tabs-panel tabs-panel-view-all tabs-panel-active">
        <ul id="rcb-linkschecklist" class="list:rcb-links categorychecklist form-no-clear">%s</ul>
    </div>
    <div class="button-controls">
        <span class="add-to-menu">
            <input type="submit" %s class="button-secondary submit-add-to-menu right" value="%s" name="add-rcb-links-menu-item" id="submit-rcb-links"/>
            <span class="spinner"></span>
       </span>
    </div>
</div>',
            walk_nav_menu_tree(array_map('wp_setup_nav_menu_item', $nav_items), 0, (object) ['walker' => $walker]),
            disabled($nav_menu_selected_id, 0),
            esc_attr(__('Add to Menu', 'real-cookie-banner'))
        );
    }

    /**
     * Correct "Custom link" to "Real Cookie Banner" type label.
     *
     * @param WP_Post $menu_item
     */
    public function wp_setup_nav_menu_item($menu_item) {
        if ($this->isMenuItem($menu_item)) {
            $menu_item->type_label = 'Real Cookie Banner';
        }

        return $menu_item;
    }

    /**
     * Register custom item types in customizer.
     *
     * @param array $item_types Menu item types.
     */
    public function register_customize_nav_menu_item_types($item_types) {
        $item_types[] = [
            'title' => 'Real Cookie Banner',
            'type_label' => 'Real Cookie Banner',
            'type' => self::METABOX_ID,
            'object' => self::NAVIGATION_ITEM_ID,
        ];

        return $item_types;
    }

    /**
     * Register custom item type links in customizer.
     *
     * @param array $items
     * @param string $type
     * @param string $object
     * @param integer $page
     */
    public function register_customize_nav_menu_items($items = [], $type = '', $object = '', $page = 0) {
        if (self::NAVIGATION_ITEM_ID !== $object) {
            return $items;
        }

        if (0 < $page) {
            return $items;
        }

        foreach ($this->getLinkElements() as $id => $element) {
            $items[] = [
                'id' => str_replace('#', '', $id),
                'title' => $element['linkText'],
                'type_label' => 'Real Cookie Banner',
                'url' => $id,
            ];
        }

        return $items;
    }

    /**
     * Check if a given object of a menu item is a `NavMenuLinkItem`.
     *
     * @param WP_Post $menu_item
     */
    public function isMenuItem($menu_item) {
        return isset($menu_item->object, $menu_item->url) &&
            $menu_item->object === 'custom' &&
            in_array($menu_item->url, $this->getLinkElements(true), true);
    }

    /**
     * New instance.
     *
     * @codeCoverageIgnore
     */
    public static function instance() {
        return new NavMenuLinks();
    }
}
