<?php
defined( 'ABSPATH' ) || exit;

if ( !class_exists( 'Better_Messages_Urls' ) ):

    class Better_Messages_Urls
    {

        public $user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3';

        public static function instance()
        {

            static $instance = null;

            if ( null === $instance ) {
                $instance = new Better_Messages_Urls();
            }

            return $instance;
        }


        public function __construct()
        {
            add_filter( 'bp_better_messages_after_format_message', array( $this, 'nice_links' ), 100, 4 );
        }

        public function check_if_url_is_file( $url ){
            $parsed_url = parse_url( $url );

            if( isset( $parsed_url['path'] ) ) {
                $pathInfo = pathinfo( $parsed_url['path'] );
                if( isset( $pathInfo['extension'] ) ){
                    return true;
                }
            }

            return false;
        }

        public function is_url_allowed( $url ){
            $valid_url = esc_url_raw( $url, ['http', 'https'] );

            if( empty( $valid_url ) ) return false;

            $valid_url = wp_http_validate_url( $valid_url );

            if( ! $valid_url ) return false;

            $parts = parse_url( $valid_url );

            if( isset($parts['port']) ){
                if( $parts['port'] !== 443 && $parts['port'] !== 80 ){
                    return false;
                }
            }

            $blacklist = [
                '127.0.0.1',
                'localhost',
                '::1',
                '0.0.0.0',
                '10.0.0.0/8',
                '172.16.0.0/12',
                '192.168.0.0/16'
            ];

            if( in_array( $parts['host'], $blacklist ) ){
                return false;
            }

            if (filter_var($parts['host'], FILTER_VALIDATE_IP)) {
                foreach ($blacklist as $blocked) {
                    if (strpos($blocked, '/') !== false) {
                        list($subnet, $mask) = explode('/', $blocked);
                        if ((ip2long($parts['host']) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet)) {
                            return false;
                        }
                    } else if ($parts['host'] === $blocked) {
                        return false;
                    }
                }
            }

            return true;
        }

        public function nice_links( $message, $message_id, $context, $user_id )
        {
            if ( $context !== 'stack' ) return $message;
            global $processedUrls;

            $links = array();

            $message = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) { return '<' . array_push($links, $match[1]) . '>'; }, $message);

            $message = preg_replace_callback('~!?\[.*?\]\(.*?\)~sU', function ($match) use (&$links) { return '<' . array_push($links, $match[0]) . '>'; }, $message);

            $regex = '/\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i';
            preg_match_all( $regex, $message, $urls );

            if( ! empty( $urls[0] ) ){
                $urls[0] = array_unique($urls[0]);
            }

            foreach ( $urls[ 0 ] as $_url ) {
                $url = strip_tags(html_entity_decode(esc_url( $_url )));
                $_url = esc_url_raw($url);

                if( ! $this->is_url_allowed( $_url ) ){
                    continue;
                }

                if ( ! isset( $processedUrls[ $message_id ] )
                        || !in_array( $_url, $processedUrls[ $message_id ] )
                        || !in_array( $url, $processedUrls[ $message_id ] )
                ) {

                    $url_md5 = md5( $url );

                    if( Better_Messages()->settings['enableNiceLinks'] === '1' ) {
                        $cache = Better_Messages()->functions->get_message_meta($message_id, 'url_info_' . $url_md5, true);

                        if (!empty($cache)) {
                            $link = $this->render_nice_link($message_id, $url, $cache);
                        } else {

                            if ($this->check_if_url_is_file($url)) {
                                $info = false;
                            } else {
                                $info = $this->fetch_meta_tags($url);
                            }

                            if ($info !== false) {
                                Better_Messages()->functions->update_message_meta($message_id, 'url_info_' . $url_md5, $info);
                                $link = $this->render_nice_link($message_id, $url, $info);
                            } else {
                                Better_Messages()->functions->update_message_meta($message_id, 'url_info_' . $url_md5, '404');
                                $link = $this->render_nice_link($message_id, $url, '404');
                            }

                        }
                    }

                    if( Better_Messages()->settings['oEmbedEnable'] === '1' ){

                        $video_providers = [
                            'youtube',
                            'vimeo',
                            'videopress',
                            'dailymotion',
                            'kickstarter'
                        ];

                        $hide_link = [
                            'twitter',
                        ];

                        $excluded_oembed = [
                            'facebook',  // issues on ajax refresh
                            'giphy',     // not works
                            'reverbnation', // not works
                            'twitter',   // mini chats issues
                            'cloudup',   // mini chats issues
                            'imgur',     // mini chats issues,
                            'instagram', // mini chats and ajax refresh issues
                            'issuu', // mini chats issues
                            'reddit', // too long content usually
                            'plugins', // not needed in messages,
                        ];

                        $is_excluded = false;
                        foreach( $excluded_oembed as $item ){
                            if( strpos( $url, $item ) !== false ){
                                $is_excluded = true;
                                break;
                            }
                        }

                        $oembed = new WP_oEmbed();
                        if( $is_excluded ){
                            $embed = false;
                        } else {
                            $cache = Better_Messages()->functions->get_message_meta( $message_id, 'media_info_' . $url_md5, true );
                            if ( ! empty( $cache ) ) {
                                $embed  = $cache;
                            } else {
                                $embed  = $oembed->get_data( $url, ['height' => '200', 'discover' => false] );
                                Better_Messages()->functions->update_message_meta( $message_id, 'media_info_' . $url_md5, $embed );
                            }
                        }

                        if( $embed !== false ){
                            $html = false;
                            $privacy_embeds = Better_Messages()->settings['privacyEmbeds'] === '1';

                            if( isset($embed->provider_name) && in_array( strtolower($embed->provider_name), $video_providers ) ){
                                if ( $privacy_embeds ) {
                                    $html = $this->render_privacy_embed( $url, $embed );
                                } else {
                                    $html = '<span class="bp-messages-iframe-container">' . $embed->html . '</span>';
                                }
                            } else if( isset($embed->html) ) {
                                // Non-video embeds (SoundCloud, Flickr, etc.) can contain
                                // arbitrary scripts that can't be sandboxed with a simple
                                // iframe swap — skip them when privacy embeds is enabled.
                                if ( ! $privacy_embeds ) {
                                    $html = $embed->html;
                                }
                            }

                            if( isset($embed->provider_name) && in_array( strtolower($embed->provider_name), $hide_link ) ){
                                $link = $html;
                            } else if( isset( $link ) )  {
                                $link = $html . $link;
                            } else {
                                $link = $html;
                            }

                            if( $html ) {
                                $message = str_replace($_url, '', $message);
                            } else {
                                $message = str_replace($_url, '<a target="_blank" href="' . $_url . '">' . $_url . '</a>', $message);
                            }
                        } else {
                            $message = str_replace( $_url, '<a target="_blank" href="' . $_url . '">' . $_url . '</a>', $message );
                        }
                    } else {

                        $message = str_replace( $_url, '<a target="_blank" href="' . $_url . '">' . $_url . '</a>', $message );
                    }

                    if( isset( $link ) ) {
                        $processedUrls[$message_id][] = $link;

                        $message .= '%%link_' . count($processedUrls[$message_id]) . '%%';

                    }
                }
            }

            return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) { return $links[$match[1] - 1]; }, $message);
        }


        public function render_nice_link( $message_id, $url, $info )
        {
            if ( $info == '404' || empty( $info[ 'title' ] ) ) {
                return '';
            }
            ob_start();
            ?>
            <a href="<?php echo $url; ?>" target="_blank" class="url-wrap">
                <?php /*if ( $info[ 'image' ] ) { ?>
                    <span class="url-image" style="background-image: url(<?php echo esc_attr( $info[ 'image' ] ); ?>)"></span>
                <?php }*/?>
                <span class="url-description">
                    <span class="url-title"><?php echo esc_attr( $info[ 'title' ] ); ?></span>
                    <span class="url-site"><?php echo esc_attr( $info[ 'site' ] ); ?></span>
                </span>
            </a>
            <?php
            return str_replace("\n", "", ob_get_clean());
        }

        public function fetch_meta_tags( $url )
        {

            $args = [
                'user-agent' => $this->user_agent,
                'headers' => []
            ];

            if( isset ( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
                $args['headers']['Accept-Language'] = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
            }

            $response = wp_remote_get( $url, $args );

            if ( !is_wp_error( $response ) && $response[ 'response' ][ 'code' ] == '200' ) {
                $tags = $this->getMetaTags( $response[ 'body' ] );

                $url_parts = parse_url( $url );

                $info = array(
                    'title'       => false,
                    'description' => false,
                    'image'       => false,
                    'site'        => $url_parts[ 'host' ]
                );

                if ( isset( $tags[ 'title' ] ) ) $info[ 'title' ] = $tags[ 'title' ];
                if ( isset( $tags[ 'og:title' ] ) ) $info[ 'title' ] = $tags[ 'og:title' ];
                if ( isset( $tags[ 'og:description' ] ) ) $info[ 'description' ] = $tags[ 'og:description' ];

                if ( isset( $tags[ 'thumbnail' ] ) ) $info[ 'image' ] = $tags[ 'thumbnail' ];
                if ( isset( $tags[ 'twitter:image' ] ) ) $info[ 'image' ] = $tags[ 'twitter:image' ];
                if ( isset( $tags[ 'og:image' ] ) ) $info[ 'image' ] = $tags[ 'og:image' ];

                if ( $info[ 'image' ] ) {
                    $image_check = wp_remote_get( $info[ 'image' ], array(
                        'user-agent' => $this->user_agent
                    ) );

                    if ( is_wp_error( $image_check ) || $image_check[ 'response' ][ 'code' ] != '200' ) {
                        $info[ 'image' ] = false;
                    }
                }

                if ( isset( $tags[ 'og:site_name' ] ) ) $info[ 'site' ] = $tags[ 'og:site_name' ];

                return $info;
            } else {
                return false;
            }
        }

        public function getMetaTags( $str )
        {
            $pattern = '
            ~<\s*meta\s
        
            # using lookahead to capture type to $1
            (?=[^>]*?
            \b(?:name|property|http-equiv)\s*=\s*
            (?|"\s*([^"]*?)\s*"|\'\s*([^\']*?)\s*\'|
            ([^"\'>]*?)(?=\s*/?\s*>|\s\w+\s*=))
            )
        
            # capture content to $2
            [^>]*?\bcontent\s*=\s*
              (?|"\s*([^"]*?)\s*"|\'\s*([^\']*?)\s*\'|
              ([^"\'>]*?)(?=\s*/?\s*>|\s\w+\s*=))
            [^>]*>
        
            ~ix';
            preg_match_all( $pattern, $str, $out );

            preg_match( '/<title>(.*?)<\/title>/', $str, $titles );

            $out = array_combine( $out[ 1 ], $out[ 2 ] );
            if ( isset( $titles[ 1 ] ) ) $out[ 'title' ] = $titles[ 1 ];

            return $out;
        }

        /**
         * Render a privacy-friendly embed placeholder.
         * Shows a thumbnail with a play button — the actual iframe loads only on click.
         */
        public function render_privacy_embed( $url, $embed ) {
            $provider  = isset( $embed->provider_name ) ? strtolower( $embed->provider_name ) : '';
            $title     = isset( $embed->title ) ? esc_attr( $embed->title ) : '';

            // Extract the iframe src from the embed HTML
            $iframe_src = '';
            if ( isset( $embed->html ) && preg_match( '/src=["\']([^"\']+)["\']/', $embed->html, $matches ) ) {
                $iframe_src = $matches[1];

                // YouTube: switch to privacy-enhanced mode
                if ( $provider === 'youtube' ) {
                    $iframe_src = str_replace( 'youtube.com', 'youtube-nocookie.com', $iframe_src );
                }
            }

            if ( empty( $iframe_src ) ) {
                return false;
            }

            return '<span class="bp-messages-iframe-container">'
                . '<span class="bm-embed-consent" data-src="' . esc_attr( $iframe_src ) . '">'
                . '<span class="bm-embed-consent-play"></span>'
                . ( $title ? '<span class="bm-embed-consent-title">' . esc_html( $title ) . '</span>' : '' )
                . '</span>'
                . '</span>';
        }

        /**
         * Extract YouTube video ID from URL.
         */
        private function extract_youtube_id( $url ) {
            $patterns = [
                '/youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/',
                '/youtu\.be\/([a-zA-Z0-9_-]+)/',
                '/youtube\.com\/embed\/([a-zA-Z0-9_-]+)/',
                '/youtube\.com\/shorts\/([a-zA-Z0-9_-]+)/',
            ];
            foreach ( $patterns as $pattern ) {
                if ( preg_match( $pattern, $url, $matches ) ) {
                    return $matches[1];
                }
            }
            return false;
        }
    }

endif;


function Better_Messages_Urls()
{
    return Better_Messages_Urls::instance();
}
