/*
=============================================================
  极验识别 Demo (Rust版)

  功能说明：
  提交极验验证码识别任务（支持三代/四代）
  极验识别为异步流程：先提交任务获取 resultid，再轮询查询结果

  使用方法：
  1. 确保已安装 Rust / Cargo（建议 stable 版本）
  2. 执行：
       cargo add reqwest --features blocking,json,rustls-tls
       cargo add serde_json
  3. 将下方的配置参数替换为你自己的值
  4. 运行脚本：cargo run --bin recognize_geetest
=============================================================
*/

use reqwest::blocking::Client;
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error;
use std::process;
use std::thread;
use std::time::{Duration, Instant};

const APPKEY: &str = "你的appkey";
const GT: &str = "你获取到的gt值";
const CHALLENGE: &str = "";
const ITEM_ID: &str = "0";
const REFERER: &str = "";
const PROXY: &str = "";
const DEVKEY: &str = "";
const SDK: &str = "";
const GIVEN: &str = "";
const HOST: &str = "";
const USER_AGENT: &str = "";
const API_BASE: &str = "http://api.ttocr.com/api";
const POLL_INTERVAL_SECONDS: u64 = 2;
const MAX_POLL_SECONDS: u64 = 60;

fn build_client(timeout_seconds: u64) -> Result<Client, Box<dyn Error>> {
    Ok(Client::builder()
        .timeout(Duration::from_secs(timeout_seconds))
        .build()?)
}

fn submit_task() -> Result<String, Box<dyn Error>> {
    println!("==================================================");
    println!("[提交任务] 正在提交极验识别任务...");
    println!("==================================================");

    let client = build_client(30)?;
    let mut params = HashMap::new();
    params.insert("appkey", APPKEY.to_string());
    params.insert("gt", GT.to_string());
    params.insert("itemid", ITEM_ID.to_string());

    if !CHALLENGE.is_empty() {
        params.insert("challenge", CHALLENGE.to_string());
    }
    if !REFERER.is_empty() {
        params.insert("referer", REFERER.to_string());
    }
    if !PROXY.is_empty() {
        params.insert("proxy", PROXY.to_string());
    }
    if !DEVKEY.is_empty() {
        params.insert("devkey", DEVKEY.to_string());
    }
    if !SDK.is_empty() {
        params.insert("sdk", SDK.to_string());
    }
    if !GIVEN.is_empty() {
        params.insert("given", GIVEN.to_string());
    }
    if !HOST.is_empty() {
        params.insert("host", HOST.to_string());
    }
    if !USER_AGENT.is_empty() {
        params.insert("userAgent", USER_AGENT.to_string());
    }

    println!("[请求参数] gt={}", GT);
    println!("[请求参数] itemid={}", ITEM_ID);

    let result: Value = client
        .post(&format!("{}/recognize", API_BASE))
        .form(&params)
        .send()?
        .json()?;

    println!(
        "[提交响应] {}",
        serde_json::to_string_pretty(&result).unwrap_or_else(|_| result.to_string())
    );

    if result["status"].as_i64() == Some(1) {
        let result_id = result["resultid"].as_str().unwrap_or("").to_string();
        println!("[成功] 任务提交成功！resultid={}", result_id);
        return Ok(result_id);
    }

    Err(format!("任务提交失败：{}", result["msg"]).into())
}

fn query_result(result_id: &str) -> Result<(), Box<dyn Error>> {
    println!("\n==================================================");
    println!("[查询结果] 正在轮询识别结果...");
    println!(
        "[查询结果] 轮询间隔：{}秒，最大等待：{}秒",
        POLL_INTERVAL_SECONDS, MAX_POLL_SECONDS
    );
    println!("==================================================");

    let client = build_client(30)?;
    let started_at = Instant::now();

    loop {
        if started_at.elapsed() > Duration::from_secs(MAX_POLL_SECONDS) {
            return Err(format!("已等待{}秒，识别超时，请稍后重试", MAX_POLL_SECONDS).into());
        }

        thread::sleep(Duration::from_secs(POLL_INTERVAL_SECONDS));

        let mut params = HashMap::new();
        params.insert("appkey", APPKEY.to_string());
        params.insert("resultid", result_id.to_string());

        let result: Value = client
            .post(&format!("{}/results", API_BASE))
            .form(&params)
            .send()?
            .json()?;

        if result["status"].as_i64() == Some(1) {
            println!("\n[成功] 识别成功！耗时：{}ms", result["time"]);
            println!("[识别结果] {}", result["data"]);
            println!(
                "\n[完整响应] {}",
                serde_json::to_string_pretty(&result).unwrap_or_else(|_| result.to_string())
            );
            return Ok(());
        }

        println!(
            "[等待中] 第{}秒 - {}",
            started_at.elapsed().as_secs(),
            result["msg"]
        );
    }
}

fn main() {
    println!("************************************************************");
    println!("  极验识别 Demo (Rust版)");
    println!("************************************************************");

    if APPKEY == "你的appkey" {
        println!("\n[错误] 请先在代码顶部的配置区域填写你的 APPKEY");
        process::exit(1);
    }
    if GT == "你获取到的gt值" {
        println!("\n[错误] 请先在代码顶部的配置区域填写 GT 值");
        process::exit(1);
    }
    if ITEM_ID == "0" {
        println!("\n[错误] 请先在代码顶部的配置区域填写 ITEM_ID（项目类型）");
        process::exit(1);
    }

    let result_id = match submit_task() {
        Ok(result_id) => result_id,
        Err(error) => {
            println!("[失败] {}", error);
            process::exit(1);
        }
    };

    if let Err(error) = query_result(&result_id) {
        println!("[失败] {}", error);
        process::exit(1);
    }

    println!("\n==================================================");
    println!("[完成] 识别执行完毕");
    println!("==================================================");
}
