package com.monet.adapt;

/*
 * 图标包适配 API —— 接入 SDK 源码（示例实现）
 *
 * 用途：在你的图标包 App 启动时，弹出授权弹窗（拦截下方所有触控）；
 *   用户点【拒绝】则退出应用；点【确定并授权】后读取设备上所有应用的
 *   包名 / 主 Activity 类名 / 图标，上报到本服务，服务端按包名/类名自动
 *   匹配进你的图标包，帮助你了解需要适配哪些应用。
 *
 * 使用：在你的启动 Activity.onCreate 里调用：
 *     AdaptCollector.start(this,
 *         "https://monet-apptracker.pages.dev",
 *         "mat_你的适配token",     // 图标包适配API 生成
 *         "mau_你的使用人数token"); // 使用人数API 生成（用于统计接入应用的使用人数）
 *   usageToken 传空字符串则不统计使用人数。
 *
 * 说明：需自行用 Android 构建工具(d8)把本源码编译进 classes.dex，
 *   或直接把本类加入你的工程一起编译。见 build-dex.txt。
 *
 * 免责声明（弹窗内已展示）：仅用于获取用户桌面图标/安装包名/类名来改进
 *   图标包适配率，无其他用途。
 */

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.provider.Settings;
import android.util.Base64;
import android.view.WindowManager;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public final class AdaptCollector {

    private static final String DISCLAIMER =
            "本应用需要读取你设备上的应用图标与名称，用于改进图标包适配率。\n\n" +
            "免责声明：仅用于获取用户桌面图标 / 安装包名 / 类名来改进图标包适配率，无其他用途。";

    private AdaptCollector() {}

    /** 在启动 Activity.onCreate 中调用。baseUrl 例如 https://monet-apptracker.pages.dev
     *  adaptToken：图标包适配API token(mat_)；usageToken：使用人数API token(mau_)，传空则不统计使用人数 */
    public static void start(final Activity activity, final String baseUrl,
                             final String adaptToken, final String usageToken) {
        // 弹窗拦截下方所有触控：不可取消、点击外部无效
        AlertDialog dialog = new AlertDialog.Builder(activity)
                .setTitle("图标适配授权")
                .setMessage(DISCLAIMER)
                .setCancelable(false)
                .setPositiveButton("确定并授权", (d, w) -> {
                    d.dismiss();
                    // 授权后：确保拥有读取全部已安装应用的权限，再读取并上报
                    ensureQueryAllPackages(activity);
                    new ReportTask(activity.getApplicationContext(), baseUrl, adaptToken, usageToken).execute();
                })
                .setNegativeButton("拒绝", (d, w) -> {
                    d.dismiss();
                    // 拒绝则退出到桌面
                    activity.finishAffinity();
                })
                .create();
        // 进一步保证弹窗期间拦截触控（模态）
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();
        if (dialog.getWindow() != null) {
            dialog.getWindow().setFlags(
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 0);
        }
    }

    /**
     * 确保拥有“读取设备上所有已安装应用”的权限。
     * Android 11+(API 30) 需在 Manifest 声明 QUERY_ALL_PACKAGES（属安装期权限，
     * 系统不弹运行时框），此处做一次可见性自检：若声明生效则可枚举全部启动项；
     * 若被系统/机型限制导致仅能看到极少应用，则跳转到本应用的“应用信息”页，
     * 便于用户手动确认相关权限。
     */
    private static void ensureQueryAllPackages(Activity activity) {
        try {
            PackageManager pm = activity.getPackageManager();
            Intent launcher = new Intent(Intent.ACTION_MAIN);
            launcher.addCategory(Intent.CATEGORY_LAUNCHER);
            int visible = pm.queryIntentActivities(launcher, 0).size();
            if (visible <= 3) {
                // 可见应用异常少，可能未获得全部应用可见性，引导到应用详情页
                Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                i.setData(android.net.Uri.parse("package:" + activity.getPackageName()));
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                activity.startActivity(i);
            }
        } catch (Throwable ignored) {
        }
    }

    /** 读取所有可启动应用的 包名/主Activity/名称/图标，分批上报 */
    private static final class ReportTask extends AsyncTask<Void, Void, Void> {
        private final Context ctx;
        private final String baseUrl;
        private final String adaptToken;
        private final String usageToken;

        ReportTask(Context ctx, String baseUrl, String adaptToken, String usageToken) {
            this.ctx = ctx;
            this.baseUrl = baseUrl.replaceAll("/+$", "");
            this.adaptToken = adaptToken;
            this.usageToken = usageToken;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            // 1) 使用人数上报（与使用人数API同款：设备唯一ID去重统计），失败不影响适配上报
            sendUsagePing();
            try {
                PackageManager pm = ctx.getPackageManager();
                Intent launcher = new Intent(Intent.ACTION_MAIN);
                launcher.addCategory(Intent.CATEGORY_LAUNCHER);
                List<ResolveInfo> apps = pm.queryIntentActivities(launcher, 0);

                JSONArray batch = new JSONArray();
                for (ResolveInfo ri : apps) {
                    String pkg = ri.activityInfo.packageName;
                    String cls = ri.activityInfo.name;
                    CharSequence label = ri.loadLabel(pm);
                    String icon = drawableToDataUrl(ri.loadIcon(pm));

                    JSONObject o = new JSONObject();
                    o.put("packageName", pkg);
                    o.put("mainActivity", cls);
                    o.put("name", label == null ? pkg : label.toString());
                    if (icon != null) o.put("iconDataUrl", icon);
                    batch.put(o);

                    // 每 40 个上报一次（避免单次请求过大）
                    if (batch.length() >= 40) {
                        post(batch);
                        batch = new JSONArray();
                    }
                }
                if (batch.length() > 0) post(batch);
            } catch (Throwable ignored) {
                // 上报失败不影响 App 使用
            }
            return null;
        }

        private void post(JSONArray apps) {
            HttpURLConnection conn = null;
            try {
                JSONObject payload = new JSONObject();
                payload.put("apps", apps);
                byte[] data = payload.toString().getBytes("UTF-8");

                URL url = new URL(baseUrl + "/adapt/report");
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(15000);
                conn.setReadTimeout(20000);
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setRequestProperty("Authorization", "Bearer " + adaptToken);
                conn.setRequestProperty("Content-Type", "application/json");
                OutputStream os = conn.getOutputStream();
                os.write(data);
                os.flush();
                os.close();
                conn.getResponseCode();
            } catch (Throwable ignored) {
            } finally {
                if (conn != null) conn.disconnect();
            }
        }

        /** 使用人数上报（同使用人数API）：/api/usage/ping，用 ANDROID_ID 作设备唯一ID去重 */
        private void sendUsagePing() {
            if (usageToken == null || usageToken.trim().isEmpty()) return;
            HttpURLConnection conn = null;
            try {
                String deviceId = Settings.Secure.getString(
                        ctx.getContentResolver(), Settings.Secure.ANDROID_ID);
                if (deviceId == null || deviceId.isEmpty()) deviceId = "unknown-device";
                JSONObject body = new JSONObject();
                body.put("deviceId", deviceId);
                body.put("appId", ctx.getPackageName());
                byte[] data = body.toString().getBytes("UTF-8");

                URL url = new URL(baseUrl + "/api/usage/ping");
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(15000);
                conn.setReadTimeout(20000);
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setRequestProperty("Authorization", "Bearer " + usageToken);
                conn.setRequestProperty("Content-Type", "application/json");
                OutputStream os = conn.getOutputStream();
                os.write(data);
                os.flush();
                os.close();
                conn.getResponseCode();
            } catch (Throwable ignored) {
            } finally {
                if (conn != null) conn.disconnect();
            }
        }

        /** Drawable -> 144x144 PNG -> data URL（控制体积） */
        private String drawableToDataUrl(Drawable d) {
            try {
                if (d == null) return null;
                int size = 144;
                Bitmap bmp;
                if (d instanceof BitmapDrawable && ((BitmapDrawable) d).getBitmap() != null) {
                    bmp = Bitmap.createScaledBitmap(((BitmapDrawable) d).getBitmap(), size, size, true);
                } else {
                    bmp = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
                    Canvas c = new Canvas(bmp);
                    d.setBounds(0, 0, size, size);
                    d.draw(c);
                }
                java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.PNG, 100, bos);
                String b64 = Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP);
                return "data:image/png;base64," + b64;
            } catch (Throwable t) {
                return null;
            }
        }
    }
}
