<?php
/**
 * WC Vendors Vendor Shop Class
 *
 * @version 2.6.5 - Fix security issues.
 *
 * @phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
 */
use function WC_Vendors\Classes\Includes\wcv_strip_html;
/**
 * WC Vendors Vendor Shop Class
 *
 * @since 2.4.8 - Added HPOS Compatibility
 */
class WCV_Vendor_Shop {

    /**
     * Seller information
     *
     * @var array
     */
    public static $seller_info;

    /**
     * Construct instance of the class.
     */
    public function __construct() {

        add_filter( 'template_include', array( $this, 'maybe_load_vendor_shop_template' ), 100 );
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_vendor_shop_styles' ) );
        add_action( 'woocommerce_before_main_content', array( $this, 'suppress_wc_sidebar_on_vendor_page' ), 1 );

        add_action( 'woocommerce_product_query', array( $this, 'vendor_shop_query' ), 10, 1 );
        add_action( 'woocommerce_before_main_content', array( 'WCV_Vendor_Shop', 'shop_description' ), 30 );
        add_filter( 'woocommerce_product_tabs', array( 'WCV_Vendor_Shop', 'seller_info_tab' ) );
        add_filter( 'post_type_archive_link', array( 'WCV_Vendor_Shop', 'change_archive_link' ) );

        // Add sold by to product loop before add to cart.
        if ( apply_filters( 'wcvendors_disable_sold_by_labels', wc_string_to_bool( get_option( 'wcvendors_display_label_sold_by_enable', 'no' ) ) ) ) {
            add_action( 'woocommerce_after_shop_loop_item', array( 'WCV_Vendor_Shop', 'template_loop_sold_by' ), 9 );
        }

        // Remove Page Title if on Vendor Shop.
        add_filter( 'woocommerce_show_page_title', array( 'WCV_Vendor_Shop', 'remove_vendor_title' ) );

        // Show vendor on all sales related invoices.
        add_action(
            'woocommerce_checkout_order_created',
            array(
                $this,
                'add_vendor_to_order_item_meta',
            ),
            10,
            1
        );

        // Add a vendor header.
        if ( apply_filters( 'wcvendors_disable_shop_headers', wc_string_to_bool( get_option( 'wcvendors_display_shop_headers', 'no' ) ) ) ) {
            add_action( 'woocommerce_before_main_content', array( 'WCV_Vendor_Shop', 'vendor_main_header' ), 20 );
            if ( wc_string_to_bool( get_option( 'wcvendors_store_single_headers', 'no' ) ) ) {
                add_action( 'woocommerce_before_single_product', array( 'WCV_Vendor_Shop', 'vendor_mini_header' ), 12 );
            }
        }

        add_filter( 'document_title_parts', array( $this, 'vendor_page_title' ) );

        // Change login and registration url to WooCommerce my-account page.
        if ( apply_filters( 'wcvendors_redirect_wp_registration_to_woocommerce_myaccount', wc_string_to_bool( get_option( 'wcvendors_redirect_wp_registration_to_woocommerce_myaccount', 'no' ) ) ) ) {
            add_filter( 'login_url', array( $this, 'change_login_url' ), 1, 3 );
            add_filter( 'register_url', array( $this, 'change_register_url' ), 10, 1 );
            add_action( 'wp_logout', array( $this, 'redirect_after_logout' ), 10 );
            add_filter( 'login_redirect', array( $this, 'change_login_redirect' ), 10, 3 );
        }

        add_action( 'wp_head', array( $this, 'product_seo_meta' ) );
    }

    /**
     * Change the archive link.
     *
     * @param string $link The archive link.
     * @return string
     */
    public static function change_archive_link( $link ) {

        $vendor_shop = urldecode( get_query_var( 'vendor_shop' ) );
        $vendor_id   = WCV_Vendors::get_vendor_id( $vendor_shop );

        return ! $vendor_id ? $link : WCV_Vendors::get_vendor_shop_page( $vendor_id );
    }

