#include "bsphp_client.h"
#include "bsphp_config.h"
#include "bsphp_crypto.h"

#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>

/*
 * BSPHP CARD 客户端（C 版）
 * ----------------------------------------
 * 1) send_api():
 *    组包(含 appsafecode/md5/mutualkey) -> AES 加密 -> RSA 签名加密 -> WinHTTP POST
 * 2) decrypt_server_response():
 *    拆分 OK|aes|rsa -> RSA 解签名得到响应 AES key -> AES 解密得到 JSON
 * 3) 业务接口:
 *    通过 send_api() 封装成 int bsphp_xxx(...)，返回 1/0。
 *
 * 返回约定：
 * - 成功返回 1，失败返回 0
 * - out_resp 为 malloc 的解密后 JSON 文本，调用方负责 free()
 */

typedef struct {
    char *data;
    size_t len;
} mem_buf_t;

static void bsphp_trace(int v, const char *fmt, ...) {
    va_list ap;
    if (!v) return;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
    fflush(stdout);
}

static void bsphp_trace_str(int v, const char *label, const char *s) {
    enum { kMax = 4096 };
    size_t n;
    if (!v) return;
    if (!s) {
        printf("%s(null)\n", label);
        fflush(stdout);
        return;
    }
    n = strlen(s);
    if (n <= (size_t)kMax) {
        printf("%s%s\n", label, s);
    } else {
        printf("%s%.*s ...[truncated total=%zu bytes]\n", label, kMax, s, n);
    }
    fflush(stdout);
}

static wchar_t *utf8_to_wide(const char *s) {
    int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
    wchar_t *out = (wchar_t *)malloc((size_t)n * sizeof(wchar_t));
    if (!out) return NULL;
    MultiByteToWideChar(CP_UTF8, 0, s, -1, out, n);
    return out;
}

static void now_formats(char out_normal[20], char out_hash[20]) {
    time_t t = time(NULL);
    struct tm tmv;
#ifdef _WIN32
    localtime_s(&tmv, &t);
#else
    localtime_r(&t, &tmv);
#endif
    strftime(out_normal, 20, "%Y-%m-%d %H:%M:%S", &tmv);
    strftime(out_hash, 20, "%Y-%m-%d#%H:%M:%S", &tmv);
}

/* 发送 application/x-www-form-urlencoded 到 BSPHP API。 */
static int http_post_form(const char *url, const char *form_body, char **out_text) {
    int ok = 0;
    wchar_t *wurl = NULL;
    URL_COMPONENTS uc;
    wchar_t host[256];
    wchar_t path[2048];
    INTERNET_PORT port;
    HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
    DWORD flags = 0, avail = 0, readn = 0;
    mem_buf_t chunk = {0};

    memset(&uc, 0, sizeof(uc));
    uc.dwStructSize = sizeof(uc);
    uc.lpszHostName = host;
    uc.dwHostNameLength = (DWORD)(sizeof(host) / sizeof(host[0]));
    uc.lpszUrlPath = path;
    uc.dwUrlPathLength = (DWORD)(sizeof(path) / sizeof(path[0]));

    wurl = utf8_to_wide(url);
    if (!wurl) goto done;
    if (!WinHttpCrackUrl(wurl, 0, 0, &uc)) goto done;
    port = uc.nPort;
    if (uc.nScheme == INTERNET_SCHEME_HTTPS) flags |= WINHTTP_FLAG_SECURE;

    hSession = WinHttpOpen(L"BSPHP-C-DEMO/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                           WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) goto done;
    hConnect = WinHttpConnect(hSession, host, port, 0);
    if (!hConnect) goto done;
    hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL,
                                  WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
    if (!hRequest) goto done;

    if (!WinHttpAddRequestHeaders(hRequest,
                                  L"Content-Type: application/x-www-form-urlencoded\r\n",
                                  (DWORD)-1, WINHTTP_ADDREQ_FLAG_ADD)) goto done;

    if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
                            (LPVOID)form_body, (DWORD)strlen(form_body),
                            (DWORD)strlen(form_body), 0)) goto done;
    if (!WinHttpReceiveResponse(hRequest, NULL)) goto done;

    do {
        if (!WinHttpQueryDataAvailable(hRequest, &avail)) goto done;
        if (avail == 0) break;
        {
            char *buf = (char *)malloc(avail + 1);
            char *newp;
            if (!buf) goto done;
            if (!WinHttpReadData(hRequest, buf, avail, &readn)) {
                free(buf);
                goto done;
            }
            buf[readn] = '\0';
            newp = (char *)realloc(chunk.data, chunk.len + readn + 1);
            if (!newp) {
                free(buf);
                goto done;
            }
            chunk.data = newp;
            memcpy(chunk.data + chunk.len, buf, readn);
            chunk.len += readn;
            chunk.data[chunk.len] = '\0';
            free(buf);
        }
    } while (avail > 0);

    *out_text = chunk.data;
    chunk.data = NULL;
    ok = 1;

