<?php
/**
 * 访客日志与 IP 名单数据访问
 */
require_once dirname(__DIR__) . '/DbHelper.php';
require_once dirname(__DIR__) . '/CloakHttpHelpers.php';

if (!function_exists('cloak_session_mark_real_visitor')) {
    /**
     * 后台「真实访客」：写入完整判定缓存，访问落地链接时走 Session 快速路径并直达真实页
     */
    function cloak_session_mark_real_visitor()
    {
        $gcuid = !empty($_COOKIE['gfuid']) ? (string) $_COOKIE['gfuid'] : '132456789';

        $_SESSION['check_result'] = 'true';
        $_SESSION['visit_to_1']   = '1';
        $_SESSION['visit_to_2']   = '2';
        $_SESSION['visit_to_3']   = '3';
        $_SESSION['gcuid']        = $gcuid;
        $_SESSION['gcu_ip']       = $_SERVER['REMOTE_ADDR'] ?? '';

        if (empty($_SESSION['gcu_country']) && defined('SHOW_SITE_COUNTRY')) {
            $_SESSION['gcu_country'] = SHOW_SITE_COUNTRY;
        }

        setCrossDomainCookie('gfuid', $gcuid, time() + 3600 * 24);
    }
}

if (!function_exists('cloak_session_clear_real_visitor')) {
    function cloak_session_clear_real_visitor()
    {
        unset(
            $_SESSION['check_result'],
            $_SESSION['visit_to_1'],
            $_SESSION['visit_to_2'],
            $_SESSION['visit_to_3'],
            $_SESSION['gcuid'],
            $_SESSION['gcu_ip'],
            $_SESSION['gcu_country'],
            $_SESSION['gcu_Client'],
            $_SESSION['gcu_Browser'],
            $_SESSION['gcu_referer']
        );
        setCrossDomainCookie('gfuid', '', time() - 3600);
    }
}

if (!function_exists('ip_group_list_addresses')) {
    function ip_group_list_addresses($groupId)
    {
        $groupId = (int) $groupId;
        if ($groupId <= 0) {
            return [];
        }
        $conn = @new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
        if ($conn->connect_error) {
            return [];
        }
        $out = [];
        $sql = 'SELECT ip_address FROM ip_list WHERE group_id = ' . $groupId;
        if ($res = $conn->query($sql)) {
            while ($row = $res->fetch_assoc()) {
                $out[] = $row['ip_address'];
            }
            $res->free();
        }
        $conn->close();
        return $out;
    }
}

if (!function_exists('ip_visitor_matches_list_entry')) {
    function ip_visitor_matches_list_entry($visitorIp, $entry)
    {
        $entry = trim($entry);
        if ($entry === '') {
            return false;
        }
        if ($visitorIp === $entry) {
            return true;
        }
        if (mb_substr($entry, -1) === '*') {
            return strpos($visitorIp, rtrim($entry, '*')) === 0;
        }
        return false;
    }
}

if (!function_exists('ip_visitor_in_blacklist_entries')) {
    function ip_visitor_in_blacklist_entries($visitorIp, array $entries)
    {
        foreach ($entries as $buffer) {
            if (ip_visitor_matches_list_entry($visitorIp, $buffer)) {
                return true;
            }
        }
        return false;
    }
}

if (!function_exists('cloak_visitor_log_request_meta')) {
    function cloak_visitor_log_request_meta()
    {
        return [
            'user_agent'          => get_SERVER_value('HTTP_USER_AGENT'),
            'http_referer'        => get_SERVER_value('HTTP_REFERER'),
            'accept_language_raw' => get_SERVER_value('HTTP_ACCEPT_LANGUAGE'),
        ];
    }
}

if (!function_exists('cloak_visitor_log_enrich')) {
    function cloak_visitor_log_enrich(array $logs)
    {
        $meta = cloak_visitor_log_request_meta();
        foreach ($meta as $key => $value) {
            if (!array_key_exists($key, $logs) || $logs[$key] === null || $logs[$key] === '') {
                $logs[$key] = $value;
            }
        }
        if (empty($logs['judge_timing']) && !empty($GLOBALS['__cloak_judge_timing'])) {
            $logs['judge_timing'] = $GLOBALS['__cloak_judge_timing'];
        }
        return $logs;
    }
}

