<?php

namespace WP_STATISTICS;

class Referred
{
    /**
     * Top Referring Transient name
     *
     * @var string
     */
    public static $top_referring_transient = 'wps_top_referring';

    /**
     * Referral Url Details Option name
     *
     * @var string
     */
    public static $referral_detail_opt = 'wp_statistics_referrals_detail';

    /**
     * Referrer Spam List
     *
     * @var string
     */
    public static $referrer_spam_link = 'https://cdn.jsdelivr.net/gh/matomo-org/referrer-spam-list@4.0.0/spammers.txt';

    /**
     * Referred constructor.
     */
    public function __construct()
    {
        # Remove Cache When Delete Visitor Table
        add_action('wp_statistics_truncate_table', array($this, 'deleteCacheData'));
    }

    /**
     * Get referer URL
     *
     * @return string
     */
    public static function getRefererURL()
    {
        return (isset($_SERVER['HTTP_REFERER']) ? sanitize_url(wp_unslash($_SERVER['HTTP_REFERER'])) : '');
    }

    /**
     * Return the referrer link for the current user.
     *
     * @return array|bool|string
     */
    public static function get()
    {

        // Get Default
        $referred = self::getRefererURL();

        // Sanitize Referer Url
        $referred = esc_url_raw(strip_tags($referred));

        // If Referer is Empty then use same WebSite Url
        if (empty($referred)) {
            $referred = get_bloginfo('url');
        }

        // Check Search Engine
        if (Option::get('addsearchwords', false)) {

            // Check to see if this is a search engine referrer
            $SEInfo = SearchEngine::getByUrl($referred);
            if (is_array($SEInfo)) {

                // If we're a known SE, check the query string
                if ($SEInfo['tag'] != '') {
                    $result = SearchEngine::getByQueryString($referred);

                    // If there were no search words, let's add the page title
                    if ($result == '' || $result == SearchEngine::$error_found) {
                        $result = wp_title('', false);
                        if ($result != '') {
                            $referred = esc_url(add_query_arg($SEInfo['querykey'], urlencode('~"' . $result . '"'), $referred));
                        }
                    }
                }
            }
        }

        return apply_filters('wp_statistics_user_referer', $referred);
    }

    /**
     * Get referrer link
     *
     * @param string $referrer
     * @param string $title
     * @param bool $is_blank
     * @return string
     */
    public static function get_referrer_link($referrer, $title = '', $is_blank = false)
    {

        // Sanitize Link
        $html_referrer = self::html_sanitize_referrer($referrer);

        // Check Url Protocol
        if (!Helper::check_url_scheme($html_referrer)) {
            $html_referrer = '//' . $html_referrer;
        }

        $html_referrer = esc_url($html_referrer);

        // Parse Url
        $base_url = @parse_url($html_referrer);

        // Get Page title
        $title = (trim($title) == "" ? $html_referrer : $title);

        // Get Html Link
        return "<a href='{$html_referrer}' title='{$title}'" . ($is_blank === true ? ' target="_blank"' : '') . ">{$base_url['host']}</a>";
    }

    /**
     * Sanitizes the referrer
     *
     * @param     $referrer
     * @param int $length
     * @return string
     */
    public static function html_sanitize_referrer($referrer, $length = -1)
    {
        $referrer = trim($referrer);

        if ('data:' == strtolower(substr($referrer, 0, 5))) {
            $referrer = 'http://127.0.0.1';
        }

        if ('javascript:' == strtolower(substr($referrer, 0, 11))) {
            $referrer = 'http://127.0.0.1';
        }

        if ($length > 0) {
            $referrer = substr($referrer, 0, $length);
        }

        return $referrer;
    }

