<?php

namespace SlimStat\Tracker;

use SlimStat\Services\Privacy;
use SlimStat\Utils\Query;

class Tracker
{
    /**
     * Process AJAX tracking request.
     * Returns the result for REST API and other callers.
     *
     * @return string|int The tracking result (record ID with checksum, error code, or 0)
     */
    public static function slimtrack_ajax()
    {
        return Ajax::process();
    }

    public static function rewrite_rule_tracker()
    {
        Routing::addRewriteRules();
    }

    public static function adblocker_javascript()
    {
        Routing::outputTrackerJsIfRequested();
    }

    public static function slimtrack()
    {
        return Processor::process();
    }

    public static function update_content_type($_status = 301, $_location = '')
    {
        return Processor::updateContentType($_status, $_location);
    }

	public static function _insert_row($_data = [], $_table = '')
	{
		if (empty($_data) || empty($_table)) {
			return -1;
		}

		foreach ($_data as $key => $value) {
			$_data[$key] = 'resource' == $key ? sanitize_url($value) : sanitize_text_field($value);
		}

		return Query::insert($_table)
			->ignore()
			->values($_data)
			->execute();
	}

    public static function _update_row($_data = [])
    {
        if (empty($_data) || empty($_data['id'])) {
            return false;
        }

        $id = abs(intval($_data['id']));
        unset($_data['id']);

        $_data = array_filter($_data);

        $table = $GLOBALS['wpdb']->prefix . 'slim_stats';
        $query = Query::update($table)->ignore()->where('id', '=', $id);

        if (!empty($_data['notes']) && is_array($_data['notes'])) {
            $notes_to_append = '[' . implode('][', $_data['notes']) . ']';
            $query->setRaw('notes', "CONCAT(IFNULL(notes, ''), %s)", [$notes_to_append]);
            unset($_data['notes']);
        }

        if ($_data !== []) {
            $query->set($_data);
        }

        $query->execute();

        return $id;
    }

    public static function _set_visit_id($_force_assign = false)
    {
        $is_new_session = true;
        $identifier     = 0;

        if (isset($_COOKIE['slimstat_tracking_code'])) {
            $identifier = Utils::getValueWithoutChecksum(sanitize_text_field(wp_unslash($_COOKIE['slimstat_tracking_code'])));
            if (false === $identifier) {
                return false;
            }

            $is_new_session = (false !== strpos($identifier, 'id'));
            $identifier     = intval($identifier);
        }

        if ($is_new_session && ($_force_assign || 'on' == \wp_slimstat::$settings['javascript_mode'])) {
            if (empty(\wp_slimstat::$settings['session_duration'])) {
                \wp_slimstat::$settings['session_duration'] = 1800;
            }

            // Use atomic counter for thread-safe visit ID generation (O(1) instead of O(n))
            $next_visit_id = VisitIdGenerator::generateNextVisitId();
            if ($next_visit_id <= 0) {
                $next_visit_id = time();
            }

            $stat = \wp_slimstat::get_stat();
            $stat['visit_id'] = intval($next_visit_id);
            \wp_slimstat::set_stat($stat);

            $set_cookie = apply_filters('slimstat_set_visit_cookie', (!empty(\wp_slimstat::$settings['set_tracker_cookie']) && 'on' == \wp_slimstat::$settings['set_tracker_cookie']));
            if ($set_cookie) {
                @setcookie('slimstat_tracking_code', self::_get_value_with_checksum($stat['visit_id']), ['expires' => time() + \wp_slimstat::$settings['session_duration'], 'path' => COOKIEPATH]);
            }

        } elseif ($identifier > 0) {
            $stat = \wp_slimstat::get_stat();
            $stat['visit_id'] = $identifier;
            \wp_slimstat::set_stat($stat);
        }

        if ($is_new_session && $identifier > 0) {
            $stat = \wp_slimstat::get_stat();
            Query::update($GLOBALS['wpdb']->prefix . 'slim_stats')
                ->set(['visit_id' => $stat['visit_id']])
                ->where('id', '=', $identifier)
                ->where('visit_id', '=', 0)
                ->execute();
        }

        return ($is_new_session && ($_force_assign || 'on' == \wp_slimstat::$settings['javascript_mode']));
    }

    public static function _get_remote_ip()
    {
        $ip_array = ['', ''];

        if (!empty($_SERVER['REMOTE_ADDR']) && false !== filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
            $ip_array[0] = sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR']));
        }

        // CF-Connecting-IP is handled separately via Utils::getCfClientIp() with CF-Ray validation.
        $originating_ip_headers = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_INCAP_CLIENT_IP'];
        foreach ($originating_ip_headers as $a_header) {
            if (!empty($_SERVER[$a_header])) {
                $header_value = sanitize_text_field(wp_unslash($_SERVER[$a_header]));
                foreach (explode(',', $header_value) as $a_ip) {
                    $a_ip = trim($a_ip);
                    if (false !== filter_var($a_ip, FILTER_VALIDATE_IP) && $a_ip != $ip_array[0]) {
                        $ip_array[1] = $a_ip;
                        break 2;
                    }
                }
            }
        }

        return apply_filters('slimstat_filter_ip_address', $ip_array);
    }

