<?php

/**
 * MemberShortcodes Hooks
 *
 * Registers and handles membership/account shortcodes:
 * [arraysubs_login], [arraysubs_logout], [arraysubs_user],
 * and [arraysubs_visibility].
 *
 * @package ArraySubs\Features\MemberShortcodes\Services
 * @since 1.9.0
 */

namespace ArraySubs\Features\MemberShortcodes\Services;

defined('ABSPATH') || exit;

/**
 * Hooks class for member shortcodes.
 */
class Hooks
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Constructor - register shortcodes on init.
     */
    public function __construct()
    {
        add_action('init', [$this, 'registerShortcodes']);
        add_filter('arraysubs_shortcode_registry', [$this, 'addShortcodeRegistry']);
    }

    /**
     * Add member shortcode definitions to the shared registry.
     *
     * @param array $shortcodes Existing shortcode registry.
     * @return array
     */
    public function addShortcodeRegistry(array $shortcodes): array
    {
        $shortcodes[] = [
            'tag' => 'arraysubs_login',
            'title' => __('Login Link', 'arraysubs'),
            'description' => __('Show a login link only to logged-out visitors.', 'arraysubs'),
            'availability' => 'free',
            'group' => __('Account', 'arraysubs'),
            'usage' => '[arraysubs_login text="Sign In"]',
            'examples' => [
                '[arraysubs_login]',
                '[arraysubs_login text="Sign In" redirect="https://example.com/my-account/"]',
            ],
            'attributes' => [
                ['name' => 'text', 'description' => __('Custom link text. If omitted, enclosed shortcode content can be used.', 'arraysubs')],
                ['name' => 'url', 'description' => __('Custom login URL. Defaults to the WordPress login URL.', 'arraysubs')],
                ['name' => 'redirect', 'description' => __('Redirect destination after login. Defaults to the current page.', 'arraysubs')],
                ['name' => 'class', 'description' => __('Optional CSS classes for the link element.', 'arraysubs')],
            ],
        ];

        $shortcodes[] = [
            'tag' => 'arraysubs_logout',
            'title' => __('Logout Link', 'arraysubs'),
            'description' => __('Show a logout link only to logged-in users.', 'arraysubs'),
            'availability' => 'free',
            'group' => __('Account', 'arraysubs'),
            'usage' => '[arraysubs_logout text="Sign Out"]',
            'examples' => [
                '[arraysubs_logout]',
                '[arraysubs_logout text="Sign Out" redirect="https://example.com/"]',
            ],
            'attributes' => [
                ['name' => 'text', 'description' => __('Custom link text. If omitted, enclosed shortcode content can be used.', 'arraysubs')],
                ['name' => 'redirect', 'description' => __('Redirect destination after logout. Defaults to the site home page.', 'arraysubs')],
                ['name' => 'class', 'description' => __('Optional CSS classes for the link element.', 'arraysubs')],
            ],
        ];

        $shortcodes[] = [
            'tag' => 'arraysubs_user',
            'title' => __('Current User Name', 'arraysubs'),
            'description' => __('Output the current logged-in user\'s name, username, or profile fields.', 'arraysubs'),
            'availability' => 'free',
            'group' => __('Account', 'arraysubs'),
            'usage' => '[arraysubs_user field="full_name" fallback="Guest"]',
            'examples' => [
                '[arraysubs_user]',
                '[arraysubs_user field="username"]',
                '[arraysubs_user field="full_name" fallback="Guest"]',
            ],
            'attributes' => [
                ['name' => 'field', 'description' => __('Name field to render: display_name, username, first_name, last_name, or full_name.', 'arraysubs')],
                ['name' => 'fallback', 'description' => __('Text to display when no user is logged in.', 'arraysubs')],
            ],
        ];

        $shortcodes[] = [
            'tag' => 'arraysubs_visibility',
            'title' => __('Visibility Wrapper', 'arraysubs'),
            'description' => __('Show or hide wrapped content based on whether the visitor is logged in or logged out.', 'arraysubs'),
            'availability' => 'free',
            'group' => __('Membership', 'arraysubs'),
            'usage' => '[arraysubs_visibility show="logged_in"]Protected content[/arraysubs_visibility]',
            'examples' => [
                '[arraysubs_visibility show="logged_in"]Welcome back![/arraysubs_visibility]',
                '[arraysubs_visibility show="logged_out" fallback="Please sign in first."][arraysubs_login text="Log In"][/arraysubs_visibility]',
            ],
            'attributes' => [
                ['name' => 'show', 'description' => __('Visibility condition: logged_in or logged_out.', 'arraysubs')],
                ['name' => 'fallback', 'description' => __('Optional content or shortcode output to show when the condition does not match.', 'arraysubs')],
            ],
        ];

        return $shortcodes;
    }

    /**
     * Register all member shortcodes.
     *
     * @return void
     */
    public function registerShortcodes(): void
    {
        add_shortcode('arraysubs_login', [$this, 'loginShortcode']);
        add_shortcode('arraysubs_logout', [$this, 'logoutShortcode']);
        add_shortcode('arraysubs_user', [$this, 'userShortcode']);
        add_shortcode('arraysubs_visibility', [$this, 'visibilityShortcode']);
    }

    /**
     * [arraysubs_login] – Login link for logged-out users.
     *
     * Attributes:
     *   text     – Link text (default: "Log In")
     *   url      – Custom login URL (default: wp_login_url)
     *   redirect – Redirect URL after login (default: current page)
     *   class    – Extra CSS class(es) on the <a> tag
     *
     * @param array|string $atts    Shortcode attributes.
     * @param string|null  $content Inner content (unused).
     * @return string HTML output or empty string.
     */
    public function loginShortcode($atts, $content = null): string
    {
        if (is_user_logged_in()) {
            return '';
        }

        $content = is_string($content) ? trim($content) : '';

        $atts = shortcode_atts(
            [
                'text'     => '',
                'url'      => '',
                'redirect' => '',
                'class'    => '',
            ],
            is_array($atts) ? $atts : [],
            'arraysubs_login'
        );

        $text = '' !== $atts['text']
            ? $atts['text']
            : ('' !== $content ? $content : __('Log In', 'arraysubs'));

        $redirect = !empty($atts['redirect'])
            ? esc_url_raw($atts['redirect'])
            : $this->getCurrentPageUrl();

        $login_url = !empty($atts['url'])
            ? esc_url_raw($atts['url'])
            : wp_login_url($redirect);

        return sprintf(
            '<a href="%s" class="%s">%s</a>',
            esc_url($login_url),
            esc_attr($this->buildCssClassString('arraysubs-login-link', $atts['class'])),
            esc_html($text)
        );
    }

    /**
     * [arraysubs_logout] – Logout link for logged-in users.
     *
     * Attributes:
     *   text     – Link text (default: "Log Out")
     *   redirect – Redirect URL after logout (default: home)
     *   class    – Extra CSS class(es) on the <a> tag
     *
     * @param array|string $atts    Shortcode attributes.
     * @param string|null  $content Inner content (unused).
     * @return string HTML output or empty string.
     */
    public function logoutShortcode($atts, $content = null): string
    {
        if (!is_user_logged_in()) {
            return '';
        }

        $content = is_string($content) ? trim($content) : '';

        $atts = shortcode_atts(
            [
                'text'     => '',
                'redirect' => '',
                'class'    => '',
            ],
            is_array($atts) ? $atts : [],
            'arraysubs_logout'
        );

        $text = '' !== $atts['text']
            ? $atts['text']
            : ('' !== $content ? $content : __('Log Out', 'arraysubs'));

        $redirect = !empty($atts['redirect'])
            ? esc_url_raw($atts['redirect'])
            : home_url('/');

        $logout_url = wp_logout_url($redirect);

        return sprintf(
            '<a href="%s" class="%s">%s</a>',
            esc_url($logout_url),
            esc_attr($this->buildCssClassString('arraysubs-logout-link', $atts['class'])),
            esc_html($text)
        );
    }

    /**
     * [arraysubs_user] – Output the current user's name.
     *
     * Attributes:
     *   field    – Which name to show: "display_name" (default),
     *              "username", "first_name", "last_name", "full_name"
     *   fallback – Text to display when no user is logged in
     *
     * @param array|string $atts    Shortcode attributes.
     * @param string|null  $content Inner content (unused).
     * @return string Escaped name or fallback.
     */
    public function userShortcode($atts, $content = null): string
    {
        $atts = shortcode_atts(
            [
                'field'    => 'display_name',
                'fallback' => '',
            ],
            is_array($atts) ? $atts : [],
            'arraysubs_user'
        );

        if (!is_user_logged_in()) {
            return esc_html($atts['fallback']);
        }

        $user = wp_get_current_user();

        $allowed_fields = [
            'display_name',
            'username',
            'first_name',
            'last_name',
            'full_name',
        ];

        $field = sanitize_key($atts['field']);
        $field = in_array($field, $allowed_fields, true) ? $field : 'display_name';

        return esc_html($this->getUserField($user, $field));
    }

    /**
     * [arraysubs_visibility] – Show or hide inner content based on login state.
     *
     * Attributes:
     *   show – Visibility condition: "logged_in" or "logged_out"
     *
     * @param array|string $atts    Shortcode attributes.
     * @param string|null  $content Inner content to show or hide.
     * @return string Processed content or empty string.
     */
    public function visibilityShortcode($atts, $content = null): string
    {
        $atts = shortcode_atts(
            [
                'show'        => 'logged_in',
                'fallback'    => '',
                'prerendered' => '', // Internal: content already shortcode-expanded (skip re-running do_shortcode).
            ],
            is_array($atts) ? $atts : [],
            'arraysubs_visibility'
        );

        $content = is_string($content) ? $content : '';
        $fallback = is_string($atts['fallback']) ? $atts['fallback'] : '';

        $show = str_replace('-', '_', sanitize_key($atts['show']));

        $should_show = false;

        switch ($show) {
            case 'logged_in':
                $should_show = is_user_logged_in();
                break;

            case 'logged_out':
                $should_show = !is_user_logged_in();
                break;
        }

        /**
         * Filter whether the visibility shortcode should display its content.
         *
         * Allows extending the visibility conditions beyond login state
         * (e.g., subscription status, membership level) without changing
         * the shortcode contract.
         *
         * @since 1.9.0
         *
         * @param bool   $should_show Whether to show the content.
         * @param string $show        The visibility condition requested.
         * @param array  $atts        All shortcode attributes.
         */
        $should_show = apply_filters('arraysubs_visibility_shortcode_show', $should_show, $show, $atts);

        if (!$should_show) {
            // Fallback is raw text (not pre-rendered), so always expand it.
            return '' !== $fallback ? do_shortcode($fallback) : '';
        }

        // Skip the second shortcode pass when the caller pre-rendered the content.
        return ($atts['prerendered'] === 'true') ? $content : do_shortcode($content);
    }

    /**
     * Resolve the requested field from a WP_User object.
     *
     * @param \WP_User $user  Current user.
     * @param string   $field Requested field name.
     * @return string
     */
    private function getUserField(\WP_User $user, string $field): string
    {
        switch ($field) {
            case 'username':
                return $user->user_login;

            case 'first_name':
                return $user->first_name;

            case 'last_name':
                return $user->last_name;

            case 'full_name':
                $full = trim($user->first_name . ' ' . $user->last_name);
                return !empty($full) ? $full : $this->getUserField($user, 'display_name');

            case 'display_name':
            default:
                return !empty($user->display_name) ? $user->display_name : $user->user_login;
        }
    }

    /**
     * Build a safe CSS class attribute string.
     *
     * @param string $default_class Default class name.
     * @param string $custom_class  Additional custom classes.
     * @return string
     */
    private function buildCssClassString(string $default_class, string $custom_class): string
    {
        $classes = [$default_class];

        if ('' !== $custom_class) {
            $classes = array_merge($classes, preg_split('/\s+/', trim($custom_class)) ?: []);
        }

        $classes = array_filter(array_map('sanitize_html_class', $classes));

        return implode(' ', array_unique($classes));
    }

    /**
     * Get the current page URL for login redirect.
     *
     * @return string
     */
    private function getCurrentPageUrl(): string
    {
        if (isset($_SERVER['REQUEST_URI'])) {
            return home_url(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])));
        }

        return home_url('/');
    }
}
