package com.indieexchange.security;

import android.app.Activity;
import com.google.android.play.core.integrity.IntegrityManagerFactory;
import com.google.android.play.core.integrity.StandardIntegrityManager;
import com.google.android.play.core.integrity.StandardIntegrityManager.PrepareIntegrityTokenRequest;
import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenRequest;
import com.unity3d.player.UnityPlayer;
import org.json.JSONObject;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import java.security.KeyStore;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;

/** No shared secret is embedded in the SDK. Google signs each request hash. */
public final class IndieIntegrity {
    private static StandardIntegrityManager.StandardIntegrityTokenProvider provider;
    private static long project;
    private static final String ALIAS = "IndieExchange.Attest.v2";
    private static KeyStore store() throws Exception {
        KeyStore keys = KeyStore.getInstance("AndroidKeyStore"); keys.load(null);
        if (!keys.containsAlias(ALIAS)) {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
            generator.initialize(new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_SIGN)
                .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")).setDigests(KeyProperties.DIGEST_SHA256).build());
            generator.generateKeyPair();
        }
        return keys;
    }
    public static synchronized String publicKey() throws Exception {
        return Base64.encodeToString(store().getCertificate(ALIAS).getPublicKey().getEncoded(), Base64.NO_WRAP);
    }

    public static void request(Activity activity, String receiver, String id, String projectNumber, String hash) {
        activity.runOnUiThread(() -> {
            try {
                long number = Long.parseLong(projectNumber);
                if (number <= 0) { finish(receiver, id, ""); return; }
                if (provider != null && project == number) { token(receiver, id, hash); return; }
                StandardIntegrityManager manager = IntegrityManagerFactory.createStandard(activity.getApplicationContext());
                manager.prepareIntegrityToken(PrepareIntegrityTokenRequest.builder().setCloudProjectNumber(number).build())
                    .addOnSuccessListener(value -> { provider = value; project = number; token(receiver, id, hash); })
                    .addOnFailureListener(error -> { provider = null; finish(receiver, id, ""); });
            } catch (Exception error) { provider = null; finish(receiver, id, ""); }
        });
    }
    private static void token(String receiver, String id, String hash) {
        provider.request(StandardIntegrityTokenRequest.builder().setRequestHash(hash).build())
            .addOnSuccessListener(result -> finishSigned(receiver, id, result.token(), hash))
            .addOnFailureListener(error -> { provider = null; finish(receiver, id, ""); });
    }
    private static void finishSigned(String receiver, String id, String token, String hash) {
        try {
            Signature signer = Signature.getInstance("SHA256withECDSA");
            signer.initSign((java.security.PrivateKey)store().getKey(ALIAS, null));
            signer.update(Base64.decode(hash, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP));
            JSONObject json = new JSONObject(); json.put("id", id); json.put("proof", token);
            json.put("publicKey", publicKey()); json.put("signature", Base64.encodeToString(signer.sign(), Base64.NO_WRAP));
            UnityPlayer.UnitySendMessage(receiver, "Complete", json.toString());
        } catch (Exception error) { finish(receiver, id, ""); }
    }
    private static void finish(String receiver, String id, String token) {
        try { JSONObject json = new JSONObject(); json.put("id", id); json.put("proof", token);
            UnityPlayer.UnitySendMessage(receiver, "Complete", json.toString()); } catch (Exception ignored) { }
    }
}