    public static function _get_language()
    {
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $accept_language = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']));
            preg_match('/([^,;]*)/', $accept_language, $array_languages);
            return str_replace('_', '-', strtolower($array_languages[0]));
        }

        return '';
    }

    public static function _get_search_terms($_url = '')
    {
        if (empty($_url)) {
            return '';
        }

        $searchterms = '';

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local plugin file, WP_Filesystem not needed
        $search_engines = file_get_contents(SLIMSTAT_ANALYTICS_DIR . 'admin/assets/data/matomo-searchengine.json');
        $search_engines = json_decode($search_engines, true);

        $parsed_url = @parse_url($_url);

        if (empty($search_engines) || empty($parsed_url) || empty($parsed_url['host'])) {
            return '';
        }

        $sek = \wp_slimstat::get_lossy_url($parsed_url['host']);

        if (!empty($search_engines[$sek])) {
            if (empty($search_engines[$sek]['params'])) {
                $search_engines[$sek]['params'] = ['q'];
            }

            foreach ($search_engines[$sek]['params'] as $a_param) {
                if (!empty($parsed_url['query'])) {
                    $searchterms = self::_get_param_from_query_string($parsed_url['query'], $a_param);
                    if (!empty($searchterms)) {
                        break;
                    }
                }
            }

            if (!empty($searchterms) && (!empty($search_engines['charsets']) && function_exists('iconv'))) {
                $charset = $search_engines['charsets'][0];
                if (count($search_engines['charsets']) > 1 && function_exists('mb_detect_encoding')) {
                    $charset = mb_detect_encoding($searchterms, $search_engines['charsets']);
                    if (false === $charset) {
                        $charset = $search_engines['charsets'][0];
                    }
                }

                $new_searchterms = @iconv($charset, 'UTF-8//IGNORE', $searchterms);
                if (!('' === $new_searchterms || '0' === $new_searchterms || false === $new_searchterms)) {
                    $searchterms = $new_searchterms;
                }
            }
        } elseif (!empty($parsed_url['query'])) {
            foreach (['ask', 'k', 'q', 'qs', 'qt', 'query', 's', 'string'] as $a_param) {
                $searchterms = self::_get_param_from_query_string($parsed_url['query'], $a_param);
                if (!empty($searchterms)) {
                    break;
                }
            }
        }

        return sanitize_text_field($searchterms);
    }

    public static function _get_param_from_query_string($_query = '', $_parameter = '')
    {
        if (empty($_query)) {
            return '';
        }

        @parse_str($_query, $values);

        return empty($values[$_parameter]) ? '' : $values[$_parameter];
    }

    public static function _get_content_info()
    {
        $content_info = ['content_type' => ''];

        if (is_404()) {
            $content_info['content_type'] = '404';
        } elseif (is_single()) {
            if (($post_type = get_post_type()) != 'post') {
                $post_type = 'cpt:' . $post_type;
            }

            $content_info['content_type'] = $post_type;
            $category_ids                 = [];
            foreach (get_object_taxonomies($GLOBALS['post']) as $a_taxonomy) {
                $terms = get_the_terms($GLOBALS['post']->ID, $a_taxonomy);
                if (is_array($terms)) {
                    foreach ($terms as $a_term) {
                        $category_ids[] = $a_term->term_id;
                    }

                    $content_info['category'] = implode(',', $category_ids);
                }
            }

            $content_info['content_id'] = $GLOBALS['post']->ID;
        } elseif (is_page()) {
            $content_info['content_type'] = 'page';
            $content_info['content_id']   = $GLOBALS['post']->ID;
        } elseif (is_attachment()) {
            $content_info['content_type'] = 'cpt:attachment';
        } elseif (is_singular()) {
            $content_info['content_type'] = 'singular';
        } elseif (is_post_type_archive()) {
            $content_info['content_type'] = 'post_type_archive';
        } elseif (is_tag()) {
            $content_info['content_type'] = 'tag';
            $list_tags                    = get_the_tags();
            if (is_array($list_tags)) {
                $tag_info = array_pop($list_tags);
                if (!empty($tag_info)) {
                    $content_info['category'] = $tag_info->term_id;
                }
            }
        } elseif (is_tax()) {
            $content_info['content_type'] = 'taxonomy';
        } elseif (is_category()) {
            $content_info['content_type'] = 'category';
            $list_categories              = get_the_category();
            if (is_array($list_categories)) {
                $cat_info = array_pop($list_categories);
                if (!empty($cat_info)) {
                    $content_info['category'] = $cat_info->term_id;
                }
            }
        } elseif (is_date()) {
            $content_info['content_type'] = 'date';
        } elseif (is_author()) {
            $content_info['content_type'] = 'author';
        } elseif (is_archive()) {
            $content_info['content_type'] = 'archive';
        } elseif (is_search()) {
            $content_info['content_type'] = 'search';
        } elseif (is_feed()) {
            $content_info['content_type'] = 'feed';
        } elseif (is_home() || is_front_page()) {
            $content_info['content_type'] = 'home';
        } elseif (!empty($GLOBALS['pagenow']) && 'wp-login.php' == $GLOBALS['pagenow']) {
            $content_info['content_type'] = 'login';
        } elseif (!empty($GLOBALS['pagenow']) && 'wp-register.php' == $GLOBALS['pagenow']) {
            $content_info['content_type'] = 'registration';
        } elseif (is_admin() && (!defined('DOING_AJAX') || !DOING_AJAX)) {
            $content_info['content_type'] = 'admin';
        }

        if (is_paged()) {
            $content_info['content_type'] .= ':paged';
        }

        if (is_singular()) {
            $author = get_the_author_meta('user_login', $GLOBALS['post']->post_author);
            if (!empty($author)) {
                $content_info['author'] = $author;
            }
        }

        return $content_info;
    }

    public static function _get_client_info($_data_js = [], $_stat = [])
    {
        if (!empty($_data_js['bw'])) {
            $_stat['resolution'] = strip_tags(trim($_data_js['bw'] . 'x' . $_data_js['bh']));
        }

        if (!empty($_data_js['sw'])) {
            $_stat['screen_width'] = intval($_data_js['sw']);
        }

        if (!empty($_data_js['sh'])) {
            $_stat['screen_height'] = intval($_data_js['sh']);
        }

        if (!empty($_data_js['sl']) && $_data_js['sl'] > 0 && $_data_js['sl'] < 60000) {
            $_stat['server_latency'] = intval($_data_js['sl']);
        }

        if (!empty($_data_js['pp']) && $_data_js['pp'] > 0 && $_data_js['pp'] < 60000) {
            $_stat['page_performance'] = intval($_data_js['pp']);
        }

        if (!empty($_data_js['fh']) && is_scalar($_data_js['fh']) && 'on' != \wp_slimstat::$settings['anonymize_ip']) {
            $fingerprint = preg_replace('/[^a-zA-Z0-9\-_]/', '', (string) $_data_js['fh']);
            if (strlen($fingerprint) > 256) {
                $fingerprint = substr($fingerprint, 0, 256);
            }
            $_stat['fingerprint'] = sanitize_text_field($fingerprint);
        }

        if (!empty($_data_js['tz'])) {
            $_stat['tz_offset'] = intval($_data_js['tz']);
        }

        return $_stat;
    }

    public static function _log_error($_error_code = 0)
    {
        \wp_slimstat::update_option('slimstat_tracker_error', [$_error_code, \wp_slimstat::date_i18n('U')]);
        $stat = \wp_slimstat::get_stat();
        do_action('slimstat_track_exit_' . abs($_error_code), $stat);
        return -$_error_code;
    }

    public static function _get_value_with_checksum($_value = 0)
    {
        return $_value . '.' . md5($_value . (\wp_slimstat::$settings['secret'] ?? ''));
    }


    public static function _is_blacklisted($_needles = [], $_haystack_string = '')
    {
        foreach (\wp_slimstat::string_to_array($_haystack_string) as $a_item) {
            $pattern = str_replace(['\\*', '\\!'], ['(.*)', '.'], preg_quote($a_item, '@'));
            if (!is_array($_needles)) {
                $_needles = [$_needles];
            }

            foreach ($_needles as $a_needle) {
                if (preg_match(sprintf('@^%s$@i', $pattern), $a_needle)) {
                    return true;
                }
            }
        }

        return false;
    }

    public static function _is_new_visitor($_fingerprint = '')
    {
        if ('on' == (\wp_slimstat::$settings['hash_ip'] ?? 'off')) {
            return false;
        }

        if ('on' == \wp_slimstat::$settings['anonymize_ip']) {
            return false;
        }

        $table = $GLOBALS['wpdb']->prefix . 'slim_stats';
        $query = Query::select('COUNT(id) as cnt')->from($table)->where('fingerprint', '=', $_fingerprint);
        $today = date('Y-m-d');
        $stat = \wp_slimstat::get_stat();
        if (!empty($stat['dt']) && date('Y-m-d', $stat['dt']) < $today) {
            $query->allowCaching(true);
        }

        $count_fingerprint = $query->getVar();
        return 0 == $count_fingerprint;
    }

    public static function _dtr_pton($_ip)
    {
        if (filter_var($_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $unpacked = unpack('A4', inet_pton($_ip));
        } elseif (filter_var($_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && defined('AF_INET6')) {
            $unpacked = unpack('A16', inet_pton($_ip));
        }

        $binary_ip = '';
        if ([] !== $unpacked && false !== $unpacked) {
            $unpacked = str_split($unpacked[1]);
            foreach ($unpacked as $char) {
                $binary_ip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
            }
        }

        return $binary_ip;
    }

    public static function _get_mask_length($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return 32;
        } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return 128;
        }

        return false;
    }

    public static function _base64_url_encode($_input = '')
    {
        return strtr(base64_encode($_input), '+/=', '._-');
    }

    public static function _base64_url_decode($_input = '')
    {
        return strip_tags(trim(base64_decode(strtr($_input, '._-', '+/='))));
    }
}
