<?php

class PeepSoEmbed
{
    public $url;
    public $_data;
    public $_error;
    public $_oembed;
    public $_oembed_discovery;

    public function __construct($url)
    {
        $this->url = $url;
        $this->_data = array();
        $this->_error = FALSE;
        $this->_oembed = defined('PEEPSO_ENABLE_OEMBED') ? PEEPSO_ENABLE_OEMBED : TRUE;
        $this->_oembed_discovery = defined('PEEPSO_ENABLE_OEMBED_DISCOVERY') ? PEEPSO_ENABLE_OEMBED_DISCOVERY : TRUE;
    }

    public function fetch()
    {
        if (strpos($this->url, PeepSo::get_page('profile')) !== FALSE) {
            $url = $this->url;
            add_filter('embed_html', function($output, $post, $width, $height) use ($url) {
                $output = preg_replace('/<a(.*)href="([^"]*)"(.*)>/','<a$1href="' . $url . '"$3>', $output);
                $output = preg_replace('/<iframe(.*)src="([^"]*)"(.*)>/','<iframe$1src="' . $url . '/embed"$3>', $output);
                return $output;
            }, 99, 4);

            add_filter('oembed_request_post_id', function($post_id, $url) {
                if (strpos($url, PeepSo::get_page('profile')) !== FALSE) {
                    $page = get_page_by_path(PeepSo::get_option('page_profile'));
                    if (is_object($page)) {
                        $post_id = $page->ID;
                    }
                }
                return $post_id;
            }, 10, 2);
        }

        $this->_oembed = apply_filters('peepso_enable_oembed', $this->_oembed, $this->url);
        $this->_oembed_discovery = apply_filters('peepso_enable_oembed_discovery', $this->_oembed_discovery, $this->url);

        // This method might end up firing several calls
        // The timeout is multiplied by the amount of attempts
        // The time taken to fire everything might cause perceived very slow loads
        $timeout = apply_filters('peepso_embed_timeout',2);
        $redirection = apply_filters('peepso_embed_redirects',2);
        $oembed_html = '';

        new PeepSoError("\n\n\n\n* * * * * * * * * * * * * * NEW PEEPSOEMBED() * * * * * * * * * * * * * *\n\n" . $this->url."\n" . date("Y-m-d H:i:s"));

        new PeepSoError("\n* * * * * * * * * * * * * * EMBED CONFIGURATION * * * * * * * * * * * * *\n");
        new PeepSoError( "Load previews \t\t".PeepSo::get_option('allow_embed'));
        new PeepSoError( "Small thumbs \t\t".PeepSo::get_option('small_url_preview_thumbnail'));
        new PeepSoError( "Allow non-SSL \t\t".PeepSo::get_option('allow_non_ssl_embed',''));
        new PeepSoError( "Prefer images \t\t".PeepSo::get_option('prefer_img_embeds',''));
        new PeepSoError( "Fallback img \t\t".PeepSo::get_option('guess_img_embeds',''));
        new PeepSoError( "Refresh embeds \t\t".PeepSo::get_option('refresh_embeds',''));

        new PeepSoError("\n* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");

        $args = array('redirection' => $redirection, 'timeout' => $timeout, 'limit_response_size' => 512000,'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36');

        if ($this->_oembed) {

            new PeepSoError("\nAttempt WP oEmbed: ");

            $oembed = _wp_oembed_get_object($args);

            // Try to find oEmbed endpoint from registered providers first
            // to prevent HTTP request from "WP_oEmbed::discover" function call.
            $html = $oembed->get_html( $this->url, array_merge(array('discover' => FALSE),$args) );
            if ($html) {
                new PeepSoError("  OK");
                $this->_data['html'] = $this->_filter_html($html);
                return TRUE;
            } else {
                new PeepSoError("  Fail");
                new PeepSoError("  No further debug available");
            }
        } else {
            new PeepSoError("\nSkip WP oEmbed");
        }

        new PeepSoError("\nVerify content type: ");

        // Check content type before attempt to discover oEmbed endpoint from URL content.
        $request = wp_safe_remote_head($this->url, $args);
        $content_type = wp_remote_retrieve_header($request, 'content-type');
        $content_type = preg_replace('#^([^/]+/[^;]+).*$#i', '$1', $content_type);
        $is_text = preg_match('#^(text/|application/(javascript|x-javascript|json))#i', $content_type);
        $is_html = $is_text && preg_match('#^text/html#i', $content_type);

        // Do not fetch non-HTML content.
        if (!$is_html) {
            new PeepSoError("  Fail: content is not valid  HTML - DDOS protection?");
            // Set thumbnail based on content type.
            if (preg_match('#^(image|audio|video)/#i', $content_type, $matches)) {
                $thumbnail = array('type' => $matches[1], 'value' => $this->url);
            } else {
                $thumbnail = $this->_content_type_to_thumbnail($content_type);
            }

            $this->_data['url'] = $this->url;
            $this->_data['site_name'] = $this->_url_to_sitename($this->url);
            $this->_data['title'] = $this->_url_to_title($this->url);
            $this->_data['description'] = '';
            $this->_data['mime_type'] = $content_type;
            $this->_data['thumbnail'] = $thumbnail;
            return TRUE;
        } else {
            new PeepSoError("  OK");
        }

        // Discover oEmbed endpoint from URL content.
        if ($this->_oembed && $this->_oembed_discovery) {

            new PeepSoError("\nAttempt oEmbed discovery:");

            $html = $oembed->get_html( $this->url, array_merge(array('discover' => TRUE), $args) );

            if ($html) {
                new PeepSoError("  OK: oEmbed has HTML");
                if(!stristr($html, '<img') && PeepSo::get_option_new('prefer_img_embeds')) {
                    // admin prefers embeds with img, try falling back
                    new PeepSoError('  oEmbed has no image, and the image preference is enabled');
                    $oembed_html = $html; // use this if PeepSoEmbed fails
                } else {
                    new PeepSoError("  OK: using oEmbed");
                    $this->_data['html'] = $this->_filter_html($html);
                    return TRUE;
                }
            } else {
                new PeepSoError("  Fail");
            }
        } else {
            new PeepSoError("\nSkip oEmbed discovery");
        }

        // Do manual parsing if failed to get the oEmbed code.

        new PeepSoError("\nFallback 1: common browser headers");

        $request = wp_safe_remote_get($this->url, array_merge($args, ['timeout' => min(30, $timeout * 5)]));
        $html = wp_remote_retrieve_body($request);


        // If failed or can't find og:image, try spoofing Facebook user agent
        if(!$html || !strpos($html, 'og:image')) {

            new PeepSoError("  Fail");
            if(!$html) {
                new PeepSoError("  Not HTML at all");
            } elseif(!strpos($html, 'og:image')) {
                new PeepSoError("  HTML, but no og:image");
            }

            if($request instanceof WP_Error) {
                new PeepSoError("  ".$request->get_error_message());
            }

            new PeepSoError("\nFallback 2: Facebook headers");

            $args_fb = array('timeout' => $timeout, 'limit_response_size' => 512000, 'user-agent' => 'facebookexternalhit/1.1');
            $request = wp_safe_remote_get($this->url, $args_fb);
            $html = wp_remote_retrieve_body($request);
        } else {
            // new PeepSoError("  OK");
        }

        if(!$html) {
            new PeepSoError("  Fail");
            if($request instanceof WP_Error) {
                new PeepSoError("  ".$request->get_error_message());
            }
            new PeepSoError("\nFallbacks failed, using oEmbed anyway");
            // PeepSoEmbed failed, use whatever oembed gave us
            $html = $oembed_html;
        }else {
            new PeepSoError("  OK");
        }

        if ($html) {
            new PeepSoError("\nEmbed result\n  OK");

            $data = $this->_parse_html($html);

            // infer image from a <img> tag if you have to
            if(!$data['thumbnail'] && PeepSo::get_option_new('guess_img_embeds')) {
                new PeepSoError("\nAttempt to infer an image, as embed has no img tags");

                $dom = new DOMDocument;
                @$dom->loadHTML($html);
                $images = $dom->getElementsByTagName('img');

                if (count($images)) {

                    new PeepSoError("  ".count($images) . ' image tag(s) found');

                    $potential_thumbs = array();

                    foreach ($images as $image) {
                        $src = $image->getAttribute('src');
                        new PeepSoError("\n".$src);

                        $search = 'https';

                        if(PeepSo::get_option('allow_non_ssl_embed')) {
                            $search = 'http';
                        }

                        if($search == strtolower(substr($src,0,strlen($search)))) {
                            $potential_thumbs[]=$src;
                            new PeepSoError("  ".$search . ' found, potential thumb');
                            break;
                        } else {
                            new PeepSoError("  ".$search . ' not found, skipping this image');
                        }
                    }

                    new PeepSoError("\n".count($potential_thumbs) . " potential thumb(s)");
                    if(count($potential_thumbs)) {
                        $key = isset($_GET['thumb_override']) ? $_GET['thumb_override'] : 0;
                        $key = isset($potential_thumbs[$key]) ? $key : 0;

                        $data['thumbnail'] = $potential_thumbs[$key];

                        new PeepSoError("\nUsing thumb $key: {$data['thumbnail']}");

                        $this->_data['potential_thumbnails'] = count($potential_thumbs);
                    }


                } else {
                    new PeepSoError("  No img tags found");
                }
            }

            // Set generic thumbnail icon if no thumbnail image is found.
            if ($data['thumbnail']) {
                $thumbnail = array('type' => 'image', 'value' => $data['thumbnail']);
            } else {
                $thumbnail = $this->_content_type_to_thumbnail($content_type);
            }

            $this->_data['url'] = $this->url;
            $this->_data['site_name'] = $this->_url_to_sitename($this->url);
            $this->_data['title'] = $data['title'];
            $this->_data['description'] = $data['description'];
            $this->_data['mime_type'] = $content_type;
            $this->_data['thumbnail'] = $thumbnail;
            return TRUE;
        }

        new PeepSoError('Embed result negative');

        return FALSE;
    }

