<?php
/**
 * Front-end tour rendering and shortcode.
 *
 * @package BW_Feature_Tour
 */

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

class BW_Frontend {

    private static $instance = null;
    private $guides_output   = array();

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

    private function __construct() {
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
        add_action( 'wp_footer', array( $this, 'output_tour_data' ), 99 );
        add_shortcode( 'bw_feature_tour', array( $this, 'shortcode' ) );
    }

    /**
     * Enqueue front-end assets on pages where guides are active.
     */
    public function enqueue_assets() {
        $guides = $this->get_active_guides();
        if ( empty( $guides ) ) {
            return;
        }

        $this->do_enqueue( $guides );
    }

    /**
     * Enqueue styles, scripts, and localize data. Separated so the shortcode
     * can force-enqueue when no guides auto-match the current page.
     */
    private function do_enqueue( $guides ) {
        if ( wp_script_is( 'bw-tour-js', 'enqueued' ) ) {
            return;
        }

        wp_enqueue_style(
            'bw-tour-css',
            BW_FEATURE_TOUR_URL . 'assets/css/bw-tour.css',
            array(),
            BW_FEATURE_TOUR_VERSION
        );

        wp_enqueue_script(
            'bw-tour-js',
            BW_FEATURE_TOUR_URL . 'assets/js/bw-tour.js',
            array( 'jquery' ),
            BW_FEATURE_TOUR_VERSION,
            true
        );

        // Pass settings and guide data to JS
        $settings = array(
            'primaryColor'   => get_option( 'bw_feature_tour_primary_color', '#2B2663' ),
            'accentColor'    => get_option( 'bw_feature_tour_accent_color', '#1fa88e' ),
            'allowRestart'   => get_option( 'bw_feature_tour_allow_restart', '1' ),
            'animationSpeed' => intval( get_option( 'bw_feature_tour_animation_speed', 400 ) ),
            'overlayOpacity' => intval( get_option( 'bw_feature_tour_overlay_opacity', 70 ) ),
            'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
            'nonce'          => wp_create_nonce( 'bw_feature_tour_nonce' ),
            'isLoggedIn'     => is_user_logged_in(),
            'currentUrl'     => $this->get_current_path(),
            'guides'         => $this->format_guides_for_js( $guides ),
        );

        wp_localize_script( 'bw-tour-js', 'bwFeatureTourData', $settings );

        // Inject custom CSS variables
        $custom_css = sprintf(
            ':root { --bw-primary: %s; --bw-accent: %s; --bw-overlay-opacity: %s; --bw-animation-speed: %sms; }',
            esc_attr( $settings['primaryColor'] ),
            esc_attr( $settings['accentColor'] ),
            esc_attr( $settings['overlayOpacity'] / 100 ),
            esc_attr( $settings['animationSpeed'] )
        );
        wp_add_inline_style( 'bw-tour-css', $custom_css );
    }

    /**
     * Get active guides for the current page and user context.
     */
    private function get_active_guides() {
        static $cached = null;
        if ( null !== $cached ) {
            return $cached;
        }

        $guides = get_posts( array(
            'post_type'      => 'bw_tour',
            'post_status'    => 'publish',
            'posts_per_page' => -1,
        ) );

        $active = array();
        foreach ( $guides as $guide ) {
            if ( $this->audience_matches( $guide ) ) {
                $active[] = $guide;
            }
        }

        $cached = $active;
        return $cached;
    }

    /**
     * Whether the current user matches this tour's audience setting.
     * URL filtering is done client-side (see `shouldAutoStart` in format_guides_for_js)
     * so manual-trigger / shortcode / JS-API tours are still loaded.
     */
    private function audience_matches( $guide ) {
        $show_to = get_post_meta( $guide->ID, '_bw_show_to', true ) ?: 'logged_in';
        if ( 'logged_in' === $show_to && ! is_user_logged_in() ) {
            return false;
        }
        if ( 'logged_out' === $show_to && is_user_logged_in() ) {
            return false;
        }
        return true;
    }

    /**
     * Whether this tour's trigger rule matches the current page (for auto-start).
     */
    private function url_matches( $guide ) {
        $type = get_post_meta( $guide->ID, '_bw_trigger_type', true );

        // Back-compat for tours saved before trigger_type existed.
        if ( '' === $type ) {
            $legacy = get_post_meta( $guide->ID, '_bw_trigger_url', true );
            if ( '' === $legacy )       { $type = 'any'; }
            elseif ( '/' === $legacy )  { $type = 'home'; }
            else                        { $type = 'url'; }
        }

        if ( 'any' === $type ) {
            return true;
        }
        if ( 'home' === $type ) {
            return is_front_page();
        }
        // 'url' — exact path match.
        $trigger_url = get_post_meta( $guide->ID, '_bw_trigger_url', true );
        if ( empty( $trigger_url ) ) {
            return false;
        }
        return untrailingslashit( $trigger_url ) === untrailingslashit( $this->get_current_path() );
    }

