using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dirichlet.Mediation;
using Runtime.ADAggregator;
using UnityEngine;

public sealed class TapadnControllerOptions
{
    public const string MediaNameKey = "tapadn.media_name";
    public const string MediaIdKey = "tapadn.media_id";
    public const string MediaKeyKey = "tapadn.media_key";
    public const string ChannelKey = "tapadn.channel";
    public const string SubChannelKey = "tapadn.sub_channel";
    public const string DebugKey = "tapadn.debug";
    public const string TapClientIdKey = "tapadn.tap_client_id";
    public const string ShakeEnabledKey = "tapadn.shake_enabled";
    public const string CustomConfigJsonKey = "tapadn.custom_config_json";
    public const string DataJsonKey = "tapadn.data_json";
    public const string ATagsKey = "tapadn.atags";
    public const string AllowIdfaAccessKey = "tapadn.allow_idfa_access";
    public const string RequestPermissionOnInitKey = "tapadn.request_permission_on_init";
    public const string RewardedAutoLoadKey = "tapadn.rewarded_auto_load";
    public const string RewardedPrewarmOnInitKey = "tapadn.rewarded_prewarm_on_init";
    public const string RewardedMaxLoadAttemptsKey = "tapadn.rewarded_max_load_attempts";
    public const string RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_ms";
    public const string RewardedShowTimeoutMsKey = "tapadn.rewarded_show_timeout_ms";
    public const string RewardNameKey = "tapadn.reward_name";
    public const string RewardAmountKey = "tapadn.reward_amount";
    public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load";
    public const string InterstitialPrewarmOnInitKey = "tapadn.interstitial_prewarm_on_init";
    public const string InterstitialMaxLoadAttemptsKey = "tapadn.interstitial_max_load_attempts";
    public const string InterstitialLoadRetryDelayMsKey = "tapadn.interstitial_load_retry_delay_ms";
    public const string InterstitialShowTimeoutMsKey = "tapadn.interstitial_show_timeout_ms";
    public const string SplashAutoLoadKey = "tapadn.splash_auto_load";
    public const string SplashPrewarmOnInitKey = "tapadn.splash_prewarm_on_init";
    public const string SplashMaxLoadAttemptsKey = "tapadn.splash_max_load_attempts";
    public const string SplashLoadRetryDelayMsKey = "tapadn.splash_load_retry_delay_ms";
    public const string SplashShowTimeoutMsKey = "tapadn.splash_show_timeout_ms";
    public const string ExpressWidthKey = "tapadn.express_width";
    public const string ExpressHeightKey = "tapadn.express_height";

    public long MediaId { get; set; }
    public string MediaName { get; set; }
    public string MediaKey { get; set; }
    public string Channel { get; set; } = "default";
    public string SubChannel { get; set; }
    public bool Debug { get; set; }
    public string TapClientId { get; set; }
    public bool ShakeEnabled { get; set; } = true;
    public string CustomConfigJson { get; set; }
    public string DataJson { get; set; }
    public string ATags { get; set; }
    public bool AllowIDFAAccess { get; set; } = true;
    public bool RequestPermissionOnInit { get; set; }
    public bool RewardedAutoLoad { get; set; } = true;
    public bool RewardedPrewarmOnInit { get; set; }
    public int RewardedMaxLoadAttempts { get; set; } = 1;
    public int RewardedLoadRetryDelayMs { get; set; } = 500;
    public int RewardedShowTimeoutMs { get; set; } = 20000;
    public string RewardName { get; set; } = "reward";
    public int RewardAmount { get; set; } = 1;
    public bool InterstitialAutoLoad { get; set; } = true;
    public bool InterstitialPrewarmOnInit { get; set; }
    public int InterstitialMaxLoadAttempts { get; set; } = 1;
    public int InterstitialLoadRetryDelayMs { get; set; } = 500;
    public int InterstitialShowTimeoutMs { get; set; } = 20000;
    public bool SplashAutoLoad { get; set; } = true;
    public bool SplashPrewarmOnInit { get; set; }
    public int SplashMaxLoadAttempts { get; set; } = 1;
    public int SplashLoadRetryDelayMs { get; set; } = 500;
    public int SplashShowTimeoutMs { get; set; } = 20000;
    public int? ExpressWidth { get; set; }
    public int? ExpressHeight { get; set; }

    public static TapadnControllerOptions Resolve(ADConfig adConfig, object[] args)
    {
        var options = new TapadnControllerOptions();
        options.ApplyAdConfig(adConfig);
        options.ApplyLegacyArgs(args);

        if (args == null)
        {
            return options;
        }

        foreach (var arg in args)
        {
            switch (arg)
            {
                case TapadnControllerOptions explicitOptions:
                    options.ApplyExplicitOptions(explicitOptions);
                    break;
                case IDictionary dictionary:
                    options.ApplyDictionary(dictionary);
                    break;
            }
        }

        return options;
    }

