package com.lib.chottu_link;

import android.net.Uri;

import com.chottulink.lib.ChottuLink;
import com.chottulink.lib.ChottuLink.AppLinkData;
import com.chottulink.lib.ChottuLink.AttributionData;
import com.chottulink.lib.ChottuLink.ConversionMeta;
import com.chottulink.lib.ChottuLink.CustomerMeta;
import com.chottulink.lib.ChottuLink.LeadMeta;
import com.chottulink.lib.DynamicLink;
import com.unity3d.player.UnityPlayer;

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

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Java bridge between Unity C# scripts and the native ChottuLink Android SDK.
 * All public static methods are called from C# via AndroidJavaClass.
 */
public class ChottuLinkPlugin {

    private static final String TAG = "ChottuLinkUnity";
    private static final String GO_NAME = "___ChottuLinkListener"; // name of GameObject created in C#

    // Utility: forward a message to Unity C#
    private static void send(String method, String msg) {
        try{
            UnityPlayer.UnitySendMessage(GO_NAME, method, msg == null ? "" : msg);
        }catch (Exception ignore){}
    }

    private static void sendSuccessData(Object data) {
        try {
            JSONObject envelope = new JSONObject();
            envelope.put("data", toJsonCompatible(data));
            send("OnSuccess", envelope.toString());
        } catch (Exception e) {
            send("OnSuccess", "{\"data\":null}");
        }
    }

    private static void sendError(String code, String message, String details) {
        try {
            JSONObject envelope = new JSONObject();
            envelope.put("code", code == null ? "native_error" : code);
            envelope.put("message", message == null ? "Unknown error" : message);
            envelope.put("details", details == null ? "" : details);
            send("OnError", envelope.toString());
        } catch (Exception e) {
            send("OnError", "{\"code\":\"native_error\",\"message\":\"Unknown error\",\"details\":\"\"}");
        }
    }

    // ------------------------------------------------ PUBLIC API ------------------------------------------------
    /** Initialise the native SDK. */
    public static void init(String apiKey) {
        try {
            ChottuLink.init(UnityPlayer.currentActivity.getApplicationContext(), apiKey);

            // Check whether the launch intent already contains a deep link and, if so, forward it.
            ChottuLink.getAppLinkData(UnityPlayer.currentActivity.getIntent())
                    .addOnSuccessListener(ChottuLinkPlugin::forwardDeepLink)
                    .addOnFailureListener(e -> send("OnError", "getAppLinkData failed: " + e.getMessage()));
        } catch (Exception e) {
            send("OnError", "Init exception: " + e.getMessage());
        }
    }

    /** Build a short dynamic link from JSON parameters. */
    public static void createDynamicLink(String jsonParams) {
        try {
            JSONObject j = new JSONObject(jsonParams);
            Uri linkUri   = Uri.parse(j.getString("link"));
            String domain = j.getString("domain");
            int androidBehaviour = j.optInt("androidBehaviour", 2); // defaults to APP behaviour
            int iosBehaviour     = j.optInt("iosBehaviour", 2);

            DynamicLink.Builder builder = ChottuLink.createDynamicLink()
                    .setLink(linkUri)
                    .setDomain(domain)
                    .androidBehavior(androidBehaviour)
                    .iosBehavior(iosBehaviour);

            // Optional parameters – only set when present & non-empty ------------------------------
            addIfPresent(j, "utmSource",        builder::setUtmSource);
            addIfPresent(j, "utmMedium",        builder::setUtmMedium);
            addIfPresent(j, "utmCampaign",      builder::setUtmCampaign);
            addIfPresent(j, "utmContent",       builder::setUtmContent);
            addIfPresent(j, "utmTerm",          builder::setUtmTerm);
            addIfPresent(j, "linkName",         builder::setLinkName);
            addIfPresent(j, "selectedPath",     builder::setSelectedPath);
            addIfPresent(j, "socialDescription",builder::setSocialDescription);
            addIfPresent(j, "socialImageUrl",   builder::setSocialImageUrl);
            addIfPresent(j, "socialTitle",      builder::setSocialTitle);

            builder.build()
                    .addOnSuccessListener(dl -> send("OnSuccess", dl.getUri().toString()))
                    .addOnFailureListener(e -> send("OnError", "Create link failed: " + e.getMessage()));
        } catch (Exception e) {
            send("OnError", "createDynamicLink exception: " + e.getMessage());
        }
    }