    public function get_data() {
        return $this->_data;
    }

    public function get_error()
    {
        return $this->_error;
    }

    private function _url_to_sitename($url)
    {
        if (preg_match('#^https?://(([^/]+)/)#i', $url, $matches)) {
            return $matches[2];
        }
        return '';
    }

    private function _url_to_title($url)
    {
        if (preg_match('#^https?://(([^/]+)/)*([^/\?\#]+)#i', $url, $matches)) {
            return $matches[3];
        }
        return '';
    }

    private function _content_type_to_thumbnail($content_type)
    {
        return array(
            'type' => 'image',
            'value' => PeepSo::get_asset('images/embeds/no_preview_available.png'),
        );

        $thumbnail = '';

        switch ($content_type) {
            // TODO: Return more thumbnail type based on content type.
            case 'application/pdf':
            case 'application/vnd.ms-excel':
            case 'application/x-sql':
            case 'application/zip':

            default:
                $thumbnail = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M4.01 2L4 22h16V8l-6-6H4.01zM13 9V3.5L18.5 9H13z"/></svg>';
                break;
        }

        return array(
            'type' => 'svg',
            'value' => $thumbnail
        );
    }

    private function _parse_html($html = '')
    {
        $data = array(
            'title' => '',
            'description' => '',
            'thumbnail' => ''
        );

        // Get title information.
        if (preg_match_all('#<meta[^>]+(?:property|name)=([\'"]?)og:title\1[^>]*>#is', $html, $matches)) {
            if (preg_match('# content=([\'"]?)(.*?)\1\s*(?:/?>|[a-z-_:]+=)#is', array_pop($matches[0]), $matches)) {
                $data['title'] = $matches[2];
            }
        } else if (preg_match_all('#<title[^>]*>(.*?)</title>#is', $html, $matches)) {
            $data['title'] = array_pop($matches[1]);
        }

        // Get description information.
        if (preg_match_all('#<meta[^>]+(?:property|name)=([\'"]?)og:description\1[^>]*>#is', $html, $matches)) {
            if (preg_match('# content=([\'"]?)(.*?)\1\s*(?:/?>|[a-z-_:]+=)#is', array_pop($matches[0]), $matches)) {
                $data['description'] = $matches[2];
            }
        } else if (preg_match_all('#<meta[^>]+name=([\'"]?)description\1[^>]*>#is', $html, $matches)) {
            if (preg_match('# content=([\'"]?)(.*?)\1\s*(?:/?>|[a-z-_:]+=)#is', array_pop($matches[0]), $matches)) {
                $data['description'] = $matches[2];
            }
        }

        // Get thumbnail information.
        if (preg_match_all('#<meta[^>]+(?:property|name)=([\'"]?)og:image\1[^>]*>#is', $html, $matches)) {
            if (preg_match('# content=([\'"]?)(.*?)\1\s*(?:/?>|[a-z-_:]+=)#is', array_pop($matches[0]), $matches)) {
                $data['thumbnail'] = $matches[2];
            }
        }

        return $data;
    }