    /**
     * Format guides for JavaScript consumption.
     */
    private function format_guides_for_js( $guides ) {
        $output = array();

        foreach ( $guides as $guide ) {
            $steps = get_post_meta( $guide->ID, '_bw_steps', true );
            if ( ! is_array( $steps ) || empty( $steps ) ) {
                continue;
            }

            $completed = false;
            if ( is_user_logged_in() ) {
                $user_completed = get_user_meta( get_current_user_id(), 'bw_feature_tour_completed', true );
                $completed = is_array( $user_completed ) && isset( $user_completed[ $guide->ID ] );
            }

            $auto_start = get_post_meta( $guide->ID, '_bw_auto_start', true ) === '1';
            $output[] = array(
                'id'        => $guide->ID,
                'title'     => $guide->post_title,
                'autoStart' => $auto_start && $this->url_matches( $guide ),
                'showOnce'  => get_post_meta( $guide->ID, '_bw_show_once', true ) === '1',
                'completed' => $completed,
                'steps'     => array_map( array( $this, 'format_step_for_js' ), $steps ),
            );
        }

        return $output;
    }

    /**
     * Per-step payload for JS. Resolves BuddyPress `/me/` alias so the post-redirect
     * URL matches step.url (prevents navigation loops).
     */
    private function format_step_for_js( $step ) {
        return array(
            'title'    => $step['title'],
            'content'  => $step['content'],
            'selector' => $step['target_selector'],
            'position' => $step['position'],
            'url'      => $this->resolve_step_url( $step['url'] ),
            'btnLabel' => $step['button_label'],
            'tip'      => $step['tip'],
        );
    }

    /**
     * Resolve BP `/<members-slug>/me/...` to the current user's canonical profile URL.
     * Returns original string if not a /me/ path or BP is not available.
     */
    private function resolve_step_url( $url ) {
        if ( empty( $url ) || false === strpos( $url, '/me/' ) && '/me' !== substr( $url, -3 ) ) {
            return $url;
        }
        if ( ! is_user_logged_in() || ! function_exists( 'bp_loggedin_user_domain' ) ) {
            return $url;
        }
        $user_domain = bp_loggedin_user_domain(); // absolute URL
        $user_path   = wp_parse_url( $user_domain, PHP_URL_PATH );
        if ( ! $user_path ) {
            return $url;
        }
        // Replace .../me/ with the canonical user path. Handles trailing segments.
        $resolved = preg_replace( '#/[^/]+/me(/|$)#', rtrim( $user_path, '/' ) . '$1', $url, 1 );
        return $resolved ? $resolved : $url;
    }

    /**
     * Get the current request path.
     */
    private function get_current_path() {
        $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/';
        $path = wp_parse_url( $uri, PHP_URL_PATH );
        return $path ? $path : '/';
    }

    /**
     * Output tour data in the footer (fallback if localize didn't fire).
     */
    public function output_tour_data() {
        // The data is already passed via wp_localize_script.
        // This hook is reserved for any additional HTML the tour needs.
    }

    /**
     * Shortcode: [bw_feature_tour id="123"]
     *
     * Renders a "Start Tour" button that triggers the specified guide.
     */
    public function shortcode( $atts ) {
        $atts = shortcode_atts( array(
            'id'    => 0,
            'label' => __( 'Start Tour', 'bw-feature-tour' ),
            'class' => '',
        ), $atts, 'bw_feature_tour' );

        $guide_id = intval( $atts['id'] );
        if ( ! $guide_id ) {
            return '';
        }

        // Verify the guide exists and is published
        $guide = get_post( $guide_id );
        if ( ! $guide || 'publish' !== $guide->post_status || 'bw_tour' !== $guide->post_type ) {
            return '';
        }

        // Force-enqueue for shortcode/manual triggers (bypasses audience cache).
        $guides = $this->get_active_guides();
        if ( ! empty( $guides ) ) {
            $this->do_enqueue( $guides );
        }

        $extra_class = $atts['class'] ? ' ' . esc_attr( $atts['class'] ) : '';

        return sprintf(
            '<button type="button" class="bw-start-tour-btn%s" data-bw-guide-id="%d">%s</button>',
            $extra_class,
            $guide_id,
            esc_html( $atts['label'] )
        );
    }
}
