#include <curl/curl.h>
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <chrono>

/*
=============================================================
  极验识别 Demo (C++版)

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

  使用方法：
  1. 确保已安装 g++ 和 libcurl 开发库
  2. 将下方的配置参数替换为你自己的值
  3. 编译运行：
       g++ -std=c++11 RecognizeGeetest.cpp -lcurl -o recognize_geetest
       ./recognize_geetest
=============================================================
*/

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

static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
    size_t total = size * nmemb;
    static_cast<std::string*>(userp)->append(static_cast<char*>(contents), total);
    return total;
}

std::string Trim(const std::string& input) {
    size_t start = 0;
    while (start < input.size() && std::isspace(static_cast<unsigned char>(input[start]))) {
        ++start;
    }

    size_t end = input.size();
    while (end > start && std::isspace(static_cast<unsigned char>(input[end - 1]))) {
        --end;
    }

    return input.substr(start, end - start);
}

std::string StripQuotes(const std::string& input) {
    if (input.size() >= 2 && input.front() == '"' && input.back() == '"') {
        return input.substr(1, input.size() - 2);
    }
    return input;
}

std::string ExtractJsonValue(const std::string& json, const std::string& key) {
    std::string needle = "\"" + key + "\"";
    size_t keyPos = json.find(needle);
    if (keyPos == std::string::npos) {
        return "";
    }

    size_t colon = json.find(':', keyPos + needle.size());
    if (colon == std::string::npos) {
        return "";
    }

    size_t pos = colon + 1;
    while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
        ++pos;
    }

    bool inString = false;
    bool escaped = false;
    int braceDepth = 0;
    int bracketDepth = 0;
    size_t end = pos;

    for (; end < json.size(); ++end) {
        char ch = json[end];
        if (inString) {
            if (escaped) {
                escaped = false;
                continue;
            }
            if (ch == '\\') {
                escaped = true;
                continue;
            }
            if (ch == '"') {
                inString = false;
            }
            continue;
        }

        if (ch == '"') {
            inString = true;
            continue;
        }
        if (ch == '{') {
            ++braceDepth;
            continue;
        }
        if (ch == '}') {
            if (braceDepth == 0 && bracketDepth == 0) {
                break;
            }
            --braceDepth;
            continue;
        }
        if (ch == '[') {
            ++bracketDepth;
            continue;
        }
        if (ch == ']') {
            --bracketDepth;
            continue;
        }
        if (ch == ',' && braceDepth == 0 && bracketDepth == 0) {
            break;
        }
    }

    return Trim(json.substr(pos, end - pos));
}

bool IsSuccessStatus(const std::string& json) {
    return ExtractJsonValue(json, "status") == "1";
}

void AppendFormField(CURL* curl, std::string& postData, const std::string& key, const std::string& value) {
    char* escaped = curl_easy_escape(curl, value.c_str(), static_cast<int>(value.size()));
    if (!postData.empty()) {
        postData += "&";
    }
    postData += key + "=" + (escaped ? escaped : "");
    if (escaped) {
        curl_free(escaped);
    }
}

std::string HttpPost(const std::string& url, const std::string& postData, long timeoutSeconds) {
    CURL* curl = curl_easy_init();
    if (!curl) {
        throw std::runtime_error("初始化 CURL 失败");
    }

    std::string response;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeoutSeconds);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    CURLcode code = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (code != CURLE_OK) {
        throw std::runtime_error(curl_easy_strerror(code));
    }

    return response;
}

