package com.amazon.ads.apidemo.utils;

import android.util.Log;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.ToNumberPolicy;

import org.json.JSONObject;

import java.lang.reflect.Type;

/**
 * Utility class for JSON operations including serialization, deserialization, and validation.
 */
public class JsonUtils {
    private JsonUtils() {
    }

    /**
     * Get configured Gson instance with custom number parsing strategy.
     *
     * @return Gson instance configured for long/double number parsing
     */
    public static Gson gson() {
        final Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create();
        return gson;
    }

    /**
     * Convert object to JSON string.
     *
     * @param o Object to convert
     * @return JSON string representation
     */
    public static String toJson(Object o) {
        return gson().toJson(o);
    }

    /**
     * Parse JSON string to object of specified type.
     *
     * @param json JSON string to parse
     * @param type Target class type
     * @return Parsed object or null if parsing fails
     */
    public static <T> T fromJson(String json, Class<T> type) {
        try {
            return gson().fromJson(json, type);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Parse JSON string to object of specified type.
     *
     * @param json JSON string to parse
     * @param type Target type
     * @return Parsed object or null if parsing fails
     */
    public static <T> T fromJson(String json, Type type) {
        try {
            return gson().fromJson(json, type);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Check if a string is a valid JSON.
     *
     * @param str String to check
     * @return True if string is a valid JSON, false otherwise
     */
    public static boolean isJson(String str) {
        if (str == null) {
            return false;
        }
        try {
            gson().fromJson(str, Dict.class);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Get optional string value from JSONObject.
     *
     * @param j JSONObject to retrieve value from
     * @param k Key to retrieve value for
     * @return String value or null if key is not present or value is "null"
     */
    public static String optString(JSONObject j, String k) {
        String value = j.optString(k);
        if ("null".equals(value)) {
            value = null;
        }

        return value;
    }

    /**
     * Remove null fields from JSON string.
     *
     * @param json JSON string to clean
     * @return Cleaned JSON string with null fields removed
     */
    public static String removeNullFields(String json) {
        Log.i("ADS JsonUtils", json);
        String cleanedJsonString = json;

        try {
            Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).setExclusionStrategies(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes f) {
                    return f.getDeclaringClass().equals(JsonElement.class) && f.getName().equals("nullValue");
                }

                @Override
                public boolean shouldSkipClass(Class<?> clazz) {
                    return false;
                }
            }).create();

            JsonElement jsonElement = gson.fromJson(json, JsonElement.class);
            cleanedJsonString = gson.toJson(jsonElement);
        } catch (Exception e) {
            // L.e(e);
        }

        return cleanedJsonString;
    }

    /**
     * Wrap response JSON string with "data" key.
     *
     * @param jsonString JSON string to wrap
     * @return Wrapped JSON string with "data" key
     */
    public static String wrapResponse(String jsonString) {
        try {
            JsonElement element = JsonParser.parseString(jsonString);
            JsonObject jsonObject = element.getAsJsonObject();

            JsonElement dataElement = jsonObject.get("data");
            if (dataElement != null && !dataElement.isJsonObject()) {
                JsonObject wrappedData = new JsonObject();
                wrappedData.add("raw", dataElement);
                jsonObject.add("data", wrappedData);
            }

            return gson().toJson(jsonObject);
        } catch (Exception e) {
            return jsonString;
        }
    }

    /**
     * Sanitize API response JSON string by removing null fields and wrapping "data" key.
     *
     * @param jsonString JSON string to sanitize
     * @return Sanitized JSON string
     */
    public static String sanitizeApiResponse(String jsonString) {
        try {
            Gson gson = new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).setExclusionStrategies(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes f) {
                    return f.getDeclaringClass().equals(JsonElement.class) && f.getName().equals("nullValue");
                }

                @Override
                public boolean shouldSkipClass(Class<?> clazz) {
                    return false;
                }
            }).create();

            JsonElement jsonElement = gson.fromJson(jsonString, JsonElement.class);
            String cleanedJson = gson.toJson(jsonElement);
            return wrapResponse(cleanedJson);
        } catch (Exception e) {
            return jsonString;
        }
    }
}