    public DirichletAdConfig BuildSdkConfig()
    {
        return new DirichletAdConfig.Builder()
            .WithMediaId(MediaId)
            .WithMediaName(string.IsNullOrWhiteSpace(MediaName) ? Application.productName : MediaName)
            .WithMediaKey(MediaKey)
            .WithGameChannel(string.IsNullOrWhiteSpace(Channel) ? "default" : Channel)
            .WithSubChannel(SubChannel)
            .EnableDebug(Debug)
            .WithTapClientId(TapClientId)
            .ShakeEnabled(ShakeEnabled)
            .WithCustomConfigJson(CustomConfigJson)
            .WithDataJson(DataJson)
            .AllowIDFAAccess(AllowIDFAAccess)
            .WithATags(ATags)
            .Build();
    }

    private void ApplyAdConfig(ADConfig adConfig)
    {
        if (adConfig == null)
        {
            return;
        }

        MediaId = ParseLong(adConfig.Id) ?? MediaId;
        MediaKey = string.IsNullOrWhiteSpace(adConfig.Key) ? MediaKey : adConfig.Key.Trim();
        MediaName = string.IsNullOrWhiteSpace(adConfig.Key2) ? MediaName : adConfig.Key2.Trim();
        ApplyCommonKeyValues(adConfig.CommonKeyValues);
    }

    private void ApplyCommonKeyValues(List<AdKeyValue> keyValues)
    {
        if (keyValues == null || keyValues.Count == 0)
        {
            return;
        }

        var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        foreach (var keyValue in keyValues)
        {
            if (keyValue == null || string.IsNullOrWhiteSpace(keyValue.key))
            {
                continue;
            }

            map[keyValue.key.Trim()] = keyValue.value;
        }

        ApplyMap(map);
    }

    private void ApplyLegacyArgs(object[] args)
    {
        if (args == null || args.Length == 0)
        {
            return;
        }

        if (args.Length > 0 && args[0] is string channel)
        {
            Channel = channel;
        }

        if (args.Length > 1 && TryConvertBool(args[1], out var debug))
        {
            Debug = debug;
        }
    }

    private void ApplyExplicitOptions(TapadnControllerOptions incoming)
    {
        if (incoming == null)
        {
            return;
        }

        MediaId = incoming.MediaId > 0 ? incoming.MediaId : MediaId;
        MediaName = incoming.MediaName ?? MediaName;
        MediaKey = incoming.MediaKey ?? MediaKey;
        Channel = incoming.Channel ?? Channel;
        SubChannel = incoming.SubChannel ?? SubChannel;
        Debug = incoming.Debug;
        TapClientId = incoming.TapClientId ?? TapClientId;
        ShakeEnabled = incoming.ShakeEnabled;
        CustomConfigJson = incoming.CustomConfigJson ?? CustomConfigJson;
        DataJson = incoming.DataJson ?? DataJson;
        ATags = incoming.ATags ?? ATags;
        AllowIDFAAccess = incoming.AllowIDFAAccess;
        RequestPermissionOnInit = incoming.RequestPermissionOnInit;
        RewardedAutoLoad = incoming.RewardedAutoLoad;
        RewardedPrewarmOnInit = incoming.RewardedPrewarmOnInit;
        RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts;
        RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs;
        RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs;
        RewardName = incoming.RewardName ?? RewardName;
        RewardAmount = incoming.RewardAmount;
        InterstitialAutoLoad = incoming.InterstitialAutoLoad;
        InterstitialPrewarmOnInit = incoming.InterstitialPrewarmOnInit;
        InterstitialMaxLoadAttempts = incoming.InterstitialMaxLoadAttempts;
        InterstitialLoadRetryDelayMs = incoming.InterstitialLoadRetryDelayMs;
        InterstitialShowTimeoutMs = incoming.InterstitialShowTimeoutMs;
        SplashAutoLoad = incoming.SplashAutoLoad;
        SplashPrewarmOnInit = incoming.SplashPrewarmOnInit;
        SplashMaxLoadAttempts = incoming.SplashMaxLoadAttempts;
        SplashLoadRetryDelayMs = incoming.SplashLoadRetryDelayMs;
        SplashShowTimeoutMs = incoming.SplashShowTimeoutMs;
        ExpressWidth = incoming.ExpressWidth ?? ExpressWidth;
        ExpressHeight = incoming.ExpressHeight ?? ExpressHeight;
    }

    private void ApplyDictionary(IDictionary dictionary)
    {
        if (dictionary == null || dictionary.Count == 0)
        {
            return;
        }

        var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        foreach (DictionaryEntry entry in dictionary)
        {
            if (entry.Key == null)
            {
                continue;
            }

            map[entry.Key.ToString()] = ConvertDictionaryValue(entry.Value);
        }

        ApplyMap(map);
    }