    private function _filter_html($html)
    {
        // Alter Facebook embed code.
        if (preg_match('# class=([\'"])fb-(post|video)\1#is', $html)) {
            $html = preg_replace('#<div[^>]+id=([\'"])fb-root\1[^<]+</div>#is', '', $html);
            $html = preg_replace('#<script[^<]+</script>#is', '', $html);
            // Remove video width setting.
            $html = preg_replace('# data-width=([\'"])\d+%?\1#is', '', $html);
            // Add PeepSo Facebook embed handler.
            $html = $html . '<script>try{peepso.util.fbParseXFBML()}catch(e){}</script>';

        }

        // Alter iframe width to match container width.
        $html = preg_replace(
            '#(<iframe[^>]*) width=([\'"])(\d+%?)\2([^>]*>)#is',
            '$1 width=${2}100%$2 data-original-width=$2$3$2$4',
            $html
        );

        return $html;
    }

    /**
     * Setup PeepSo embed hooks.
     *
     * @static
     */
    public static function init()
    {
        add_filter('peepso_data', array('PeepSoEmbed', 'filter_data'));
        add_filter('peepso_embed_content', array('PeepSoEmbed', 'filter_embed_content'), 10, 3);
        add_action('wp_ajax_peepso_embed_content', array('PeepSoEmbed', 'ajax_embed_content'));
    }