done:
    if (!ok && chunk.data) free(chunk.data);
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);
    if (wurl) free(wurl);
    return ok;
}

/* 解密服务端返回：OK|aes_b64|rsa_b64 -> JSON 文本。 */
static int decrypt_server_response(bsphp_client_t *c, const char *api, const char *raw_text,
                                   char **out_plain_json) {
    char *work = _strdup(raw_text);
    char *p1, *p2;
    char *enc_b64;
    char *rsa_b64;
    char *sig_plain = NULL;
    char *json_plain = NULL;
    char *k1, *k2, *k3;
    char aes_key[17];
    int ok = 0;
    if (!work) return 0;

    bsphp_trace(c->verbose, "[BSPHP][%s] --- decrypt: HTTP body (after percent-decode; expect OK|aes_b64|rsa_b64) ---\n", api);
    bsphp_trace_str(c->verbose, "", raw_text);

    p1 = strchr(work, '|');
    if (!p1) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: no '|' in response\n", api);
        goto done;
    }
    p2 = strchr(p1 + 1, '|');
    if (!p2) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: need at least 2 '|' segments\n", api);
        goto done;
    }
    *p1 = '\0';
    *p2 = '\0';
    enc_b64 = p1 + 1;
    rsa_b64 = p2 + 1;

    bsphp_trace(c->verbose, "[BSPHP][%s] decrypt-before: first segment=%s\n", api, work);
    bsphp_trace(c->verbose, "[BSPHP][%s] decrypt-before: AES ciphertext (b64)\n", api);
    bsphp_trace_str(c->verbose, "", enc_b64);
    bsphp_trace(c->verbose, "[BSPHP][%s] decrypt-before: RSA ciphertext (b64)\n", api);
    bsphp_trace_str(c->verbose, "", rsa_b64);

    if (!bsphp_rsa_decrypt_pkcs1_base64(rsa_b64, c->server_private_key, &sig_plain)) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: RSA decrypt failed\n", api);
        goto done;
    }
    bsphp_trace(c->verbose, "[BSPHP][%s] RSA-decrypt signature string\n", api);
    bsphp_trace_str(c->verbose, "", sig_plain);

    k1 = strchr(sig_plain, '|');
    if (!k1) goto done;
    k2 = strchr(k1 + 1, '|');
    if (!k2) goto done;
    k3 = strchr(k2 + 1, '|');
    if (!k3) goto done;
    if ((k3 - (k2 + 1)) < 16) goto done;
    memcpy(aes_key, k2 + 1, 16);
    aes_key[16] = '\0';
    bsphp_trace(c->verbose, "[BSPHP][%s] response AES key (16 chars)=%s\n", api, aes_key);

    if (!bsphp_aes128_cbc_decrypt_base64(enc_b64, aes_key, &json_plain)) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: AES decrypt failed\n", api);
        goto done;
    }
    bsphp_trace(c->verbose, "[BSPHP][%s] decrypt-after (plain JSON)\n", api);
    bsphp_trace_str(c->verbose, "", json_plain);
    *out_plain_json = json_plain;
    json_plain = NULL;
    ok = 1;

done:
    if (work) free(work);
    if (sig_plain) free(sig_plain);
    if (json_plain) free(json_plain);
    return ok;
}