std::string SubmitTask() {
    std::cout << "==================================================" << std::endl;
    std::cout << "[提交任务] 正在提交极验识别任务..." << std::endl;
    std::cout << "==================================================" << std::endl;

    CURL* encoder = curl_easy_init();
    std::string postData;
    AppendFormField(encoder, postData, "appkey", APPKEY);
    AppendFormField(encoder, postData, "gt", GT);
    AppendFormField(encoder, postData, "itemid", ITEM_ID);
    if (!CHALLENGE.empty()) {
        AppendFormField(encoder, postData, "challenge", CHALLENGE);
    }
    if (!REFERER.empty()) {
        AppendFormField(encoder, postData, "referer", REFERER);
    }
    if (!PROXY.empty()) {
        AppendFormField(encoder, postData, "proxy", PROXY);
    }
    if (!DEVKEY.empty()) {
        AppendFormField(encoder, postData, "devkey", DEVKEY);
    }
    if (!SDK.empty()) {
        AppendFormField(encoder, postData, "sdk", SDK);
    }
    if (!GIVEN.empty()) {
        AppendFormField(encoder, postData, "given", GIVEN);
    }
    if (!HOST.empty()) {
        AppendFormField(encoder, postData, "host", HOST);
    }
    if (!USER_AGENT.empty()) {
        AppendFormField(encoder, postData, "userAgent", USER_AGENT);
    }
    curl_easy_cleanup(encoder);

    std::cout << "[请求参数] gt=" << GT << std::endl;
    std::cout << "[请求参数] itemid=" << ITEM_ID << std::endl;

    std::string result = HttpPost(API_BASE + "/recognize", postData, 30L);
    std::cout << "[提交响应] " << result << std::endl;

    if (!IsSuccessStatus(result)) {
        throw std::runtime_error("任务提交失败：" + StripQuotes(ExtractJsonValue(result, "msg")));
    }

    std::string resultID = StripQuotes(ExtractJsonValue(result, "resultid"));
    std::cout << "[成功] 任务提交成功！resultid=" << resultID << std::endl;
    return resultID;
}

void QueryResult(const std::string& resultID) {
    std::cout << "\n==================================================" << std::endl;
    std::cout << "[查询结果] 正在轮询识别结果..." << std::endl;
    std::cout << "[查询结果] 轮询间隔：" << POLL_INTERVAL_SECONDS << "秒，最大等待：" << MAX_POLL_SECONDS << "秒" << std::endl;
    std::cout << "==================================================" << std::endl;

    for (int elapsed = 0; elapsed <= MAX_POLL_SECONDS; elapsed += POLL_INTERVAL_SECONDS) {
        std::this_thread::sleep_for(std::chrono::seconds(POLL_INTERVAL_SECONDS));

        CURL* encoder = curl_easy_init();
        std::string postData;
        AppendFormField(encoder, postData, "appkey", APPKEY);
        AppendFormField(encoder, postData, "resultid", resultID);
        curl_easy_cleanup(encoder);

        std::string result = HttpPost(API_BASE + "/results", postData, 30L);
        if (IsSuccessStatus(result)) {
            std::cout << "\n[成功] 识别成功！耗时：" << StripQuotes(ExtractJsonValue(result, "time")) << "ms" << std::endl;
            std::cout << "[识别结果] " << ExtractJsonValue(result, "data") << std::endl;
            std::cout << "\n[完整响应] " << result << std::endl;
            return;
        }

        std::cout << "[等待中] 第" << (elapsed + POLL_INTERVAL_SECONDS) << "秒 - "
                  << StripQuotes(ExtractJsonValue(result, "msg")) << std::endl;
    }

    throw std::runtime_error("已等待60秒，识别超时，请稍后重试");
}

int main() {
    std::cout << "************************************************************" << std::endl;
    std::cout << "  极验识别 Demo (C++版)" << std::endl;
    std::cout << "************************************************************" << std::endl;

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

    curl_global_init(CURL_GLOBAL_DEFAULT);

    try {
        std::string resultID = SubmitTask();
        QueryResult(resultID);
    } catch (const std::exception& error) {
        std::cout << "[失败] " << error.what() << std::endl;
        curl_global_cleanup();
        return 1;
    }

    curl_global_cleanup();

    std::cout << "\n==================================================" << std::endl;
    std::cout << "[完成] 识别执行完毕" << std::endl;
    std::cout << "==================================================" << std::endl;
    return 0;
}