    /** Resolve a previously created short link back to its deep-link data. */
    public static void resolveLink(String shortUrl) {
        try {
            ChottuLink.getAppLinkDataFromUri(Uri.parse(shortUrl))
                    .addOnSuccessListener(appLinkData -> {
                        if (appLinkData == null || appLinkData.getLink() == null) {
                            send("OnError", "Resolve returned null");
                            return;
                        }

                        JSONObject out = new JSONObject();
                        try {
                            out.put("link", appLinkData.getLink().toString());
                            String shortLink = appLinkData.getShortLink() != null ? appLinkData.getShortLink().toString() : "";
                            String shortLinkRaw = appLinkData.getShortLinkRaw() != null ? appLinkData.getShortLinkRaw().toString() : "";
                            out.put("shortLink", shortLink);
                            out.put("shortLinkRaw", shortLinkRaw);
                            out.put("isDeferred", appLinkData.isDeferred());
                        } catch (Exception ignored) { }

                        send("OnSuccess", out.toString());
                    })
                    .addOnFailureListener(e -> send("OnError", "Resolve failed: " + e.getMessage()));
        } catch (Exception e) {
            send("OnError", "resolveLink exception: " + e.getMessage());
        }
    }

    public static void identify(String jsonPayload) {
        try {
            JSONObject payload = new JSONObject(jsonPayload);
            String id = payload.optString("id", "");
            if (id.isEmpty()) {
                sendError("validation_error", "CustomerMeta.id is required", "identify");
                return;
            }

            CustomerMeta.Builder b = new CustomerMeta.Builder();
            b.setId(id);
            String v;
            if ((v = optNullableString(payload, "name")) != null) b.setName(v);
            if ((v = optNullableString(payload, "email")) != null) b.setEmail(v);
            if ((v = optNullableString(payload, "phone")) != null) b.setPhone(v);
            if ((v = optNullableString(payload, "emailSha256")) != null) b.setEmailSha256(v);
            if ((v = optNullableString(payload, "phoneSha256")) != null) b.setPhoneSha256(v);

            ChottuLink.identify(b.build());
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "identify");
        }
    }

    public static void trackLead(String jsonPayload) {
        try {
            JSONObject payload = new JSONObject(jsonPayload);
            LeadMeta.Builder b = new LeadMeta.Builder();
            String en = optNullableString(payload, "eventName");
            if (en != null) b.setEventName(en);
            Map<String, Object> meta = toMap(payload.optJSONObject("metadata"));
            if (meta != null) b.setMetadata(meta);

            ChottuLink.trackLead(b.build());
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "trackLead");
        }
    }

    public static void trackConversion(String jsonPayload) {
        try {
            JSONObject payload = new JSONObject(jsonPayload);
            if (!payload.has("revenue")) {
                sendError("validation_error", "ConversionMeta.revenue is required", "trackConversion");
                return;
            }

            ConversionMeta.Builder b = new ConversionMeta.Builder();
            b.setRevenue(payload.optDouble("revenue"));
            String v;
            if ((v = optNullableString(payload, "currency")) != null) b.setCurrency(v);
            if ((v = optNullableString(payload, "eventName")) != null) b.setEventName(v);
            if ((v = optNullableString(payload, "productId")) != null) b.setProductId(v);
            if ((v = optNullableString(payload, "transactionId")) != null) b.setTransactionId(v);
            Map<String, Object> meta = toMap(payload.optJSONObject("metadata"));
            if (meta != null) b.setMetadata(meta);

            ChottuLink.trackConversion(b.build());
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "trackConversion");
        }
    }

    public static void trackEvent(String jsonPayload) {
        try {
            JSONObject payload = new JSONObject(jsonPayload);
            String eventName = payload.optString("eventName", "");
            if (eventName.isEmpty()) {
                sendError("validation_error", "eventName is required", "trackEvent");
                return;
            }

            ChottuLink.trackEvent(eventName, toMap(payload.optJSONObject("metadata")));
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "trackEvent");
        }
    }

    public static void flush() {
        try {
            ChottuLink.flush();
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "flush");
        }
    }

    public static void logout() {
        try {
            ChottuLink.logout();
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "logout");
        }
    }

    public static void optOut(boolean enabled) {
        try {
            ChottuLink.optOut(enabled);
            sendSuccessData("ok");
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "optOut");
        }
    }

    public static void isOptedOut() {
        try {
            sendSuccessData(ChottuLink.isOptedOut());
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "isOptedOut");
        }
    }

    public static void getAttributionData() {
        try {
            AttributionData attribution = ChottuLink.getAttributionData();
            if (attribution == null) {
                sendSuccessData(new JSONObject());
                return;
            }
            sendSuccessData(attributionDataToJson(attribution));
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "getAttributionData");
        }
    }

    public static void getApiKey() {
        try {
            String key = ChottuLink.getApiKey();
            sendSuccessData(key == null ? "" : key);
        } catch (Exception e) {
            sendError("native_error", e.getMessage(), "getApiKey");
        }
    }

    /** To be called from the main activity's onNewIntent */
    public static void onNewIntent(android.content.Intent intent) {
        try {
            ChottuLink.getAppLinkData(intent)
                    .addOnSuccessListener(ChottuLinkPlugin::forwardDeepLink)
                    .addOnFailureListener(e -> send("OnError", "getAppLinkData failed: " + e.getMessage()));
        } catch (Exception e) {
            send("OnError", "onNewIntent exception: " + e.getMessage());
        }
    }

    // ------------------------------------------------ helpers ---------------------------------------------------
    private static void forwardDeepLink(AppLinkData data) {
        if (data != null && data.getLink() != null) {
            JSONObject out = new JSONObject();
            try {
                out.put("link", data.getLink().toString());
                String shortLink = data.getShortLink() != null ? data.getShortLink().toString() : "";
                String shortLinkRaw = data.getShortLinkRaw() != null ? data.getShortLinkRaw().toString() : "";
                out.put("shortLink", shortLink);
                out.put("shortLinkRaw", shortLinkRaw);
                out.put("isDeferred", data.isDeferred());
            } catch (Exception ignored) { }

            send("OnDeepLink", out.toString());
        }
    }

    // functional interface to avoid verbose setters
    private interface StringSetter { void set(String v); }

    private static void addIfPresent(JSONObject j, String key, StringSetter setter) {
        String v = j.optString(key, "");
        if (!v.isEmpty()) setter.set(v);
    }

    /** Maps SDK {@link AttributionData} to the JSON keys Unity expects (aligned with iOS). */
    private static JSONObject attributionDataToJson(AttributionData a) throws JSONException {
        JSONObject o = new JSONObject();
        o.put("attributionType", a.getAttributionType());
        o.put("installId", a.getInstallId() == null ? JSONObject.NULL : a.getInstallId());
        o.put("matchFound", a.isMatchFound());
        o.put("matchType", a.getMatchType() == null ? JSONObject.NULL : a.getMatchType());
        o.put("shortUrl", a.getShortUrl() == null ? JSONObject.NULL : a.getShortUrl());
        o.put("clickedShortUrl", a.getClickedShortUrl() == null ? JSONObject.NULL : a.getClickedShortUrl());
        o.put("destinationUrl", a.getDestinationUrl() == null ? JSONObject.NULL : a.getDestinationUrl());
        o.put("destinationWithUtm", a.getDestinationWithUtm() == null ? JSONObject.NULL : a.getDestinationWithUtm());
        o.put("utmSource", a.getUtmSource() == null ? JSONObject.NULL : a.getUtmSource());
        o.put("utmMedium", a.getUtmMedium() == null ? JSONObject.NULL : a.getUtmMedium());
        o.put("utmCampaign", a.getUtmCampaign() == null ? JSONObject.NULL : a.getUtmCampaign());
        o.put("utmTerm", a.getUtmTerm() == null ? JSONObject.NULL : a.getUtmTerm());
        o.put("utmContent", a.getUtmContent() == null ? JSONObject.NULL : a.getUtmContent());
        o.put("isAttributed", a.isAttributed());
        return o;
    }

    private static String optNullableString(JSONObject object, String key) {
        if (!object.has(key) || object.isNull(key)) return null;
        String value = object.optString(key, "");
        return value.isEmpty() ? null : value;
    }

    private static Map<String, Object> toMap(JSONObject object) {
        if (object == null) return null;
        Map<String, Object> map = new HashMap<>();
        Iterator<String> keys = object.keys();
        while (keys.hasNext()) {
            String key = keys.next();
            Object value = object.opt(key);
            if (value instanceof JSONObject) {
                map.put(key, toMap((JSONObject) value));
            } else if (value instanceof JSONArray) {
                map.put(key, value.toString());
            } else if (value == JSONObject.NULL) {
                map.put(key, null);
            } else {
                map.put(key, value);
            }
        }
        return map;
    }

    private static Object toJsonCompatible(Object value) {
        if (value == null) return JSONObject.NULL;
        if (value instanceof Map) return new JSONObject((Map<?, ?>) value);
        return value;
    }
}