/* 核心发送函数：负责请求加密、签名、POST、响应解密。 */
static int send_api(bsphp_client_t *c, const char *api, const char *extra_query, char **out_resp) {
    char t1[20], t2[20], appsafecode[33], key_md5[33], md5_self[33], aes_key[17], sig_md5[33];
    char req_plain[4096], md5_seed[4096], signature[256];
    char *enc_b64 = NULL, *rsa_b64 = NULL, *payload = NULL, *encoded = NULL, *form = NULL;
    char *http_text = NULL;
    int ok = 0;

    now_formats(t1, t2);
    bsphp_md5_hex(t1, appsafecode);
    snprintf(md5_seed, sizeof(md5_seed),
             "api=%s&BSphpSeSsL=%s&date=%s&mutualkey=%s&appsafecode=%s%s%s",
             api, c->bsphp_sessl, t2, c->mutual_key, appsafecode,
             (extra_query && *extra_query) ? "&" : "",
             (extra_query && *extra_query) ? extra_query : "");
    bsphp_md5_hex(md5_seed, md5_self);

    snprintf(req_plain, sizeof(req_plain),
             "api=%s&BSphpSeSsL=%s&date=%s&md5=%s&mutualkey=%s&appsafecode=%s%s%s",
             api, c->bsphp_sessl, t2, md5_self, c->mutual_key, appsafecode,
             (extra_query && *extra_query) ? "&" : "",
             (extra_query && *extra_query) ? extra_query : "");

    snprintf(signature, sizeof(signature), "%s%s", c->server_private_key, appsafecode);
    bsphp_md5_hex(signature, key_md5);
    memcpy(aes_key, key_md5, 16);
    aes_key[16] = '\0';

    bsphp_trace(c->verbose, "\n[BSPHP][%s] ========== request ==========\n", api);
    bsphp_trace(c->verbose, "[BSPHP][%s] URL=%s\n", api, c->url);
    bsphp_trace(c->verbose, "[BSPHP][%s] appsafecode(MD5 of time)=%s\n", api, appsafecode);
    bsphp_trace(c->verbose, "[BSPHP][%s] md5_seed(self-md5 input)=%s\n", api, md5_seed);
    bsphp_trace(c->verbose, "[BSPHP][%s] encrypt-before (plain urlencoded body)=\n", api);
    bsphp_trace_str(c->verbose, "", req_plain);
    bsphp_trace(c->verbose, "[BSPHP][%s] MD5(server_priv+appsafe) full=%s\n", api, key_md5);
    bsphp_trace(c->verbose, "[BSPHP][%s] AES key (16 chars)=%s\n", api, aes_key);

    if (!bsphp_aes128_cbc_encrypt_base64(req_plain, aes_key, &enc_b64)) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: AES encrypt failed\n", api);
        goto done;
    }
    bsphp_trace(c->verbose, "[BSPHP][%s] encrypt-after (AES b64)\n", api);
    bsphp_trace_str(c->verbose, "", enc_b64);

    bsphp_md5_hex(enc_b64, sig_md5);
    snprintf(signature, sizeof(signature), "0|AES-128-CBC|%s|%s|%s", aes_key, sig_md5,
             c->signature_tail ? c->signature_tail : "json");
    bsphp_trace(c->verbose, "[BSPHP][%s] sign string (RSA plain)=\n", api);
    bsphp_trace_str(c->verbose, "", signature);

    if (!bsphp_rsa_encrypt_pkcs1_base64(signature, c->client_public_key, &rsa_b64)) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: RSA encrypt failed\n", api);
        goto done;
    }
    bsphp_trace(c->verbose, "[BSPHP][%s] RSA encrypt-after (b64)\n", api);
    bsphp_trace_str(c->verbose, "", rsa_b64);

    payload = (char *)malloc(strlen(enc_b64) + strlen(rsa_b64) + 2);
    if (!payload) goto done;
    sprintf(payload, "%s|%s", enc_b64, rsa_b64);
    encoded = bsphp_url_encode(payload);
    if (!encoded) goto done;
    form = (char *)malloc(strlen(encoded) + 16);
    if (!form) goto done;
    sprintf(form, "parameter=%s", encoded);
    bsphp_trace(c->verbose, "[BSPHP][%s] POST body (parameter= urlencoded)\n", api);
    bsphp_trace_str(c->verbose, "", form);

    if (!http_post_form(c->url, form, &http_text)) {
        bsphp_trace(c->verbose, "[BSPHP][%s] error: HTTP POST failed\n", api);
        goto done;
    }
    /* 服务端常把 body 做百分号编码，分段符 | 会变成 %7C，须先解码再按 | 拆分 */
    {
        char *dec = bsphp_url_decode(http_text);
        if (dec) {
            free(http_text);
            http_text = dec;
        }
    }
    bsphp_trace(c->verbose, "[BSPHP][%s] ========== response ==========\n", api);
    ok = decrypt_server_response(c, api, http_text, out_resp);