    /**
     * Attach embed config to peepsodata variable.
     *
     * @static
     * @param array $data
     * @return array
     */
    public static function filter_data($data)
    {
        $data['embed'] = array(
            'enable' => PeepSo::get_option('allow_embed', 1),
            'enable_non_ssl' => PeepSo::get_option('allow_non_ssl_embed', 0),
        );
        return $data;
    }

    /**
     * Filter to fetch embed content of a URL.
     *
     * @static
     * @param array $data
     * @param string $url
     * @param boolean $refresh
     * @return array
     */
    public static function filter_embed_content($data, $url, $refresh = FALSE)
    {
        if(PeepSo::is_dev_mode('embeds')) {
            $refresh = true;
        }

        // #7340 Fix link preview problem with the new Twitter URL (x.com).
        $pattern_twitter_x = '/^https?:\/\/(www\.)?x\.com/i';
        if (preg_match($pattern_twitter_x, $url)) {
            $url = preg_replace($pattern_twitter_x, 'https://twitter.com', $url);
        }

        $embed_cache_key = 'peepso_embed_content_' . md5($url);

        // Get embed content from cache if possible.
        if (!$refresh) {
            $embed_content = PeepSo3_Mayfly::get($embed_cache_key);
            if ($embed_content) {
                $data['data'] = $embed_content;
                return $data;
            }
        }

        $is_peepso = FALSE;

        if( PeepSo::is_dev_mode('iframe_embeds') ) {
//            $peepso_url = add_query_arg(['peepso_embed' => 1], $url);
//
//
//            $resp = PeepSoCom_Connect::get($peepso_url);
//
//            new PeepSoError('Detecting PeepSo Single Activity');
//
//            if ($resp instanceof PeepSo3_Error_Remote_Get) {
//
//            } else {
//                $html = $resp;
//
//                if (strstr($html, '<!--PEEPSO_IS_POST-->')) {
//                    $is_peepso = TRUE;
//                    new PeepSoError('Confirmed PeepSo Single Activity');
//                } else {
//                    new PeepSoError('Not a PeepSo Single Activity, falling back');
//                }
//            }
        }

        if($is_peepso) {
            // @todo request the target site to provide an embeddable HTML based on privacy etc
            $data['data']['url'] = $url;
            $data['data']['site_name'] = 'SITENAME';
            $data['data']['title'] = 'TITLE';
            $data['data']['description'] = 'DESC';
            $data['data']['mime_type'] = 'MIME';
            $data['data']['thumbnail'] = 'THUMB';
            $data['data']['html']="<iframe width=100% src=$peepso_url></iframe>";
        } else {
            $embed = new PeepSoEmbed($url);
            if ($embed->fetch()) {
                $embed_content = $embed->get_data();
                $data['data'] = $embed_content;
                PeepSo3_Mayfly::set($embed_cache_key, $embed_content, HOUR_IN_SECONDS);
            } else {
                $data['error'] = $embed->get_error();
                PeepSo3_Mayfly::del($embed_cache_key);
            }
        }

        return $data;
    }

    /**
     * Ajax endpoint to fetch embed content of a URL.
     *
     * @static
     */
    public static function ajax_embed_content()
    {
        $result = array();
        $url = (string) $_POST['url'];
        $refresh = (int) $_POST['refresh'] ? TRUE : FALSE;
        $data = apply_filters('peepso_embed_content', array(), trim($url), $refresh);

        if ( isset($data['error']) ) {
            $result['error'] = $data['error'];
        } else {
            $html = PeepSoTemplate::exec_template('activity', 'content-embed', $data['data'], TRUE);
            $result['success'] = TRUE;
            $result['data'] = array('html' => $html);
        }

        echo json_encode($result);
        die();
    }
}

// EOF
