package com.bsphp.car;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.net.URI;
import java.net.URLDecoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;

public final class AccountBSPHPClient {
    private static final DateTimeFormatter D1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter D2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'#'HH:mm:ss");
    private final HttpClient http = HttpClient.newHttpClient();
    private String sessl = "";

    public String sessl() { return sessl; }

    public JsonObject send(String api, Map<String, String> params) throws Exception {
        String appsafe = BSPHPCrypto.md5Hex(D1.format(LocalDateTime.now()));
        Map<String, String> p = new LinkedHashMap<>();
        p.put("api", api); p.put("BSphpSeSsL", sessl); p.put("date", D2.format(LocalDateTime.now()));
        p.put("md5", ""); p.put("mutualkey", AccountBSPHPConfig.MUTUAL_KEY); p.put("appsafecode", appsafe);
        p.putAll(params);
        StringBuilder ds = new StringBuilder();
        for (var e : p.entrySet()) {
            if (ds.length() > 0) ds.append('&');
            ds.append(e.getKey()).append('=').append(java.net.URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8));
        }
        String aes = BSPHPCrypto.md5Hex(AccountBSPHPConfig.SERVER_PRIVATE_KEY + appsafe).substring(0, 16);
        String enc = BSPHPCrypto.aes128CbcEncryptBase64(ds.toString(), aes);
        String rsa = BSPHPCrypto.rsaEncryptPkcs1Base64("0|AES-128-CBC|" + aes + "|" + BSPHPCrypto.md5Hex(enc) + "|json", AccountBSPHPConfig.CLIENT_PUBLIC_KEY);
        String body = "parameter=" + BSPHPCrypto.percentEncodePayload(enc + "|" + rsa);

