<?php
require_once __DIR__ . '/UnifiedScrmData.class.php';

/**
 * 数据爬虫类
 * 用于处理需要先获取 Token 授权，再读取接口数据的场景
 */
class Xinghe
{
    private $cookieFile;
    private $ch;                // cURL 句柄
    private $pageUrl;
    private $account;
    private $password;
    private $unifiedData;
    private $baseUrl;

    /**
     * 构造函数：初始化 cURL 和 Cookie 文件
     */
    public function __construct($pageUrl, $username, $password)
    {
        $this->pageUrl = $pageUrl;
        $this->username = $username;
        $this->password = $password;

        $parsedUrl = parse_url($pageUrl);
        $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] . '://' : '';
        $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
        $baseUrl = $scheme . $host;
        $this->baseUrl = $baseUrl;

        $this->unifiedData = new UnifiedScrmData();
    }

    public function run()
    {
        // 1. 创建临时 Cookie 文件
        $this->cookieFile = tempnam(sys_get_temp_dir(), 'spider_cookie_');
        if ($this->cookieFile === false) {
            throw new \RuntimeException("无法在系统临时目录创建 Cookie 文件");
        }

        // 2. 初始化 cURL 并设置全局参数
        $this->ch = curl_init();
        
        $apiUrl = '/share/share/api_yinliu_count.html?page=1&limit=10&id=&class_id=&is_repet=1&start_time=&end_time=';

        $defaultOptions = [
            CURLOPT_RETURNTRANSFER => true,         // 将结果返回为字符串
            CURLOPT_HEADER         => false,        // 不输出响应头
            CURLOPT_TIMEOUT        => 15,           // 设置超时时间（秒）
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
            // 核心优化：同时指定读写 Cookie 为同一个文件，cURL 会自动维护会话
            CURLOPT_COOKIEJAR      => $this->cookieFile, 
            CURLOPT_COOKIEFILE     => $this->cookieFile,
        ];
        
        curl_setopt_array($this->ch, $defaultOptions);

        $this->authenticate();
        $first_page_data = $this->fetchApiData($apiUrl);            // 首页数据
        $this->unifiedData->total = $first_page_data['count'];      // 总在线人数
        $this->unifiedData->todayNewCount = $first_page_data['totalRow']['day_sum']; // 今日新增在线人数
        
        $total_pages = ceil($this->unifiedData->total/10);          // 总页数
        $allListPagesData = [$first_page_data['data']];             // 初始化列表容器，装入第一页数据
        for ($page = 2; $page <= $total_pages; $page++) {
            $apiUrl = '/share/share/api_yinliu_count.html?page=' . $page . '&limit=10&id=&class_id=&is_repet=1&start_time=&end_time=';
            
            $page_data = $this->fetchApiData($apiUrl);
            
            $allListPagesData[] = $page_data['data'];
        }

        return $this->parseToUnifiedData($first_page_data, $allListPagesData);
    }

    // 清爽至极的数据清洗：详情是详情，列表是列表
    protected function parseToUnifiedData($detailData, $allListPagesData)
    {
        $unifiedData = $this->unifiedData;

        // 循环合并了所有页数的 List 数组
        foreach ($allListPagesData as $records) {
            foreach ($records as $item) {
                if(!empty($item['user'])) {
                    $number = $item['user'] ?? null;
                    $isOnline = (isset($item['online']) && $item['online'] == 1);
                    
                    $unifiedData->addNumber($number, $isOnline, $item['day_sum']);
                }
            }
        }

        return $unifiedData;
    }

    /**
     * 析构函数：释放资源，清理垃圾
     */
    public function __destruct()
    {
        // 关闭 cURL 会话
        if (is_resource($this->ch) || $this->ch instanceof \CurlHandle) {
            curl_close($this->ch);
        }

        // 删除临时 Cookie 文件
        if (file_exists($this->cookieFile)) {
            unlink($this->cookieFile);
        }
    }

    /**
     * 第一步：访问授权页面，建立会话
     *
     * @param string $authUrl 包含 token 的授权地址
     * @return bool 授权请求是否成功
     * @throws \Exception
     */
    public function authenticate()
    {
        $this->sendRequest($this->pageUrl);
        return true; // 如果没有抛出异常，则认为请求成功
    }

    /**
     * 第二步：请求 API 接口获取数据
     *
     * @param string $apiUrl 接口数据地址
     * @return array|null 解析后的数组数据，如果解析失败返回 null
     * @throws \Exception
     */
    public function fetchApiData($apiUrl)
    {
        $response = $this->sendRequest($this->baseUrl . $apiUrl);
        
        // 尝试解析 JSON 数据
        $data = json_decode($response, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \RuntimeException("JSON 解析失败: " . json_last_error_msg() . "。原始响应: " . $response);
        }

        return $data;
    }

    /**
     * 发送 cURL 请求的底层私有方法
     *
     * @param string $url 目标地址
     * @return string 服务器响应内容
     * @throws \Exception
     */
    private function sendRequest($url)
    {
        curl_setopt($this->ch, CURLOPT_URL, $url);
        
        $response = curl_exec($this->ch);

        // 检查是否有网络或 cURL 底层错误
        if ($response === false) {
            $error = curl_error($this->ch);
            throw new \RuntimeException("请求失败 [{$url}]: {$error}");
        }

        // 检查 HTTP 状态码
        $httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
        if ($httpCode >= 400) {
            throw new \RuntimeException("HTTP 请求异常 [{$url}]，状态码: {$httpCode}");
        }
        
        return (string)$response;
    }
}