    /**
     * Get Number Referer Domain
     *
     * @param $url
     * @param string $type [list|number]
     * @param array $time_rang
     * @param null $limit
     * @return array
     * @throws \Exception
     */
    public static function get_referer_from_domain($url, $type = 'number', $time_rang = array(), $limit = null)
    {
        global $wpdb;

        //Get Domain Name
        $search_url = Helper::get_domain_name($url);

        //Prepare SQL
        $time_sql = '';
        if (count($time_rang) > 0 and !empty($time_rang)) {
            $time_sql = sprintf("AND `last_counter` BETWEEN '%s' AND '%s'", $time_rang[0], $time_rang[1]);
        }
        $sql = $wpdb->prepare("SELECT " . ($type == 'number' ? 'COUNT(*)' : '*') . " FROM `" . DB::table('visitor') . "` WHERE `referred` REGEXP \"^(https?://|www\\.)[\.A-Za-z0-9\-]+\\.[a-zA-Z]{2,4}\" AND referred <> '' AND LENGTH(referred) >=12 AND (`referred` LIKE  %s OR `referred` LIKE %s OR `referred` LIKE %s OR `referred` LIKE %s) " . $time_sql . " ORDER BY `" . DB::table('visitor') . "`.`ID` DESC " . ($limit != null ? " LIMIT " . $limit : "") . "", 'https://www.' . $wpdb->esc_like($search_url) . '%', 'https://' . $wpdb->esc_like($search_url) . '%', 'http://www.' . $wpdb->esc_like($search_url) . '%', 'http://' . $wpdb->esc_like($search_url) . '%');

        //Get Count
        return ($type == 'number' ? $wpdb->get_var($sql) : Visitor::PrepareData($wpdb->get_results($sql)));
    }

    /**
     * Downloads the referrer spam database
     *
     * @see https://github.com/matomo-org/referrer-spam-blacklist.
     * @return string
     */
    public static function download_referrer_spam()
    {

        if (Option::get('referrerspam') == false) {
            return '';
        }

        // Download the file from MaxMind, this places it in a temporary location.
        $response = wp_remote_get(self::$referrer_spam_link, array('timeout' => 60));
        if (is_wp_error($response)) {
            return false;
        }
        $referrerspamlist = wp_remote_retrieve_body($response);
        if (is_wp_error($referrerspamlist)) {
            return false;
        }

        if ($referrerspamlist != '' || Option::get('referrerspamlist') != '') {
            Option::update('referrerspamlist', $referrerspamlist);
        }

        return true;
    }

    /**
     * Get WebSite IP Server And Country Name
     *
     * @param $url string domain name e.g : wp-statistics.com
     * @return array
     * @throws \Exception
     */
    public static function get_domain_server($url)
    {

        //Create Empty Object
        $result = array('ip' => '', 'country' => '');

        //Get Ip by Domain
        if (function_exists('gethostbyname')) {

            // Get Host Domain
            $ip = gethostbyname($url);

            // Check Validate IP
            if (filter_var($ip, FILTER_VALIDATE_IP)) {
                $result['ip']      = $ip;
                $result['country'] = GeoIP::getCountry($ip);
            }
        }

        return $result;
    }

    /**
     * Get Top Referring Site
     *
     * @param int $number
     * @return array
     * @throws \Exception
     */
    public static function getTop($number = 10)
    {
        global $wpdb;

        //Get Top Referring
        if (false === ($get_urls = get_transient(self::$top_referring_transient))) {

            $sql = $wpdb->prepare("ORDER BY `number` DESC LIMIT %d", $number);

            $result = $wpdb->get_results(self::GenerateReferSQL($sql, ''));
            foreach ($result as $items) {
                $get_urls[$items->domain] = self::get_referer_from_domain($items->domain);
            }

            // Put the results in a transient. Expire after 12 hours.
            set_transient(self::$top_referring_transient, $get_urls, 20 * HOUR_IN_SECONDS);
        }

        // Return Data
        return self::PrepareReferData($get_urls);
    }