if (!function_exists('cloak_visitor_log_async_enabled')) {
    function cloak_visitor_log_async_enabled()
    {
        return defined('VISITOR_LOG_ASYNC') && strtoupper(VISITOR_LOG_ASYNC) === 'ON';
    }
}

if (!function_exists('cloak_visitor_log_register_shutdown_drain')) {
    function cloak_visitor_log_register_shutdown_drain()
    {
        static $registered = false;
        if ($registered || !cloak_visitor_log_async_enabled()) {
            return;
        }
        $registered = true;
        register_shutdown_function(static function () {
            if (function_exists('fastcgi_finish_request')) {
                @fastcgi_finish_request();
            }
            VisitorLogQueue::drain(5);
        });
    }
}

if (!function_exists('cloak_write_log_db_sync')) {
    function cloak_write_log_db_sync($logs)
    {
        return VisitorLogWorker::insertFull($logs);
    }
}

if (!function_exists('cloak_write_log_db')) {
    function cloak_write_log_db($logs)
    {
        $logs = cloak_visitor_log_enrich($logs);

        if (defined('WRITE_LOG') && WRITE_LOG == '1') {
            $my_url       = $_SERVER['PHP_SELF'] ?? '';
            $my_file_name = substr($my_url, strrpos($my_url, '/') + 1);
            $config_name  = str_replace('.php', '', $my_file_name);
            $fp           = fopen(dirname(__DIR__, 2) . '/jump.txt', 'a+');
            fwrite($fp, date('Y-m-d H:i:s') . ' | 写入数据库 ' . ' | ' . ($_SESSION['check_result'] ?? '') . ' | ' . $config_name . ' | async=' . (cloak_visitor_log_async_enabled() ? '1' : '0') . "\n");
            fclose($fp);
        }

        $result = isset($logs['result']) ? (string) $logs['result'] : '';

        // 二次风控 wait：同步最小 INSERT 获取 log_id，TEXT 字段异步 enrich
        if ($result === 'wait') {
            $logId = VisitorLogWorker::insertMinimal($logs);
            if ($logId === null) {
                return cloak_write_log_db_sync($logs);
            }
            if (cloak_visitor_log_async_enabled()) {
                VisitorLogQueue::push([
                    'type'   => 'enrich',
                    'log_id' => $logId,
                    'logs'   => $logs,
                ]);
            } else {
                VisitorLogWorker::enrich($logId, $logs);
            }
            return $logId;
        }

        if (!cloak_visitor_log_async_enabled()) {
            $conn = new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
            if ($conn->connect_error) {
                $error  = 'Connection failed: ' . $conn->connect_error;
                $handle = fopen(dirname(__DIR__, 2) . '/err.txt', 'a+');
                fwrite($handle, date('Y-m-d H:i:s') . ' | ' . $error . "\n");
                fclose($handle);
                return null;
            }
            VisitorLogSchema::ensureColumns($conn);
            $data = [];
            foreach ($logs as $key => $value) {
                $data[$key] = cloak_db_escape($conn, $value);
            }
            $d = static function ($key) use ($data, $conn) {
                return array_key_exists($key, $data) ? $data[$key] : cloak_db_escape($conn, '');
            };
            $sql = "INSERT INTO `visitor_logs`(`campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `referer`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_url`, `device`) VALUES ('"
                . $d('campagin_id') . "','"
                . $d('visit_md5_code') . "','"
                . $d('site_name') . "','"
                . date('Y-m-d H:i:s') . "','"
                . $d('customers_ip') . "','"
                . $d('country') . "','"
                . $d('result') . "','"
                . $d('reason') . "','"
                . $d('v_referer') . "','"
                . $d('Client') . "','"
                . $d('v_Browser') . "','"
                . $d('v_PageURL') . "','"
                . $d('accept_language') . "','"
                . $d('user_agent') . "','"
                . $d('http_referer') . "','"
                . $d('accept_language_raw') . "','"
                . $d('judge_timing') . "','"
                . $d('fp_url') . "','"
                . $d('device') . "')";
            if ($conn->query($sql) === true) {
                $last_id = $conn->insert_id;
                $conn->close();
                return $last_id;
            }
            $handle = fopen(dirname(__DIR__, 2) . '/err.txt', 'a+');
            fwrite($handle, date('Y-m-d H:i:s') . ' | ' . 'Error: ' . $sql . '<br>' . $conn->error . "\n");
            fclose($handle);
            $conn->close();
            return null;
        }

        if (!VisitorLogQueue::push(['type' => 'insert_full', 'logs' => $logs])) {
            return cloak_write_log_db_sync($logs);
        }
        cloak_visitor_log_register_shutdown_drain();
        return null;
    }
}

