package com.yutou.biliapi.net;

import com.alibaba.fastjson2.JSONObject;
import com.yutou.biliapi.api.LoginApi;
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
import com.yutou.biliapi.bean.login.LoginInfoBean;
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
import com.yutou.common.okhttp.HttpBody;
import com.yutou.common.okhttp.HttpLoggingInterceptor;
import com.yutou.common.utils.ConfigTools;
import com.yutou.common.utils.Log;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.io.IOUtils;
import retrofit2.Response;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Iterator;

public class BiliCookieManager {
    public static final int COOKIE_INVALID = -101;
    public static final int COOKIE_SUCCESS = 0;


    private static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" +
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLgd2OAkcGVtoE3ThUREbio0Eg\n" +
            "Uc/prcajMKXvkCKFCWhJYJcLkcM2DKKcSeFpD/j6Boy538YXnR6VhcuUJOhH2x71\n" +
            "nzPjfdTcqMz7djHum0qSZA0AyCBDABUqCrfNgCiJ00Ra7GmRj+YCK1NJEuewlb40\n" +
            "JNrRuoEUXpabUzGB8QIDAQAB\n" +
            "-----END PUBLIC KEY-----";


    /**
     * <a href="https://socialsisteryi.github.io/bilibili-API-collect/docs/login/cookie_refresh.html#java">文档地址</a>
     */
    public static String getCorrespondPath(String plaintext) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        String publicKeyStr = PUBLIC_KEY
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replace("\n", "")
                .trim();
        byte[] publicBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        String algorithm = "RSA/ECB/OAEPPadding";
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        // Encode the plaintext to bytes
        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);

        // Add OAEP padding to the plaintext bytes
        OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
        // Encrypt the padded plaintext bytes
        byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
        // Convert the encrypted bytes to a Base64-encoded string
        return new BigInteger(1, encryptedBytes).toString(16);
    }

    private static String getRefreshCsrf(String htmlContent) {
        htmlContent = htmlContent.split("<div id=\"1-name\">")[1].split("</div>")[0];
        return htmlContent;
    }

    public static boolean refreshCookie(LoginCookieDatabaseBean cookie) {
        try {
            LoginApi api = BiliLoginNetApiManager.getInstance().getLoginApi(cookie.getDedeUserID());
            String correspondPath = getCorrespondPath(String.format("refresh_%d", System.currentTimeMillis()));
            Log.i("correspondPath = " + correspondPath);
            var body = okhttp(correspondPath, cookie);
            String refreshCsrf = getRefreshCsrf(body);
            Log.i("body = " + refreshCsrf);
            String oldCsrf = cookie.getRefreshToken();
            Response<HttpBody<LoginInfoBean>> newCookie = api.refreshCookie(cookie.getBiliJct(), refreshCsrf, "main_web", cookie.getRefreshToken()).execute();
            LoginCookieDatabaseBean nc = BiliLoginNetApiManager.getInstance().getCookie(newCookie.headers(), newCookie.body());
            nc.setGourl(cookie.getGourl());
            nc.setSql_time(cookie.getSql_time());
            BiliBiliLoginDatabase.getInstance().updateLoginCookie(nc);
            api.confirmRefreshCookie(refreshCsrf, oldCsrf).execute();
            Log.i("返回正确");
            return true;
        } catch (Exception e) {
            Log.e(e);
            return false;
        }
    }


    public static String okhttp(String correspondPath, LoginCookieDatabaseBean cookie) {
        JSONObject json = JSONObject.parseObject(JSONObject.toJSONString(cookie));
        StringBuilder ck = new StringBuilder();
        json.remove("sql_time");
        json.remove("gourl");
        json.remove("Expires");
        json.remove("path");
        json.remove("refresh_token");
        json.put("SESSDATA", URLDecoder.decode(json.getString("SESSDATA"), StandardCharsets.UTF_8));

        // 构建Cookie字符串，去掉最后一个分号
        Iterator<String> keys = json.keySet().iterator();
        while (keys.hasNext()) {
            String key = keys.next();
            ck.append(key).append("=").append(json.getString(key));
            if (keys.hasNext()) {
                ck.append(";");
            }
        }
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("https://www.bilibili.com/correspond/1/" + correspondPath)
                .method("GET", null) // GET请求不带body
                .addHeader("Cookie", ck.toString())
                .addHeader("User-Agent", ConfigTools.getUserAgent())
                .addHeader("Accept", "*/*")
                .addHeader("Host", "www.bilibili.com")
                .addHeader("Connection", "keep-alive")
                .build();

        try {
            okhttp3.Response execute = client.newCall(request).execute();
            while (execute.code() == 404) {
                execute = client.newCall(request).execute();
            }
            return IOUtils.toString(execute.body().byteStream(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new RuntimeException("Failed to execute HTTP request", e);
        }
    }
}