    /**
     * Filter WooCommerce main query to include vendor shop pages.
     *
     * @since 2.7.0 Inactive vendors' store pages now return a 404.
     *
     * @param object $q    Existing query object.
     *
     * @return void
     */
    public static function vendor_shop_query( $q ) {

        $vendor_shop = urldecode( get_query_var( 'vendor_shop' ) );
        if ( empty( $vendor_shop ) ) {
            return;
        }

        $vendor_id = WCV_Vendors::get_vendor_id( $vendor_shop );
        if ( ! $vendor_id ) {
            $q->set_404();
            status_header( 404 );

            return;
        }

        // Inactive vendors' store pages should not be publicly accessible, consistent with how
        // their products are hidden from the shop loop and blocked from purchase elsewhere.
        if ( 'inactive' === get_user_meta( $vendor_id, '_wcv_vendor_status', true ) ) {
            $q->set_404();
            status_header( 404 );

            return;
        }

        add_filter( 'woocommerce_page_title', array( 'WCV_Vendor_Shop', 'page_title' ) );

        $q->set( 'author', $vendor_id );
    }


    /**
     * Seller info tab.
     *
     * @param array $tabs The tabs.
     *
     * @return array
     */
    public static function seller_info_tab( $tabs ) {

        global $post;

        if ( ! wc_string_to_bool( get_option( 'wcvendors_label_store_info_enable', 'no' ) ) ) {
            return $tabs;
        }

        if ( WCV_Vendors::is_vendor( $post->post_author ) ) {

            $seller_info = get_user_meta( $post->post_author, 'pv_seller_info', true );
            $has_html    = wc_string_to_bool( get_user_meta( $post->post_author, 'pv_shop_html_enabled', true ) );
            $global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );

            $seller_info_label = __( get_option( 'wcvendors_display_label_store_info' ), 'wc-vendors' ); // phpcs:ignore

            // Run the built in WordPress oEmbed on the seller info if html is enabled.
            if ( $global_html || $has_html ) {
                $embed       = new WP_Embed();
                $seller_info = $embed->autoembed( $seller_info );
            }

            if ( ! empty( $seller_info ) ) {

                $seller_info        = do_shortcode( $seller_info );
                self::$seller_info  = '<div class="pv_seller_info">';
                self::$seller_info .= apply_filters_deprecated( 'wcv_before_seller_info_tab', array( '' ), '2.3.0', 'wcvendors_before_seller_info_tab' );
                self::$seller_info .= apply_filters( 'wcvendors_before_seller_info_tab', '' );
                self::$seller_info .= ( $global_html || $has_html ) ? wpautop( wptexturize( $seller_info ) ) : sanitize_text_field( $seller_info );
                self::$seller_info .= apply_filters_deprecated( 'wcv_after_seller_info_tab', array( '' ), '2.3.0', 'wcvendors_after_seller_info_tab' );
                self::$seller_info .= apply_filters( 'wcvendors_after_seller_info_tab', '' );
                self::$seller_info .= '</div>';

                $tabs['seller_info'] = array(
                    'title'    => apply_filters( 'wcvendors_seller_info_label', $seller_info_label ),
                    'priority' => 50,
                    'callback' => array( 'WCV_Vendor_Shop', 'seller_info_tab_panel' ),
                );
            }
        }