done:
    if (enc_b64) bsphp_secure_free(enc_b64);
    if (rsa_b64) bsphp_secure_free(rsa_b64);
    if (payload) bsphp_secure_free(payload);
    if (encoded) bsphp_secure_free(encoded);
    if (form) bsphp_secure_free(form);
    if (http_text) free(http_text);
    return ok;
}

/* 加载配置到客户端上下文。 */
void bsphp_client_init(bsphp_client_t *c) {
    memset(c, 0, sizeof(*c));
    c->url = BSPHP_API_URL;
    c->mutual_key = BSPHP_MUTUAL_KEY;
    c->server_private_key = BSPHP_SERVER_PRIVATE_KEY_B64;
    c->client_public_key = BSPHP_CLIENT_PUBLIC_KEY_B64;
    c->signature_tail = BSPHP_SIGNATURE_TAIL;
    c->verbose = 0;
}

/* 开关详细日志输出。 */
void bsphp_client_set_verbose(bsphp_client_t *c, int on) {
    if (!c) return;
    c->verbose = on ? 1 : 0;
}

/* 修改签名尾（json/XML）。 */
void bsphp_client_set_signature_tail(bsphp_client_t *c, const char *tail) {
    if (!c || !tail || !tail[0]) return;
    c->signature_tail = tail;
}

/* 初始化握手：internet.in -> BSphpSeSsL.in。 */
int bsphp_bootstrap(bsphp_client_t *c, char **out_resp) {
    char *r = NULL;
    if (!send_api(c, "internet.in", "", &r)) return 0;
    free(r);
    return send_api(c, "BSphpSeSsL.in", "", out_resp);
}

/*
 * Mac(C++) 卡模式常用接口清单（对照 BSPHPClient.swift）
 *
 * 基础/公共：
 * - internet.in      连通性检测（bootstrap 第一步）
 * - BSphpSeSsL.in    获取会话 SeSsL（bootstrap 第二步）
 * - gg.in            公告
 * - date.in          服务器时间
 * - getsetimag.in    获取验证码类型开关
 * - imga.in          获取验证码图片信息
 *
 * 卡模式：
 * - login.ic         卡登录（icid, icpwd, key, maxoror）
 * - getdate.ic       卡到期时间
 * - getlkinfo.ic     登录状态
 * - timeout.ic       心跳
 * - getinfo.ic       卡信息字段（ic_carid, ic_pwd, info[,type]）
 * - socard.in        查卡（cardid）
 * - setcaron.ic      绑定机器（key, icid, icpwd）
 * - setcarnot.ic     解绑机器（icid, icpwd）
 * - cancellation.ic  注销
 *
 * 扩展（按后台开通）：
 * - chong.ic / appcustom.in / remotecancellation.ic / getdata.ic 等
 *
 * 统一调用方式：
 *   send_api(c, "api_name", "k1=v1&k2=v2", &resp);
 */
int bsphp_login_ic(bsphp_client_t *c, const char *icid, const char *icpwd, const char *machine_code, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "icid=%s&icpwd=%s&key=%s&maxoror=%s", icid, icpwd, machine_code, machine_code);
    return send_api(c, "login.ic", extra, out_resp);
}

/* getinfo.ic：查询卡信息字段。 */
int bsphp_get_card_info(bsphp_client_t *c, const char *ic_carid, const char *ic_pwd, const char *info, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "ic_carid=%s&ic_pwd=%s&info=%s", ic_carid, ic_pwd, info);
    return send_api(c, "getinfo.ic", extra, out_resp);
}

/* getdate.ic：读取到期时间。 */
int bsphp_get_date_ic(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "getdate.ic", "", out_resp);
}

/* getlkinfo.ic：读取当前登录状态。 */
int bsphp_get_login_info(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "getlkinfo.ic", "", out_resp);
}

/* timeout.ic：心跳。 */
int bsphp_heart_ic(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "timeout.ic", "", out_resp);
}

/* gg.in：公告。 */
int bsphp_get_notice(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "gg.in", "", out_resp);
}

/* v.in：版本信息。 */
int bsphp_get_version(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "v.in", "", out_resp);
}

/* miao.in：软件描述。 */
int bsphp_get_soft_info(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "miao.in", "", out_resp);
}

