package com.amazon.ads.apidemo.utils;

import androidx.annotation.NonNull;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A thread-safe dictionary implementation that extends ConcurrentHashMap.
 * Provides convenient methods for working with key-value pairs including:
 * - Type-safe value retrieval
 * - JSON serialization/deserialization
 * - Builder pattern support
 *
 * @param <T> The type of values stored in the dictionary
 */
public final class Dict<T> extends ConcurrentHashMap<String, T> {
    private static final int INITIAL_CAPACITY = 10;

    /**
     * Builder class for constructing Dict instances fluently.
     *
     * @param <T> The type of values stored in the dictionary
     */
    public static final class Builder<T> {
        private final Dict<T> data = new Dict<T>(INITIAL_CAPACITY);

        /**
         * Adds a key-value pair to the dictionary being built.
         *
         * @param k The key
         * @param v The value
         * @return This builder instance for method chaining
         */
        public Builder<T> put(String k, T v) {
            data.put(k, v);
            return this;
        }

        /**
         * Returns the built Dict instance.
         *
         * @return The constructed Dict
         */
        public Dict<T> dict() {
            return data;
        }

        /**
         * Returns the contents as a standard Map.
         *
         * @return A new Map containing all key-value pairs
         */
        public Map<String, T> map() {
            Map<String, T> map = new ConcurrentHashMap<>(INITIAL_CAPACITY);
            map.putAll(data);
            return map;
        }

        /**
         * Returns the contents as a JSON string.
         *
         * @return JSON representation of the dictionary
         */
        public String json() {
            return JsonUtils.toJson(data);
        }
    }

    /**
     * Creates a Dict from an existing Map.
     *
     * @param map The source map
     * @return A new Dict containing all entries from the map
     */
    public static <T> Dict<T> fromMap(Map map) {
        return new Dict<T>(map);
    }

    /**
     * Creates a Dict from a JSON string.
     *
     * @param json JSON string to parse
     * @return A new Dict containing the parsed data
     */
    public static <T> Dict<T> fromJsonString(String json) {
        return new Dict<T>(JsonUtils.fromJson(json, Map.class));
    }

    /**
     * Creates a new Dict builder.
     *
     * @return A new Builder instance
     */
    public static <T> Builder<T> builder() {
        return new Builder<T>();
    }

    /**
     * Constructs an empty Dict with default initial capacity.
     */
    public Dict() {
        super(INITIAL_CAPACITY);
    }

    /**
     * Constructs an empty Dict with the specified initial capacity.
     *
     * @param initialCapacity The initial capacity
     */
    public Dict(int initialCapacity) {
        super(initialCapacity);
    }

    /**
     * Constructs a Dict with a single key-value pair.
     *
     * @param key The key
     * @param val The value
     */
    public Dict(String key, T val) {
        put(key, val);
    }

    /**
     * Constructs a Dict from an existing Map.
     *
     * @param map The source map
     */
    public Dict(Map<String, T> map) {
        putAll(map);
    }

    /**
     * Constructs a Dict from an object (if it's a Map).
     *
     * @param obj The source object
     */
    public Dict(Object obj) {
        if (obj instanceof Map) {
            putAll((Map) obj);
        }
    }

    /**
     * Returns the dictionary contents as a JSON string.
     *
     * @return JSON representation of this dictionary
     */
    @Override
    public String toString() {
        return toJson();
    }

    /**
     * Associates the specified value with the specified key.
     *
     * @param key The key
     * @param val The value
     * @return The previous value associated with the key
     */
    @Override
    public T put(@NonNull String key, @NonNull T val) {
        T prevVal = get(key);
        super.put(key, val);
        return prevVal;
    }

    /**
     * Converts this Dict to a standard Map.
     *
     * @return A new Map containing all key-value pairs
     */
    public Map<String, T> toMap() {
        Map<String, T> map = new ConcurrentHashMap<>(10);
        map.putAll(this);
        return map;
    }

    /**
     * Gets a string value from the dictionary.
     *
     * @param key The key to look up
     * @return The string value or null if not found or type mismatch
     */
    public String getString(String key) {
        T val = get(key);
        if (val instanceof String) {
            return (String) val;
        }
        return null;
    }

    /**
     * Gets an integer value from the dictionary with default null.
     *
     * @param key The key to look up
     * @return The integer value or null if not found or type mismatch
     */
    public Integer getInteger(String key) {
        return getInteger(key, null);
    }

    /**
     * Gets an integer value from the dictionary with specified default.
     *
     * @param key      The key to look up
     * @param defValue The default value to return if not found
     * @return The integer value or defValue if not found or type mismatch
     */
    public Integer getInteger(String key, Integer defValue) {
        Object val = get(key);
        if (val == null) {
            return defValue;
        }
        if (val instanceof Integer) {
            return (Integer) val;
        } else if (val instanceof Long) {
            Long longVal = (Long) val;
            if (longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) {
                return longVal.intValue();
            }
        } else if (val instanceof Short) {
            return ((Short) val).intValue();
        } else if (val instanceof Byte) {
            return ((Byte) val).intValue();
        }
        return defValue;
    }

    /**
     * Gets a boolean value from the dictionary with default false.
     *
     * @param key The key to look up
     * @return The boolean value or false if not found or type mismatch
     */
    public boolean getBoolean(String key) {
        return getBoolean(key, false);
    }

    /**
     * Gets a boolean value from the dictionary with specified default.
     *
     * @param key          The key to look up
     * @param defaultValue The default value to return if not found
     * @return The boolean value or defaultValue if not found or type mismatch
     */
    public boolean getBoolean(String key, boolean defaultValue) {
        T val = get(key);
        if (val instanceof Boolean) {
            return (Boolean) val;
        }
        return defaultValue;
    }

    /**
     * Gets a nested Dict from the dictionary.
     *
     * @param key The key to look up
     * @return The Dict value or null if not found or type mismatch
     */
    public Dict getDict(String key) {
        T val = get(key);
        if (val instanceof Map) {
            return Dict.fromMap((Map) val);
        }
        return null;
    }

    /**
     * Serializes this Dict to a JSON string.
     *
     * @return JSON representation of this dictionary
     */
    public String toJson() {
        return JsonUtils.toJson(this);
    }
}