        return $tabs;
    }


    /**
     * Output the seller info
     *
     * @return void
     */
    public static function seller_info_tab_panel() {

        echo wp_kses(
            self::$seller_info,
            wcv_allowed_html_tags()
        );
    }


    /**
     * Show the description a vendor sets when viewing products by that vendor
     */
    public static function shop_description() {

        if ( ! wc_string_to_bool( get_option( 'wcvendors_display_shop_description', 'no' ) ) || is_wcv_pro_active() ) {
            return;
        }

        $vendor_shop = urldecode( get_query_var( 'vendor_shop' ) );
        $vendor_id   = WCV_Vendors::get_vendor_id( $vendor_shop );

        if ( $vendor_id ) {
            $has_html    = wc_string_to_bool( get_user_meta( $vendor_id, 'pv_shop_html_enabled', true ) );
            $global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
            $description = do_shortcode( get_user_meta( $vendor_id, 'pv_shop_description', true ) );

            echo '<div class="pv_shop_description">';
            echo wp_kses(
                ( $global_html || $has_html ) ? wpautop( wptexturize( wp_kses_post( $description ) ) ) : sanitize_text_field( $description ),
                wcv_allowed_html_tags()
            );
            echo '</div>';
        }
    }

    /**
     * Add rewrite rules
     *
     * @deprecated 2.0.9
     * @moved      to WCV_Vendors class
     */
    public static function add_rewrite_rules() {

        $permalink = untrailingslashit( get_option( 'wcvendors_vendor_shop_permalink' ) );

        // Remove beginning slash.
        if ( '/' === substr( $permalink, 0, 1 ) ) {
            $permalink = substr( $permalink, 1, strlen( $permalink ) );
        }

        add_rewrite_tag( '%vendor_shop%', '([^&]+)' );

        add_rewrite_rule( $permalink . '/([^/]*)/page/([0-9]+)', 'index.php?post_type=product&vendor_shop=$matches[1]&paged=$matches[2]', 'top' );
        add_rewrite_rule( $permalink . '/([^/]*)', 'index.php?post_type=product&vendor_shop=$matches[1]', 'top' );
    }


    /**
     * Get page title
     *
     * @param string $page_title The current page title.
     * @return string
     */
    public static function page_title( $page_title = '' ) {

        $vendor_shop = urldecode( get_query_var( 'vendor_shop' ) );
        $vendor_id   = WCV_Vendors::get_vendor_id( $vendor_shop );

        return $vendor_id ? WCV_Vendors::get_vendor_shop_name( $vendor_id ) : $page_title;
    }


    /**
     * Adding sold by to product loop
     *
     * @param int $product_id The product ID.
     * @return void
     */
    public static function template_loop_sold_by( $product_id ) {

        $vendor_id         = WCV_Vendors::get_vendor_from_product( $product_id );
        $sold_by_label     = __( get_option( 'wcvendors_label_sold_by' ), 'wc-vendors' ); // phpcs:ignore
        $sold_by_separator = __( get_option( 'wcvendors_label_sold_by_separator' ), 'wc-vendors' ); // phpcs:ignore
        $sold_by           = wcv_get_sold_by_link( $vendor_id );

        wc_get_template(
            'vendor-sold-by.php',
            array(
                'vendor_id'         => $vendor_id,
                'sold_by_label'     => $sold_by_label,
                'sold_by_separator' => $sold_by_separator,
                'sold_by'           => $sold_by,

            ),
            'wc-vendors/front/',
            WCV_PLUGIN_DIR . 'templates/front/'
        );
    }


    /**
     * Remove the Page title from Archive-Product while on a vendor Page
     *
     * @param boolean $show_title Whether to show the page title.
     * @return bool
     */
    public static function remove_vendor_title( $show_title ) {

        if ( WCV_Vendors::is_vendor_page() ) {
            return false;
        }

        return $show_title;
    }

    /**
     * Display a vendor header at the top of the vendors product archive page
     *
     * @return void
     */
    public static function vendor_main_header() {

        // Remove the basic shop description from the loop.
        remove_action( 'woocommerce_before_main_content', array( 'WCV_Vendor_Shop', 'shop_description' ), 30 );

        if ( WCV_Vendors::is_vendor_page() ) {
            $vendor_shop = urldecode( get_query_var( 'vendor_shop' ) );
            $vendor_id   = WCV_Vendors::get_vendor_id( $vendor_shop );
            $shop_name   = get_user_meta( $vendor_id, 'pv_shop_name', true );

            // Shop description.
            $has_html         = wc_string_to_bool( get_user_meta( $vendor_id, 'pv_shop_html_enabled', true ) );
            $global_html      = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
            $description      = do_shortcode( get_user_meta( $vendor_id, 'pv_shop_description', true ) );
            $shop_description = ( $global_html || $has_html ) ? wpautop( wptexturize( wp_kses_post( $description ) ) ) : sanitize_text_field( $description );
            $seller_info      = ( $global_html || $has_html ) ? wpautop( get_user_meta( $vendor_id, 'pv_seller_info', true ) ) : sanitize_text_field( get_user_meta( $vendor_id, 'pv_seller_info', true ) );
            $vendor           = get_userdata( $vendor_id );
            $vendor_email     = $vendor->user_email;
            $vendor_login     = $vendor->user_login;
            if ( ! wc_string_to_bool( get_option( 'wcvendors_display_shop_description', 'no' ) ) ) {
                $shop_description = '';
            }
            do_action_deprecated( 'wcv_before_main_header', array( $vendor_id ), '2.3.0', 'wcvendors_before_main_header' );
            do_action( 'wcvendors_before_main_header', $vendor_id );

            wc_get_template(
                'vendor-main-header.php',
                array(
                    'vendor'           => $vendor,
                    'vendor_id'        => $vendor_id,
                    'shop_name'        => $shop_name,
                    'shop_description' => $shop_description,
                    'seller_info'      => $seller_info,
                    'vendor_email'     => $vendor_email,
                    'vendor_login'     => $vendor_login,
                ),
                'wc-vendors/front/',
                WCV_PLUGIN_DIR . 'templates/front/'
            );

            do_action_deprecated( 'wcv_after_main_header', array( $vendor_id ), '2.3.0', 'wcvendors_after_main_header' );
            do_action( 'wcvendors_after_main_header', $vendor_id );

        }
    }


    /**
     * Display a vendor header at the top of the single-product page
     *
     * @return void
     */
    public static function vendor_mini_header() {

        global $product;

        $post = get_post( $product->get_id() );

        if ( WCV_Vendors::is_vendor_product_page( $post->post_author ) ) {

            $vendor           = get_userdata( $post->post_author );
            $vendor_id        = $post->post_author;
            $vendor_shop_link = WCV_Vendors::get_vendor_shop_page( $vendor_id );
            $shop_name        = get_user_meta( $vendor_id, 'pv_shop_name', true );
            $has_html         = $vendor->pv_shop_html_enabled;
            $global_html      = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
            $description      = do_shortcode( $vendor->pv_shop_description );
            $shop_description = ( $global_html || $has_html ) ? wpautop( wptexturize( wp_kses_post( $description ) ) ) : sanitize_text_field( $description );
            $seller_info      = ( $global_html || $has_html ) ? wpautop( get_user_meta( $vendor_id, 'pv_seller_info', true ) ) : sanitize_text_field( get_user_meta( $vendor_id, 'pv_seller_info', true ) );
            $vendor_email     = $vendor->user_email;
            $vendor_login     = $vendor->user_login;

            if ( ! wc_string_to_bool( get_option( 'wcvendors_display_shop_description', 'no' ) ) ) {
                $shop_description = '';
            }

            do_action_deprecated( 'wcv_before_mini_header', array( $vendor->ID ), '2.3.0', 'wcvendors_before_mini_header' );
            do_action( 'wcvendors_before_mini_header', $vendor->ID );

            wc_get_template(
                'vendor-mini-header.php',
                array(
                    'vendor'           => $vendor,
                    'vendor_id'        => $vendor_id,
                    'vendor_shop_link' => $vendor_shop_link,
                    'shop_name'        => $shop_name ? $shop_name : $vendor->pv_shop_name,
                    'shop_description' => $shop_description,
                    'seller_info'      => $seller_info,
                    'vendor_email'     => $vendor_email,
                    'vendor_login'     => $vendor_login,
                ),
                'wc-vendors/front/',
                WCV_PLUGIN_DIR . 'templates/front/'
            );

            do_action_deprecated( 'wcv_after_mini_header', array( $vendor->ID ), '2.3.0', 'wcvendors_after_mini_header' );
            do_action( 'wcvendors_after_mini_header', $vendor->ID );

        }
    }

    /**
     * Add vendor to order item meta
     *
     * @since  1.9.0
     * @access public
     *
     * @param WC_Order $order The order item.
     * @return void
     */
    public function add_vendor_to_order_item_meta( $order ) {

        if ( $order->get_parent_id() > 0 ) {
            return;
        }

        if ( 0 === $order->get_id() ) {
            return;
        }

        if ( wc_string_to_bool( get_option( 'wcvendors_display_label_sold_by_enable', 'no' ) ) ) {
            $items = $order->get_items();
            foreach ( $items as  $item ) {
                $product_id    = $item->get_product_id() ? $item->get_product_id() : $item->get_variation_id();
                $vendor_id     = WCV_Vendors::get_vendor_from_product( $product_id );
                $sold_by_label = __( get_option( 'wcvendors_label_sold_by' ), 'wc-vendors' ); // phpcs:ignore
                $sold_by       = WCV_Vendors::is_vendor( $vendor_id )
                    ? sprintf( WCV_Vendors::get_vendor_sold_by( $vendor_id ) )
                    : get_bloginfo( 'name' );

                $item->add_meta_data( apply_filters( 'wcvendors_sold_by_in_email', $sold_by_label ), $sold_by, true );
                $item->save_meta_data();
            }
        }
    }


    /**
     * Add the Vendor shop name to the <title> tag on archive and single product page
     *
     * @param string $title The vendor page title.
     *
     * @since 1.9.9
     * @since 2.7.0 Bail on 404s so inactive vendor pages don't leak the shop name into the title.
     */
    public function vendor_page_title( $title ) {

        if ( ! is_404() && WCV_Vendors::is_vendor_page() ) {

            $title['title'] = self::page_title();
        }

        return $title;
    }

    /**
     * Change the url users will be redirected to for login
     *
     * @param string  $login_url The current login url.
     * @param string  $redirect  The redirect url.
     * @param boolean $force_reauth Whether to force re auth.
     *
     * @return string The url users will be redirected to for login
     * @since 2.1.1
     */
    public function change_login_url( $login_url, $redirect, $force_reauth ) {

        $login_url = get_permalink( wc_get_page_id( 'myaccount' ) );

        if ( ! empty( $redirect ) ) {
            $login_url = add_query_arg( 'redirect_to', rawurlencode( $redirect ), $login_url );
        }

        if ( $force_reauth ) {
            $login_url = add_query_arg( 'reauth', '1', $login_url );
        }

        return $login_url;
    }

    /**
     * Change the registration url to avoid registration in default WordPress registration page
     *
     * @param string $register_url The current WordPress registration url.
     *
     * @return string $register_url The new registration url.
     * @since 2.1.1
     */
    public function change_register_url( $register_url ) {

        $register_url = get_permalink( wc_get_page_id( 'myaccount' ) );

        return $register_url;
    }

    /**
     * Redirect users to mu-account page after logout
     *
     * @return void
     * @since 2.1.1
     */
    public function redirect_after_logout() {

        wp_safe_redirect( get_permalink( wc_get_page_id( 'myaccount' ) ) );
        exit();
    }

    /**
     * Redirect user after successful login.
     *
     * @param string $redirect_to The url to redirect to.
     * @param string $request     The url the user is coming from.
     * @param object $user        Logged in user's data.
     *
     * @return string The url to redirect to
     * @since 2.1.1
     */
    public function change_login_redirect( $redirect_to, $request, $user ) {

        if ( isset( $user->roles ) && is_array( $user->roles ) ) {
            if ( in_array( 'administrator', $user->roles, true ) ) {
                // redirect them to the default place.
                return $redirect_to;
            } else {
                return get_permalink( wc_get_page_id( 'myaccount' ) );
            }
        } else {
            return $redirect_to;
        }
    }

    /**
     * Output SEO & OpenGraph meta tags
     *
     * @return    void
     * @since      2.5.2
     * @version    2.5.2
     */
    public function product_seo_meta() {

        global $post;

        if ( is_archive() ) {
            return;
        }

        $hide_seo = wc_string_to_bool( get_option( 'wcvendors_hide_product_seo', 'no' ) );

        if ( $hide_seo ) {
            return;
        }

        $product = wc_get_product( $post );

        if ( ! is_a( $product, 'WC_Product' ) ) {
            return;
        }

        $product_id = $product->get_id();

        $seo_title       = get_post_meta( $product_id, 'wcv_product_seo_title', true );
        $seo_description = get_post_meta( $product_id, 'wcv_product_seo_description', true );
        $seo_keywords    = get_post_meta( $product_id, 'wcv_product_seo_keywords', true );
        $seo_image_url   = get_the_post_thumbnail_url( $post, 'large' );

        $seo_opengraph    = get_post_meta( $product_id, 'wcv_product_seo_opengraph', true );
        $seo_twitter_card = get_post_meta( $product_id, 'wcv_product_seo_twitter_card', true );

        $seo_store_name     = get_user_meta( $post->post_author, 'pv_shop_name', true );
        $seo_store_url      = WCV_Vendors::is_vendor( $post->post_author ) ? WCV_Vendors::get_vendor_shop_page( $post->post_author ) : '';
        $seo_twitter_author = get_user_meta( $post->post_author, '_wcv_twitter_username', true );

        // use categories for keywords if none are defined.
        if ( '' === $seo_keywords ) {
            $categories   = get_the_term_list( $product_id, 'product_cat', '', ',', '' );
            $seo_keywords = wcv_strip_html( $categories );
        }

        $seo_title       = ! empty( $seo_title ) ? $seo_title : $product->get_name();
        $seo_description = ! empty( $seo_description ) ? $seo_description : substr( $product->get_description(), 0, apply_filters( 'wcv_seo_description_length', 155 ) );

        $seo_product_amount  = $product->get_price();
        $seo_currency_code   = get_woocommerce_currency();
        $seo_currency_symbol = get_woocommerce_currency_symbol( $seo_currency_code );

        wc_get_template(
            'product-seo-meta.php',
            array(
                'product_id'          => $product_id,
                'seo_title'           => $seo_title,
                'seo_description'     => $seo_description,
                'seo_keywords'        => $seo_keywords,
                'seo_image_url'       => $seo_image_url,
                'seo_product_amount'  => $seo_product_amount,
                'seo_currency_code'   => $seo_currency_code,
                'seo_currency_symbol' => $seo_currency_symbol,
                'seo_store_name'      => $seo_store_name,
                'seo_twitter_author'  => $seo_twitter_author,
                'seo_opengraph'       => $seo_opengraph,
                'seo_twitter_card'    => $seo_twitter_card,
                'seo_store_url'       => $seo_store_url,
            ),
            'wc-vendors/product/',
            WCV_TEMPLATE_BASE . 'product/'
        );
    }

    /**
     * Whether the vendor shop sidebar feature is enabled.
     *
     * @since 2.6.9
     *
     * @return bool
     */
    private function is_vendor_shop_sidebar_enabled() {
        return wc_string_to_bool( get_option( 'wcvendors_vendor_shop_sidebar_enabled', 'no' ) );
    }

    /**
     * When the vendor shop sidebar is enabled, suppress the default theme/WooCommerce
     * sidebar so only our dedicated widget area is shown. Handles both the WooCommerce
     * default sidebar and the Storefront theme's own sidebar mechanism.
     *
     * @since 2.6.9
     */
    public function suppress_wc_sidebar_on_vendor_page() {
        if ( ! WCV_Vendors::is_vendor_page() ) {
            return;
        }
        if ( ! $this->is_vendor_shop_sidebar_enabled() ) {
            return;
        }
        // WooCommerce default sidebar (most themes).
        remove_action( 'woocommerce_sidebar', 'woocommerce_get_sidebar', 10 );
        // Storefront theme renders its sidebar via a separate action.
        remove_action( 'storefront_sidebar', 'storefront_get_sidebar', 10 );
    }

    /**
     * Enqueue the vendor shop stylesheet on vendor store pages.
     *
     * @since 2.6.9
     */
    public function enqueue_vendor_shop_styles() {
        if ( ! WCV_Vendors::is_vendor_page() ) {
            return;
        }
        wp_enqueue_style(
            'wcv-store',
            WCV_ASSETS_URL . 'css/wcv-store.css',
            array(),
            WCV_VERSION
        );
    }

    /**
     * Swap in our own vendor shop template on vendor store pages.
     *
     * Only active when the vendor shop sidebar option is enabled. When disabled
     * the WooCommerce default template is used.
     *
     * @since 2.6.9
     *
     * @param string $template The resolved template path.
     * @return string
     */
    public function maybe_load_vendor_shop_template( $template ) {
        if ( ! WCV_Vendors::is_vendor_page() ) {
            return $template;
        }
        if ( ! $this->is_vendor_shop_sidebar_enabled() ) {
            return $template;
        }

        $located = locate_template( array( 'wc-vendors/front/vendor-shop.php' ) );
        if ( $located ) {
            return $located;
        }

        return WCV_TEMPLATE_BASE . 'front/vendor-shop.php';
    }
}