    /**
     * Prepare Refer Data
     *
     * @param $get_urls
     * @return array
     * @throws \Exception
     */
    public static function PrepareReferData($get_urls)
    {

        //Prepare List
        $list = array();

        //Load country Code
        $ISOCountryCode = Country::getList();

        //Get Refer Site Detail
        $refer_opt     = get_option(self::$referral_detail_opt);
        $referrer_list = (empty($refer_opt) ? array() : $refer_opt);

        if (!$get_urls) {
            return array();
        }

        // Check List
        foreach ($get_urls as $domain => $number) {

            //Get Site Link
            $referrer_html = Referred::html_sanitize_referrer($domain);

            //Get Site information if Not Exist
            if (!array_key_exists($domain, $referrer_list)) {
                $get_site_inf           = Referred::get_domain_server($domain);
                $get_site_title         = Helper::get_site_title_by_url($domain);
                $referrer_list[$domain] = array(
                    'ip'      => $get_site_inf['ip'],
                    'country' => $get_site_inf['country'],
                    'title'   => ($get_site_title === false ? '' : $get_site_title),
                );
            }

            // Push to list
            $list[] = array(
                'domain'    => $domain,
                'title'     => $referrer_list[$domain]['title'],
                'ip'        => ($referrer_list[$domain]['ip'] != "" ? $referrer_list[$domain]['ip'] : '-'),
                'country'   => ($referrer_list[$domain]['country'] != "" ? $ISOCountryCode[$referrer_list[$domain]['country']] : ''),
                'flag'      => ($referrer_list[$domain]['country'] != "" ? Country::flag($referrer_list[$domain]['country']) : ''),
                'page_link' => Menus::admin_url('referrers', array('referrer' => $referrer_html)),
                'number'    => number_format_i18n($number)
            );
        }

        //Save Referrer List Update
        update_option(self::$referral_detail_opt, $referrer_list, 'no');

        // Return Data
        return $list;
    }

    /**
     * Get Referred Site List
     *
     * @param array $args
     * @return mixed
     */
    public static function getList($args = array())
    {
        global $wpdb;

        // Check Custom Date
        $where = '';
        if (isset($args['from']) and isset($args['to'])) {
            $where = "AND `last_counter` BETWEEN '" . $args['from'] . "' AND '" . $args['to'] . "' ";
        }

        // Check Min Number
        $having = '';
        if (isset($args['min'])) {
            $having = "HAVING `number` >" . $args['min'];
        }

        // Check Limit
        $limit = '';
        if (isset($args['limit'])) {
            $limit = "LIMIT " . $args['limit'];
        }

        // Return List
        return $wpdb->get_results(self::GenerateReferSQL($having . " ORDER BY `number` DESC " . $limit, $where));
    }

    /**
     * Generate Basic SQL Refer List
     *
     * @param string $where
     * @param string $extra
     * @return string
     */
    public static function GenerateReferSQL($extra = '', $where = '')
    {

        // Check Protocol Of domain
        $domain_name = rtrim(preg_replace('/^https?:\/\//', '', get_site_url()), " / ");
        foreach (array("http", "https", "ftp") as $protocol) {
            foreach (array('', 'www.') as $w3) {
                $where .= " AND `referred` NOT LIKE '{$protocol}://{$w3}{$domain_name}%' ";
            }
        }

        // Return SQL
        return "SELECT SUBSTRING_INDEX(REPLACE( REPLACE( referred, 'http://', '') , 'https://' , '') , '/', 1 ) as `domain`, count(referred) as `number` FROM " . DB::table('visitor') . " WHERE `referred` REGEXP \"^(https?://|www\\.)[\.A-Za-z0-9\-]+\\.[a-zA-Z]{2,4}\" AND referred <> '' AND LENGTH(referred) >=12 " . $where . " GROUP BY domain " . $extra;
    }

    /**
     * Remove Complete Cache Data
     * @param $table_name
     */
    public function deleteCacheData($table_name)
    {
        if ($table_name == "visitor") {
            delete_transient(self::$top_referring_transient);
            delete_option(self::$referral_detail_opt);
        }
    }
}

new Referred();