/* url.in：预设 URL。 */
int bsphp_get_preset_url(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "url.in", "", out_resp);
}

/* weburl.in：Web URL。 */
int bsphp_get_web_url(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "weburl.in", "", out_resp);
}

/* date.in：服务器时间。m 可为空。 */
int bsphp_get_server_date(bsphp_client_t *c, const char *m, char **out_resp) {
    char extra[256];
    if (m && m[0]) snprintf(extra, sizeof(extra), "m=%s", m);
    else extra[0] = '\0';
    return send_api(c, "date.in", extra, out_resp);
}

/* globalinfo.in：全局配置段。info 可为空。 */
int bsphp_get_global_info(bsphp_client_t *c, const char *info, char **out_resp) {
    char extra[512];
    if (info && info[0]) snprintf(extra, sizeof(extra), "info=%s", info);
    else extra[0] = '\0';
    return send_api(c, "globalinfo.in", extra, out_resp);
}

/* imga.in：验证码信息。 */
int bsphp_get_captcha_info(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "imga.in", "", out_resp);
}

/* getsetimag.in：验证码开关。 */
int bsphp_get_set_imag(bsphp_client_t *c, const char *type, char **out_resp) {
    char extra[512];
    if (type && type[0]) snprintf(extra, sizeof(extra), "type=%s", type);
    else extra[0] = '\0';
    return send_api(c, "getsetimag.in", extra, out_resp);
}

/* logic 系列：布尔逻辑与文本。 */
int bsphp_get_logic_a(bsphp_client_t *c, char **out_resp) { return send_api(c, "logica.in", "", out_resp); }
int bsphp_get_logic_b(bsphp_client_t *c, char **out_resp) { return send_api(c, "logicb.in", "", out_resp); }
int bsphp_get_logic_info_a(bsphp_client_t *c, char **out_resp) { return send_api(c, "logicinfoa.in", "", out_resp); }
int bsphp_get_logic_info_b(bsphp_client_t *c, char **out_resp) { return send_api(c, "logicinfob.in", "", out_resp); }

/* socard.in：查卡。 */
int bsphp_query_card(bsphp_client_t *c, const char *cardid, char **out_resp) {
    char extra[512];
    snprintf(extra, sizeof(extra), "cardid=%s", cardid ? cardid : "");
    return send_api(c, "socard.in", extra, out_resp);
}

/* setcaron.ic：绑定机器。 */
int bsphp_bind_card(bsphp_client_t *c, const char *key, const char *icid, const char *icpwd, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "key=%s&icid=%s&icpwd=%s",
             key ? key : "", icid ? icid : "", icpwd ? icpwd : "");
    return send_api(c, "setcaron.ic", extra, out_resp);
}

/* setcarnot.ic：解绑机器。 */
int bsphp_unbind_card(bsphp_client_t *c, const char *icid, const char *icpwd, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "icid=%s&icpwd=%s",
             icid ? icid : "", icpwd ? icpwd : "");
    return send_api(c, "setcarnot.ic", extra, out_resp);
}

