using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dodohold.core;
using Dataoke;
using Google.Protobuf.WellKnownTypes;
using System.Diagnostics;
using System.Text.Json;
using TencentCloud.Tcm.V20210413.Models;
using System.Security.Cryptography;
using System.Data;
using System.Threading.Channels;
using YunhuiKit;
using System.IO;


namespace molilian.core
{
    public partial class TkOrderTrackingCore
    {
        private const int DefaultLinkSummaryTtlSeconds = 24 * 3600;
        private const int MaxLinkSummaryTtlSeconds = 7 * 24 * 3600;
        private const string LinkSummaryCacheVersion = "v2";

        public class TkOrderLinkSummaryDTO
        {
            public TkChannelEnum channel { get; set; }
            public int accountId { get; set; } = 0;
            public string ip { get; set; } = string.Empty;
            public string oaid { get; set; } = string.Empty;
            public string mktId { get; set; } = string.Empty;
            public string itemId { get; set; } = string.Empty;
            public string itemName { get; set; } = string.Empty;
            public string taoToken { get; set; } = string.Empty;
            public string shortLinkurl { get; set; } = string.Empty;
            public string deeplink_url { get; set; } = string.Empty;
            public string item_url { get; set; } = string.Empty;
            public string item_deeplink_url { get; set; } = string.Empty;
            public DateTime create_time { get; set; } = DateTime.Now;
        }

        public static void SaveLinkSummary(TkDataDTO item, TkPoolDTO? account = null)
        {
            if (!ShouldSaveLinkSummary(item, account)) return;

            TkOrderLinkSummaryDTO newData = new()
            {
                accountId = item.accountId,
                channel = item.channel,
                ip = item.ip,
                oaid = item.oaid,
                mktId = item.mktId,
                itemId = item.itemId,
                itemName = item.itemName,
                taoToken = item.taoToken,
                shortLinkurl = item.shortLinkurl,
                deeplink_url = item.deeplink_url,
                item_url = item.item_url,
                item_deeplink_url = item.item_deeplink_url,
                create_time = item.create_time,
            };

            var config = TkConfigCore.Get();
            int ttl = GetLinkSummaryTtlSeconds(config);
            string cacheKey = BuildItemSummaryKey(item.accountId, item.itemId);
            if (!string.IsNullOrEmpty(cacheKey)) RedisHelper.Set(cacheKey, newData, ttl);

            string mktId = item.mktId;
            if (!string.IsNullOrEmpty(mktId) && mktId.Contains('-'))
            {
                mktId = mktId.Split('-')[^1];
                cacheKey = BuildMktSummaryKey(item.accountId, mktId);
                RedisHelper.Set(cacheKey, newData, ttl);
            }

            cacheKey = BuildTitleSummaryKey(item.accountId, item.itemName);
            if (!string.IsNullOrEmpty(cacheKey)) RedisHelper.Set(cacheKey, newData, ttl);
        }

        private static bool ShouldSaveLinkSummary(TkDataDTO item, TkPoolDTO? account)
        {
            if (item == null || item.accountId <= 0) return false;
            if (!item.success) return false;
            if (string.IsNullOrEmpty(item.itemId)) return false;

            bool hasLink = !string.IsNullOrEmpty(item.shortLinkurl) ||
                           !string.IsNullOrEmpty(item.deeplink_url) ||
                           !string.IsNullOrEmpty(item.item_url) ||
                           !string.IsNullOrEmpty(item.item_deeplink_url);
            if (!hasLink) return false;

            account ??= GetCachedAccount(item.accountId);
            return account?.enable_order_summary_cache ?? true;
        }

        private static TkPoolDTO? GetCachedAccount(int accountId)
        {
            try
            {
                var list = TkPoolCore.GetCachedAccounts();
                return list?.FirstOrDefault(e => e.id == accountId);
            }
            catch
            {
                return null;
            }
        }

        private static int GetLinkSummaryTtlSeconds(TkConfigDTO config)
        {
            long configuredHours = config?.fake_click_ttl ?? 0;
            long ttl = configuredHours > 0 ? configuredHours * 3600 : DefaultLinkSummaryTtlSeconds;
            return (int)Math.Clamp(ttl, 3600, MaxLinkSummaryTtlSeconds);
        }

