package com.nadi.player.deviceidentity;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;

import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

import java.io.BufferedReader;
import java.io.FileReader;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;

/**
 * DeviceIdentity — Capacitor plugin that returns the most stable, "MAC-like"
 * identifier the platform allows the app to read.
 *
 * <p>Android 6+ deliberately blocks the real WiFi MAC. We try (in order):
 * <ol>
 *   <li>WiFi-card MAC via {@link WifiInfo#getMacAddress()} — works only on
 *       Android &lt; 6 or rooted devices.</li>
 *   <li>{@code /sys/class/net/wlan0/address} — sometimes readable even on
 *       Android 7+ for the actual physical MAC.</li>
 *   <li>{@link NetworkInterface} for any non-loopback interface — last
 *       resort for ethernet (Android TV).</li>
 *   <li>{@link Settings.Secure#ANDROID_ID} converted to MAC format
 *       ({@code XX:XX:XX:XX:XX:XX}) — guaranteed to exist, stable across
 *       app launches, unique per device + app-signing-key.</li>
 * </ol>
 *
 * <p>The {@code source} field in the response tells the JS layer which
 * level of fallback was hit, so the UI can decide whether to display a
 * "MAC Address" or "Device ID" label.
 */
@CapacitorPlugin(name = "DeviceIdentity")
public class DeviceIdentityPlugin extends Plugin {

    private static final String FALLBACK_MAC = "00:00:00:00:00:00";

    @PluginMethod
    public void getMac(PluginCall call) {
        Context ctx = getContext();
        String mac = null;
        String source = "android_id";

        // 1. Legacy WifiInfo (works on Android < 6 / rooted)
        try {
            WifiManager wm = (WifiManager) ctx.getApplicationContext()
                    .getSystemService(Context.WIFI_SERVICE);
            if (wm != null) {
                @SuppressLint("HardwareIds")
                WifiInfo info = wm.getConnectionInfo();
                if (info != null) {
                    String candidate = info.getMacAddress();
                    if (isUsableMac(candidate)) {
                        mac = candidate.toUpperCase(Locale.ROOT);
                        source = "wifi";
                    }
                }
            }
        } catch (Throwable ignored) { /* permissions or no wifi */ }

        // 2. /sys/class/net/wlan0/address
        if (mac == null) {
            String fromSys = readMacFromSysFile("wlan0");
            if (isUsableMac(fromSys)) {
                mac = fromSys.toUpperCase(Locale.ROOT);
                source = "sysfs";
            }
        }

        // 3. NetworkInterface (covers eth0 on Android TV)
        if (mac == null) {
            try {
                Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
                if (nis != null) {
                    for (NetworkInterface ni : Collections.list(nis)) {
                        if (ni.isLoopback()) continue;
                        byte[] hw = ni.getHardwareAddress();
                        if (hw == null || hw.length != 6) continue;
                        StringBuilder sb = new StringBuilder(17);
                        for (int i = 0; i < hw.length; i++) {
                            if (i > 0) sb.append(':');
                            sb.append(String.format(Locale.ROOT, "%02X", hw[i] & 0xff));
                        }
                        String candidate = sb.toString();
                        if (isUsableMac(candidate)) {
                            mac = candidate;
                            source = "netif:" + ni.getName();
                            break;
                        }
                    }
                }
            } catch (Throwable ignored) { /* SecurityException on some OEMs */ }
        }

        // 4. Fallback: Settings.Secure.ANDROID_ID formatted as MAC
        @SuppressLint("HardwareIds")
        String androidId = Settings.Secure.getString(
                ctx.getContentResolver(), Settings.Secure.ANDROID_ID);
        if (androidId == null) androidId = "";
        // Strip non-hex, pad to 12, slice to 12, format as XX:XX:XX:XX:XX:XX
        String hex = androidId.toUpperCase(Locale.ROOT).replaceAll("[^0-9A-F]", "");
        if (hex.length() < 12) hex = (hex + "000000000000").substring(0, 12);
        else hex = hex.substring(0, 12);
        // Set "locally-administered" bit so we don't masquerade as a real OUI
        char[] c = hex.toCharArray();
        int firstByte = Integer.parseInt(new String(c, 0, 2), 16);
        firstByte = (firstByte | 0x02) & 0xFE;
        String firstByteStr = String.format(Locale.ROOT, "%02X", firstByte);
        StringBuilder formatted = new StringBuilder(17);
        formatted.append(firstByteStr).append(':');
        for (int i = 2; i < 12; i += 2) {
            formatted.append(c, i, 2);
            if (i < 10) formatted.append(':');
        }
        String pseudoMac = formatted.toString();

        if (mac == null) {
            mac = pseudoMac;
            source = TextUtils.isEmpty(androidId) ? "fallback" : "android_id";
        }

        JSObject ret = new JSObject();
        ret.put("mac", mac);
        ret.put("source", source);
        ret.put("android_id", androidId);
        ret.put("model", Build.MANUFACTURER + " " + Build.MODEL);
        ret.put("os_version", String.valueOf(Build.VERSION.SDK_INT));
        call.resolve(ret);
    }

    // ── helpers ─────────────────────────────────────────────────────────

    private static boolean isUsableMac(String s) {
        if (s == null) return false;
        s = s.trim();
        if (s.isEmpty()) return false;
        if (s.equalsIgnoreCase(FALLBACK_MAC)) return false;
        if (s.equalsIgnoreCase("02:00:00:00:00:00")) return false;
        return s.matches("(?i)^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$");
    }

    private static String readMacFromSysFile(String iface) {
        BufferedReader r = null;
        try {
            r = new BufferedReader(new FileReader("/sys/class/net/" + iface + "/address"));
            String line = r.readLine();
            return line == null ? null : line.trim();
        } catch (Throwable ignored) {
            return null;
        } finally {
            if (r != null) try { r.close(); } catch (Throwable ignored2) {}
        }
    }
}