        HttpRequest req = HttpRequest.newBuilder().uri(URI.create(AccountBSPHPConfig.URL))
                .header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8)).build();
        HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        if (resp.statusCode() != 200) return null;
        String raw = URLDecoder.decode(resp.body(), StandardCharsets.UTF_8);
        String[] parts = raw.split("\\|", -1);
        if (parts.length < 3) return null;
        String sig = BSPHPCrypto.rsaDecryptPkcs1Base64(parts[2].trim(), AccountBSPHPConfig.SERVER_PRIVATE_KEY);
        String key = sig.split("\\|")[2].substring(0, 16);
        String dec = BSPHPCrypto.aes128CbcDecryptBase64ToString(parts[1].trim(), key);
        JsonObject root = JsonParser.parseString(dec).getAsJsonObject();
        return root.getAsJsonObject("response");
    }

    public boolean bootstrap() throws Exception {
        JsonObject c = send("internet.in", Map.of());
        if (c == null || !"1".equals(msg(c.get("data")))) return false;
        JsonObject s = send("BSphpSeSsL.in", Map.of());
        if (s == null) return false;
        String t = msg(s.get("data"));
        if (t == null || t.isEmpty()) return false;
        sessl = t;
        return true;
    }

    public JsonObject login(String user, String pwd, String coode) throws Exception {
        JsonObject r = send("login.lg", Map.of("user", user, "pwd", pwd, "coode", coode, "key", user, "maxoror", user));
        if (r != null && r.has("SeSsL")) sessl = msg(r.get("SeSsL"));
        return r;
    }

    public JsonObject loginSms(String mobile, String area, String smsCode, String key, String maxoror, String coode) throws Exception {
        JsonObject r = send("login_sms.lg", Map.of(
                "mobile", mobile,
                "area", area,
                "sms_code", smsCode,
                "key", key,
                "maxoror", maxoror,
                "coode", coode
        ));
        if (r != null && r.has("SeSsL")) sessl = msg(r.get("SeSsL"));
        return r;
    }

    public JsonObject loginEmail(String email, String emailCode, String key, String maxoror, String coode) throws Exception {
        JsonObject r = send("login_email.lg", Map.of(
                "email", email,
                "email_code", emailCode,
                "key", key,
                "maxoror", maxoror,
                "coode", coode
        ));
        if (r != null && r.has("SeSsL")) sessl = msg(r.get("SeSsL"));
        return r;
    }

    public JsonObject registerAccount(String user, String pwd, String pwdb, String coode, String mobile,
                                      String mibaoWenti, String mibaoDaan, String qq, String mail, String extension) throws Exception {
        return send("registration.lg", Map.of(
                "user", user,
                "pwd", pwd,
                "pwdb", pwdb,
                "coode", coode,
                "mobile", mobile,
                "mibao_wenti", mibaoWenti,
                "mibao_daan", mibaoDaan,
                "qq", qq,
                "mail", mail,
                "extension", extension
        ));
    }

    public JsonObject registerSms(String user, String mobile, String area, String smsCode,
                                  String pwd, String pwdb, String key, String coode) throws Exception {
        return send("register_sms.lg", Map.of(
                "user", user,
                "mobile", mobile,
                "area", area,
                "sms_code", smsCode,
                "pwd", pwd,
                "pwdb", pwdb,
                "key", key,
                "coode", coode
        ));
    }

    public JsonObject registerEmail(String user, String email, String emailCode,
                                    String pwd, String pwdb, String key, String coode) throws Exception {
        return send("register_email.lg", Map.of(
                "user", user,
                "email", email,
                "email_code", emailCode,
                "pwd", pwd,
                "pwdb", pwdb,
                "key", key,
                "coode", coode
        ));
    }

    public JsonObject sendSmsCode(String scene, String mobile, String area, String coode) throws Exception {
        return send("send_sms.lg", Map.of(
                "scene", scene,
                "mobile", mobile,
                "area", area,
                "coode", coode
        ));
    }

    public JsonObject sendEmailCode(String scene, String email, String coode) throws Exception {
        return send("send_email.lg", Map.of(
                "scene", scene,
                "email", email,
                "coode", coode
        ));
    }

    public JsonObject unbind(String user, String pwd) throws Exception {
        return send("jiekey.lg", Map.of("user", user, "pwd", pwd));
    }

    public JsonObject recharge(String user, String userpwd, boolean userset, String ka, String pwd) throws Exception {
        return send("chong.lg", Map.of(
                "user", user,
                "userpwd", userpwd,
                "userset", userset ? "1" : "0",
                "ka", ka,
                "pwd", pwd
        ));
    }

    public JsonObject resetSmsPwd(String mobile, String area, String smsCode, String pwd, String pwdb, String coode) throws Exception {
        return send("resetpwd_sms.lg", Map.of(
                "mobile", mobile,
                "area", area,
                "sms_code", smsCode,
                "pwd", pwd,
                "pwdb", pwdb,
                "coode", coode
        ));
    }

    public JsonObject resetEmailPwd(String email, String emailCode, String pwd, String pwdb, String coode) throws Exception {
        return send("resetpwd_email.lg", Map.of(
                "email", email,
                "email_code", emailCode,
                "pwd", pwd,
                "pwdb", pwdb,
                "coode", coode
        ));
    }

    public JsonObject backto(String user, String pwd, String pwdb, String wenti, String daan, String coode) throws Exception {
        return send("backto.lg", Map.of(
                "user", user,
                "pwd", pwd,
                "pwdb", pwdb,
                "wenti", wenti,
                "daan", daan,
                "coode", coode
        ));
    }

    public JsonObject changePassword(String user, String pwd, String pwda, String pwdb, String img) throws Exception {
        return send("password.lg", Map.of(
                "user", user,
                "pwd", pwd,
                "pwda", pwda,
                "pwdb", pwdb,
                "img", img
        ));
    }

    public JsonObject feedback(String user, String pwd, String table, String qq, String leix, String txt, String coode) throws Exception {
        return send("liuyan.in", Map.of(
                "user", user,
                "pwd", pwd,
                "table", table,
                "qq", qq,
                "leix", leix,
                "txt", txt,
                "coode", coode
        ));
    }

    public JsonObject logout() throws Exception {
        JsonObject r = send("cancellation.lg", Map.of());
        sessl = "";
        try { bootstrap(); } catch (Exception ignored) {}
        return r;
    }

    public JsonObject getEndTime() throws Exception { return send("vipdate.lg", Map.of()); }
    public JsonObject getUserInfo() throws Exception { return send("getuserinfo.lg", Map.of()); }
    public JsonObject getUserInfo(String info) throws Exception { return send("getuserinfo.lg", Map.of("info", info)); }
    public JsonObject heartbeat() throws Exception { return send("timeout.lg", Map.of()); }
    public JsonObject getUserKey() throws Exception { return send("userkey.lg", Map.of()); }
    public JsonObject getServerDate() throws Exception { return send("date.in", Map.of()); }
    public JsonObject getPresetURL() throws Exception { return send("url.in", Map.of()); }
    public JsonObject getWebURL() throws Exception { return send("weburl.in", Map.of()); }
    public JsonObject getGlobalInfo() throws Exception { return send("globalinfo.in", Map.of()); }
    public JsonObject getLogicA() throws Exception { return send("logica.in", Map.of()); }
    public JsonObject getLogicB() throws Exception { return send("logicb.in", Map.of()); }
    public JsonObject getLogicInfoA() throws Exception { return send("logicinfoa.in", Map.of()); }
    public JsonObject getLogicInfoB() throws Exception { return send("logicinfob.in", Map.of()); }
    public JsonObject getAppCustom(String info) throws Exception { return send("appcustom.in", Map.of("info", info)); }
    public JsonObject getCodeEnabled() throws Exception { return send("getsetimag.in", Map.of()); }
    public JsonObject getCodeEnabled(String type) throws Exception { return send("getsetimag.in", Map.of("type", type)); }

    public JsonObject call(String api, Map<String, String> params) throws Exception { return send(api, params); }

    public static String msg(JsonElement e) {
        if (e == null || e.isJsonNull()) return "";
        if (e.isJsonPrimitive() && e.getAsJsonPrimitive().isString()) return e.getAsString();
        return e.toString();
    }
}