    private void ApplyMap(IDictionary<string, string> map)
    {
        MediaId = GetLong(map, MediaIdKey, "media_id") ?? MediaId;
        MediaName = GetString(map, MediaNameKey, "media_name") ?? MediaName;
        MediaKey = GetString(map, MediaKeyKey, "media_key") ?? MediaKey;
        Channel = GetString(map, ChannelKey, "channel") ?? Channel;
        SubChannel = GetString(map, SubChannelKey, "sub_channel") ?? SubChannel;
        Debug = GetBool(map, DebugKey, "debug") ?? Debug;
        TapClientId = GetString(map, TapClientIdKey, "tap_client_id") ?? TapClientId;
        ShakeEnabled = GetBool(map, ShakeEnabledKey) ?? ShakeEnabled;
        CustomConfigJson = GetString(map, CustomConfigJsonKey) ?? CustomConfigJson;
        DataJson = GetString(map, DataJsonKey) ?? DataJson;
        ATags = GetString(map, ATagsKey) ?? ATags;
        AllowIDFAAccess = GetBool(map, AllowIdfaAccessKey) ?? AllowIDFAAccess;
        RequestPermissionOnInit = GetBool(map, RequestPermissionOnInitKey) ?? RequestPermissionOnInit;
        RewardedAutoLoad = GetBool(map, RewardedAutoLoadKey) ?? RewardedAutoLoad;
        RewardedPrewarmOnInit = GetBool(map, RewardedPrewarmOnInitKey) ?? RewardedPrewarmOnInit;
        RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts;
        RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs;
        RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs;
        RewardName = GetString(map, RewardNameKey) ?? RewardName;
        RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount;
        InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad;
        InterstitialPrewarmOnInit = GetBool(map, InterstitialPrewarmOnInitKey) ?? InterstitialPrewarmOnInit;
        InterstitialMaxLoadAttempts = GetInt(map, InterstitialMaxLoadAttemptsKey) ?? InterstitialMaxLoadAttempts;
        InterstitialLoadRetryDelayMs = GetInt(map, InterstitialLoadRetryDelayMsKey) ?? InterstitialLoadRetryDelayMs;
        InterstitialShowTimeoutMs = GetInt(map, InterstitialShowTimeoutMsKey) ?? InterstitialShowTimeoutMs;
        SplashAutoLoad = GetBool(map, SplashAutoLoadKey) ?? SplashAutoLoad;
        SplashPrewarmOnInit = GetBool(map, SplashPrewarmOnInitKey) ?? SplashPrewarmOnInit;
        SplashMaxLoadAttempts = GetInt(map, SplashMaxLoadAttemptsKey) ?? SplashMaxLoadAttempts;
        SplashLoadRetryDelayMs = GetInt(map, SplashLoadRetryDelayMsKey) ?? SplashLoadRetryDelayMs;
        SplashShowTimeoutMs = GetInt(map, SplashShowTimeoutMsKey) ?? SplashShowTimeoutMs;
        ExpressWidth = GetInt(map, ExpressWidthKey) ?? ExpressWidth;
        ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight;
    }

    private static string GetString(IDictionary<string, string> map, params string[] keys)
    {
        foreach (var key in keys)
        {
            if (!string.IsNullOrWhiteSpace(key) &&
                map.TryGetValue(key, out var value) &&
                !string.IsNullOrWhiteSpace(value))
            {
                return value.Trim();
            }
        }

        return null;
    }

    private static bool? GetBool(IDictionary<string, string> map, params string[] keys)
    {
        var value = GetString(map, keys);
        return TryConvertBool(value, out var result) ? result : null;
    }

    private static int? GetInt(IDictionary<string, string> map, params string[] keys)
    {
        var value = GetString(map, keys);
        return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? result : (int?)null;
    }

    private static long? GetLong(IDictionary<string, string> map, params string[] keys)
    {
        return ParseLong(GetString(map, keys));
    }

    private static long? ParseLong(string value)
    {
        return long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? result : (long?)null;
    }

    private static bool TryConvertBool(object value, out bool result)
    {
        switch (value)
        {
            case bool boolValue:
                result = boolValue;
                return true;
            case string stringValue:
                if (bool.TryParse(stringValue, out result))
                {
                    return true;
                }

                if (string.Equals(stringValue, "1", StringComparison.OrdinalIgnoreCase))
                {
                    result = true;
                    return true;
                }

                if (string.Equals(stringValue, "0", StringComparison.OrdinalIgnoreCase))
                {
                    result = false;
                    return true;
                }

                break;
        }

        result = false;
        return false;
    }

    private static string ConvertDictionaryValue(object value)
    {
        if (value == null)
        {
            return null;
        }

        if (value is string stringValue)
        {
            return stringValue;
        }

        if (value is IEnumerable enumerable)
        {
            var items = new List<string>();
            foreach (var item in enumerable)
            {
                if (item != null)
                {
                    items.Add(item.ToString());
                }
            }

            return string.Join(",", items.ToArray());
        }

        return value.ToString();
    }
}