        private static string BuildItemSummaryKey(int accountId, string itemId)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(itemId)) return string.Empty;
            return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:item:{itemId}";
        }

        private static string BuildMktSummaryKey(int accountId, string mktId)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(mktId)) return string.Empty;
            return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:mktId:{mktId}";
        }

        private static string BuildTitleSummaryKey(int accountId, string itemTitle)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(itemTitle)) return string.Empty;
            return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:title:{Sha256Hex(itemTitle)}";
        }

        private static string BuildLegacyItemSummaryKey(int accountId, string itemId)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(itemId)) return string.Empty;
            return $":cache:order_summary:{accountId}:{itemId}";
        }

        private static string BuildLegacyMktSummaryKey(int accountId, string mktId)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(mktId)) return string.Empty;
            return $":cache:order_summary:{accountId}:mktId:{mktId}";
        }

        private static string BuildLegacyTitleSummaryKey(int accountId, string itemTitle)
        {
            if (accountId <= 0 || string.IsNullOrEmpty(itemTitle)) return string.Empty;
            return $":cache:order_summary:{accountId}:{itemTitle}";
        }

        private static string Sha256Hex(string value)
        {
            byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(value));
            return Convert.ToHexString(hash).ToLowerInvariant();
        }

        private static async Task<TkOrderLinkSummaryDTO?> GetFirstSummaryAsync(YunhuiKit.RedisClient redis, IEnumerable<string> keys)
        {
            foreach (var key in keys.Where(e => !string.IsNullOrEmpty(e)))
            {
                var result = await redis.GetAsync<TkOrderLinkSummaryDTO>(key);
                if (IsUsableOrderSummary(result)) return result;
            }
            return null;
        }


        private static bool IsUsableOrderSummary(TkOrderLinkSummaryDTO? summary)
        {
            if (summary == null) return false;
            return !string.IsNullOrEmpty(summary.shortLinkurl) ||
                   !string.IsNullOrEmpty(summary.deeplink_url) ||
                   !string.IsNullOrEmpty(summary.item_url) ||
                   !string.IsNullOrEmpty(summary.item_deeplink_url);
        }

        public static async Task<(int, TkOrderLinkSummaryDTO)> GetLinkSummaryAsync(TkPoolDTO account, string itemId, string mktId, string itemTitle)
        {
            try
            {
                int accountId = account.id;
                mktId = !string.IsNullOrEmpty(mktId) && mktId.Contains('-')
                    ? mktId.Split('-')[^1]
                    : mktId;


                // 获取所有关联账户ID（包括当前账户）
                var relatedAccountIds = new List<int> { accountId };
                if (!string.IsNullOrEmpty(account.related_account_ids))
                {
                    relatedAccountIds.AddRange(account.related_account_ids
                        .Split(',')
                        .Where(id => !string.IsNullOrWhiteSpace(id))
                        .Select(int.Parse));
                }

                var tasks = EndPointCore.List()
                    .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
                    .Select(async node =>
                    {
                        try
                        {
                            var redisServer = EndPointCore.GetRedisServer(node);
                            if (string.IsNullOrEmpty(redisServer)) return (0, null);


                            using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
                            var redis = scope.Client;

                            // 按优先级依次查询不同的缓存key
                            TkOrderLinkSummaryDTO result = null;


                            // 遍历所有关联账户ID
                            foreach (var relatedId in relatedAccountIds)
                            {
                                if (!string.IsNullOrEmpty(mktId))
                                {
                                    result = await GetFirstSummaryAsync(redis, new[]
                                    {
                                        BuildMktSummaryKey(relatedId, mktId),
                                        BuildLegacyMktSummaryKey(relatedId, mktId)
                                    });
                                    if (IsUsableOrderSummary(result)) return (1, result);
                                }

                                if (!string.IsNullOrEmpty(itemId))
                                {
                                    result = await GetFirstSummaryAsync(redis, new[]
                                    {
                                        BuildItemSummaryKey(relatedId, itemId),
                                        BuildLegacyItemSummaryKey(relatedId, itemId)
                                    });
                                    if (IsUsableOrderSummary(result)) return (2, result);

                                    if (!string.IsNullOrEmpty(itemTitle))
                                    {
                                        result = await GetFirstSummaryAsync(redis, new[]
                                        {
                                            BuildTitleSummaryKey(relatedId, itemTitle),
                                            BuildLegacyTitleSummaryKey(relatedId, itemTitle)
                                        });
                                        if (IsUsableOrderSummary(result)) return (3, result);
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                        }
                        return (0, null);
                    });

                var results = await Task.WhenAll(tasks);
                return results.FirstOrDefault(r => r != (0, null));
            }
            catch (Exception)
            {
                // TODO: 添加日志记录
                return (0, null);
            }
        }

        public static async Task RemoveLinkSummaryAsync(int accountId, string itemId, string mktId, string itemTitle)
        {
            try
            {
                var tasks = EndPointCore.List()
                    .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
                    .Select(async node =>
                    {
                        try
                        {

                            var redisServer = EndPointCore.GetRedisServer(node);
                            if (string.IsNullOrEmpty(redisServer)) return;

                            using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
                            var redis = scope.Client;

                            mktId = !string.IsNullOrEmpty(mktId) && mktId.Contains('-')
                                ? mktId.Split('-')[^1]
                                : mktId;

                            var keys = new[]
                            {
                                BuildItemSummaryKey(accountId, itemId),
                                BuildLegacyItemSummaryKey(accountId, itemId),
                                BuildMktSummaryKey(accountId, mktId),
                                BuildLegacyMktSummaryKey(accountId, mktId),
                                BuildTitleSummaryKey(accountId, itemTitle),
                                BuildLegacyTitleSummaryKey(accountId, itemTitle)
                            }.Where(k => !string.IsNullOrEmpty(k));

                            // 批量删除所有相关缓存
                            await redis.DelAsync(keys.ToArray());
                        }
                        catch (Exception ex) { }
                    });
                await Task.WhenAll(tasks);
            }
            catch (Exception)
            {
                // TODO: 添加日志记录
            }
        }



        //public static async void RemoveLinkSummary(int accountId, string itemId, string mktId, string itemTitle)
        //{
        //    await EndPointCore.ProcessEndPointNodesAsync(node =>
        //    {
        //        if (!node.is_public_api) return Task.CompletedTask;
        //        if (string.IsNullOrEmpty(node.redis_server)) return Task.CompletedTask;


        //        var redis = RedisClientManager.GetRedisClient(node.redis_server);

        //        string cacheKey = $":cache:order_summary:{accountId}:{itemId}";
        //        redis.Del(cacheKey);

        //        cacheKey = $":cache:order_summary:{accountId}:mktId:{mktId}";
        //        redis.Del(cacheKey);

        //        cacheKey = $":cache:order_summary:{accountId}:{itemTitle}";
        //        redis.Del(cacheKey);

        //        return Task.CompletedTask;
        //    });
        //}

        public static async Task<bool> MatchOrder(TkPoolDTO account, TkOrderDetailDTO item)
        {
            DateTime now = DateTime.Now;
            if (item.tbPaidTime < now.AddDays(-24)) return false;

            (int match_type, var summary) = await GetLinkSummaryAsync(account, item.itemId, item.mktId, item.itemTitle);
            if (summary == null) return false;


            var config = TkConfigCore.Get();
            Random rand = new();
            int click_num = rand.Next(config.fake_click_min, config.fake_click_max + 1);

            if (account.fake_click_min != 0 && account.fake_click_max != 0)
                click_num = rand.Next(account.fake_click_min, account.fake_click_max + 1);

            DateTime paidTime = item.tbPaidTime;
            if (paidTime < summary.create_time) return false;
            if (paidTime < now.AddDays(-24)) return false;

            int minutes = now.Minute / 10 * 10;
            DateTime rounded = new(now.Year, now.Month, now.Day, now.Hour, minutes, 0);
            string batchId = rounded.ToString("yyyyMMddHHmm");

            string accountName = item.accountName;

            string shortLinkUrl = !string.IsNullOrEmpty(summary.item_url) ? summary.item_url : summary.shortLinkurl;
            string deeplinkUrl = !string.IsNullOrEmpty(summary.item_deeplink_url) ? summary.item_deeplink_url : summary.deeplink_url;
            switch (account.fake_click_link_type)
            {
                case FakeClickLinkType.Deeplink:
                    shortLinkUrl = string.Empty;
                    break;
                case FakeClickLinkType.H5:
                    deeplinkUrl = string.Empty;
                    break;
            }

            var exist = new DBContext.Table("tk_order_tracking").Fields("id").Get<dynamic>("tradeId=@tradeId", new { item.tradeId });

            DateTime expTime = now.Hour >= 21 ? now.AddHours(3) : now.Date.AddDays(1);
            var data = new TkOrderTrackingDTO()
            {
                match_type = match_type,

                channel = summary.channel,
                accountId = item.accountId,
                accountName = accountName,
                batchId = batchId,
                ip = summary.ip,
                oaid = summary.oaid,
                mktId = summary.mktId,
                itemId = summary.itemId,
                itemName = summary.itemName,
                taoToken = summary.taoToken,
                shortLinkUrl = shortLinkUrl,
                deeplinkUrl = deeplinkUrl,

                tradeId = item.tradeId,
                tradeParentId = item.tradeParentId,

                create_time = summary.create_time,
                click_time = item.clickTime,
                paid_time = item.tbPaidTime,

                exp_time = expTime,
                click_num = click_num,
            };
            if (exist == null)
            {
                using var conn = DBContext.GetOpenConnection();
                conn.Insert(data);
            }
            //RemoveLinkSummary(item.accountId, item.itemId, item.mktId, item.itemTitle);
            await RemoveLinkSummaryAsync(item.accountId, item.itemId, item.mktId, item.itemTitle);
            return true;
        }
    }
}