/* appcustom.in：自定义配置模型。 */
int bsphp_get_app_custom(bsphp_client_t *c, const char *info, const char *get_type, const char *user, const char *pwd, const char *icid, const char *icpwd, char **out_resp) {
    char extra[2048];
    int n = snprintf(extra, sizeof(extra), "info=%s", info ? info : "");
    if (get_type && get_type[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&get_type=%s", get_type);
    if (user && user[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&user=%s", user);
    if (pwd && pwd[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&pwd=%s", pwd);
    if (icid && icid[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&icid=%s", icid);
    if (icpwd && icpwd[0] && n > 0 && n < (int)sizeof(extra)) snprintf(extra + n, sizeof(extra) - (size_t)n, "&icpwd=%s", icpwd);
    return send_api(c, "appcustom.in", extra, out_resp);
}

/* getdata.ic：读取验证 key。 */
int bsphp_get_data_ic(bsphp_client_t *c, const char *key, char **out_resp) {
    char extra[512];
    snprintf(extra, sizeof(extra), "key=%s", key ? key : "");
    return send_api(c, "getdata.ic", extra, out_resp);
}

/* AddCardFeatures.key.ic：添加机器特征。 */
int bsphp_add_card_features(bsphp_client_t *c, const char *carid, const char *key, const char *maxoror, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "carid=%s&key=%s&maxoror=%s",
             carid ? carid : "", key ? key : "", maxoror ? maxoror : "");
    return send_api(c, "AddCardFeatures.key.ic", extra, out_resp);
}

/* remotecancellation.ic：远程解绑。 */
int bsphp_remote_cancellation(bsphp_client_t *c, const char *icid, const char *icpwd, const char *type, const char *biaoji, char **out_resp) {
    char extra[1024];
    int n = snprintf(extra, sizeof(extra), "icid=%s&icpwd=%s&type=%s",
                     icid ? icid : "", icpwd ? icpwd : "", type ? type : "");
    if (biaoji && biaoji[0] && n > 0 && n < (int)sizeof(extra)) snprintf(extra + n, sizeof(extra) - (size_t)n, "&biaoji=%s", biaoji);
    return send_api(c, "remotecancellation.ic", extra, out_resp);
}

/* chong.ic：卡充值。 */
int bsphp_recharge_card(bsphp_client_t *c, const char *icid, const char *ka, const char *pwd, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "icid=%s&ka=%s&pwd=%s", icid ? icid : "", ka ? ka : "", pwd ? pwd : "");
    return send_api(c, "chong.ic", extra, out_resp);
}

/* liuyan.in：意见反馈。 */
int bsphp_feedback_ic(bsphp_client_t *c, const char *table, const char *leix, const char *qq, const char *txt, const char *img, const char *user, const char *pwd, char **out_resp) {
    char extra[2048];
    int n = snprintf(extra, sizeof(extra), "table=%s&leix=%s&qq=%s&txt=%s",
                     table ? table : "", leix ? leix : "", qq ? qq : "", txt ? txt : "");
    if (img && img[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&img=%s", img);
    if (user && user[0] && n > 0 && n < (int)sizeof(extra)) n += snprintf(extra + n, sizeof(extra) - (size_t)n, "&user=%s", user);
    if (pwd && pwd[0] && n > 0 && n < (int)sizeof(extra)) snprintf(extra + n, sizeof(extra) - (size_t)n, "&pwd=%s", pwd);
    return send_api(c, "liuyan.in", extra, out_resp);
}

/* CallRemote.in：中转地址调用。 */
int bsphp_call_remote(bsphp_client_t *c, const char *datas, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "datas=%s", datas ? datas : "");
    return send_api(c, "CallRemote.in", extra, out_resp);
}

/* GetMyData.in：读取自定义键值。 */
int bsphp_get_my_data(bsphp_client_t *c, const char *keys, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "keys=%s", keys ? keys : "");
    return send_api(c, "GetMyData.in", extra, out_resp);
}

/* SetMysData.in：写入自定义键值。 */
int bsphp_set_my_data(bsphp_client_t *c, const char *keys, const char *datas, char **out_resp) {
    char extra[2048];
    snprintf(extra, sizeof(extra), "keys=%s&datas=%s", keys ? keys : "", datas ? datas : "");
    return send_api(c, "SetMysData.in", extra, out_resp);
}

/* appbadpush.in：上报异常信息。 */
int bsphp_app_bad_push(bsphp_client_t *c, const char *table, char **out_resp) {
    char extra[1024];
    snprintf(extra, sizeof(extra), "table=%s", table ? table : "");
    return send_api(c, "appbadpush.in", extra, out_resp);
}

/* SetAppRemarks.ic：写入卡备注。 */
int bsphp_set_app_remarks(bsphp_client_t *c, const char *icid, const char *icpwd, const char *remarks, char **out_resp) {
    char extra[2048];
    snprintf(extra, sizeof(extra), "icid=%s&icpwd=%s&remarks=%s",
             icid ? icid : "", icpwd ? icpwd : "", remarks ? remarks : "");
    return send_api(c, "SetAppRemarks.ic", extra, out_resp);
}

/* cancellation.ic：注销。 */
int bsphp_logout_ic(bsphp_client_t *c, char **out_resp) {
    return send_api(c, "cancellation.ic", "", out_resp);
}

/* 通用接口：适配未封装 API。 */
int bsphp_api_call(bsphp_client_t *c, const char *api, const char *extra_query, char **out_resp) {
    if (!c || !api || !out_resp) return 0;
    return send_api(c, api, (extra_query ? extra_query : ""), out_resp);
}