if (!function_exists('write_log_db')) {
    function write_log_db($logs)
    {
        return cloak_write_log_db($logs);
    }
}

if (!function_exists('cloak_howmanytimesbyip')) {
    function cloak_howmanytimesbyip($ip)
    {
        $row  = ['total' => 0];
        $conn = new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
        if ($conn->connect_error) {
            $handle = fopen(dirname(__DIR__, 2) . '/err.txt', 'a+');
            fwrite($handle, date('Y-m-d H:i:s') . ' | Connection failed: ' . $conn->connect_error . "\n");
            fclose($handle);
            return 0;
        }
        $ipEsc = cloak_db_escape($conn, $ip);
        $sql   = "SELECT count(*) as `total` FROM `visitor_logs` WHERE `IP`='" . $ipEsc . "'";
        if ($conn->query($sql) == true) {
            $result = $conn->query($sql);
            $row    = $result->fetch_array();
            $conn->close();
        } else {
            $handle = fopen(dirname(__DIR__, 2) . '/err.txt', 'a+');
            fwrite($handle, date('Y-m-d H:i:s') . ' | Error: ' . $sql . '<br>' . $conn->error . "\n");
            fclose($handle);
            $conn->close();
            return 0;
        }
        return $row['total'];
    }
}

if (!function_exists('howmanytimesbyip')) {
    function howmanytimesbyip($ip)
    {
        return cloak_howmanytimesbyip($ip);
    }
}

if (!function_exists('cloak_write_black_list')) {
    function cloak_write_black_list($ip)
    {
        if (!defined('BLACKLIST_GROUP_ID') || (int) BLACKLIST_GROUP_ID <= 0) {
            return;
        }
        $gid  = (int) BLACKLIST_GROUP_ID;
        $conn = @new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
        if ($conn->connect_error) {
            return;
        }
        $stmt = $conn->prepare('INSERT IGNORE INTO ip_list (group_id, ip_address) VALUES (?, ?)');
        if ($stmt) {
            $stmt->bind_param('is', $gid, $ip);
            $stmt->execute();
            $stmt->close();
        }
        $conn->close();
    }
}

if (!function_exists('write_black_list')) {
    function write_black_list($ip)
    {
        cloak_write_black_list($ip);
    }
}

if (!function_exists('browser_headers')) {
    function browser_headers()
    {
        $headers = [];
        foreach ($_SERVER as $name => $value) {
            if (preg_match('/^HTTP_/', $name)) {
                $name           = strtolower(strtr(substr($name, 5), '_', '-'));
                $headers[$name] = $value;
            }
        }
        return $headers;
    }
}

if (!function_exists('accept_lang_zh')) {
    function accept_lang_zh()
    {
        if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return false;
        }
        $lang = substr((string) $_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 4);
        if (preg_match('/zh-c/i', $lang) or preg_match('/zh/i', $lang)) {
            return true;
        }
        return false;
    }
}

if (!function_exists('is_https')) {
    function is_https()
    {
        return cloak_request_is_https();
    }
}
