using dodohold.core;
using Microsoft.AspNetCore.Mvc;
using molilian.core;
using System.Text.Json;

namespace molilian.api.Controllers
{
    [ApiController]
    [MyAuthorize("admin")]
    [Route("api/[controller]/[action]")]
    public class DeeplinkReportController : ControllerBase
    {
        private const int MaxRangeDays = 90;

        readonly IAuthorizationProvider provider = new AdminProvider();
        protected IHttpContextAccessor _accessor;

        public DeeplinkReportController(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }

        [HttpPost]
        public async Task<ActionResult> daily([FromBody] JsonElement form)
        {
            _ = provider.Get(_accessor.HttpContext);

            var queryDate = form.PathReadArray<string>("query_date[]");
            var channelName = form.Read("channel_name", string.Empty).Trim();

            DateTime start = DateTime.Now.Date.AddDays(-6);
            DateTime end = DateTime.Now.Date;

            if (queryDate.Count == 2)
            {
                if (DateTime.TryParse(queryDate[0], out var parsedStart))
                {
                    start = parsedStart.Date;
                }

                if (DateTime.TryParse(queryDate[1], out var parsedEnd))
                {
                    end = parsedEnd.Date;
                }
            }

            if (end < start)
            {
                (start, end) = (end, start);
            }

            if ((end - start).TotalDays >= MaxRangeDays)
            {
                end = start.AddDays(MaxRangeDays - 1);
            }

            var accountName = string.IsNullOrWhiteSpace(channelName)
                ? "tool"
                : channelName;
            var reportChannels = GetReportChannels(channelName);
            var channelNames = reportChannels
                .Select(item => item.channel_name)
                .ToList();
            var channelTotals = channelNames.ToDictionary(
                item => item,
                _ => 0L,
                StringComparer.OrdinalIgnoreCase
            );
            var dailyRows = new List<DeeplinkDailyReportRow>();

            for (var date = start; date <= end; date = date.AddDays(1))
            {
                var dateKey = date.ToString("yyyyMMdd");
                var channelStats = await GetChannelStatsAsync(reportChannels, dateKey);

                foreach (var item in channelStats)
                {
                    channelTotals[item.channel_name] += item.total_count;
                }

                var reportDate = date.ToString("yyyy-MM-dd");
                var accountTotalCount = await TkLogCore.GetTotalAsync($":parse_total:{accountName}:{dateKey}");
                var row = new DeeplinkDailyReportRow
                {
                    row_key = reportDate,
                    row_type = "daily",
                    report_date = reportDate,
                    request_count = accountTotalCount,
                    total_count = accountTotalCount,
                    success_count = await TkLogCore.GetTotalAsync($":parse_total:{accountName}:success:{dateKey}"),
                    fail_count = await TkLogCore.GetTotalAsync($":parse_total:{accountName}:fail:{dateKey}")
                };
                row.children = channelStats
                    .Select(item => new DeeplinkDailyReportRow
                    {
                        row_key = $"{reportDate}:{item.channel_name}",
                        row_type = "channel",
                        report_date = item.display_name,
                        channel_name = item.channel_name,
                        display_name = item.display_name,
                        request_count = item.request_count,
                        total_count = item.total_count,
                        success_count = item.success_count,
                        fail_count = item.fail_count
                    })
                    .ToList();

                dailyRows.Add(row);
            }

            var channels = channelTotals
                .Where(item => !string.IsNullOrWhiteSpace(channelName) || item.Value > 0)
                .OrderByDescending(item => item.Value)
                .ThenBy(item => item.Key)
                .Select(item => new DeeplinkDailyReportChannel
                {
                    channel_name = item.Key,
                    display_name = GetDisplayName(reportChannels, item.Key),
                    request_count = item.Value,
                    total_count = item.Value
                })
                .ToList();

            var activeChannelNames = channels
                .Select(item => item.channel_name)
                .ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (var row in dailyRows)
            {
                row.children = (row.children ?? new List<DeeplinkDailyReportRow>())
                    .Where(item => activeChannelNames.Contains(item.channel_name))
                    .OrderByDescending(item => item.total_count)
                    .ThenBy(item => item.channel_name)
                    .ToList();

                if (!row.children.Any())
                {
                    row.children = null;
                }
            }

            var list = dailyRows
                .OrderByDescending(item => item.report_date)
                .ToList();

            var summary = new DeeplinkDailyReportSummary
            {
                request_count = list.Sum(item => item.request_count),
                total_count = list.Sum(item => item.total_count),
                success_count = list.Sum(item => item.success_count),
                fail_count = list.Sum(item => item.fail_count)
            };

            return new APIResult(new
            {
                data = new
                {
                    list,
                    count = list.Count,
                    summary,
                    channels,
                    maxRangeDays = MaxRangeDays
                }
            });
        }

        private static List<DeeplinkDailyReportChannel> GetReportChannels(string channelName)
        {
            var channels = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            try
            {
                var rules = new DBContext.Table("deeplink_parse_rule")
                    .Where("status=@status", new { status = 1 })
                    .Select<DeeplinkParseRuleDTO>();
                if (rules != null)
                {
                    foreach (var rule in rules)
                    {
                        AddChannel(channels, rule.channel_name, rule.name);
                    }
                }
            }
            catch
            {
                // Channel names are display metadata. Redis statistics remain usable if this lookup fails.
            }

            if (!string.IsNullOrWhiteSpace(channelName))
            {
                AddChannel(channels, channelName, channelName);
            }
            else
            {
                foreach (var name in Enum.GetNames(typeof(TkChannelEnum)))
                {
                    AddChannel(channels, name, name);
                }
            }

            return channels
                .OrderBy(item => item.Key)
                .Select(item => new DeeplinkDailyReportChannel
                {
                    channel_name = item.Key,
                    display_name = item.Value
                })
                .ToList();
        }

        private static void AddChannel(
            Dictionary<string, string> channels,
            string channelName,
            string displayName
        )
        {
            if (string.IsNullOrWhiteSpace(channelName)) return;

            var normalizedChannel = channelName.Trim();
            if (normalizedChannel.Equals("all", StringComparison.OrdinalIgnoreCase)) return;
            if (normalizedChannel.Equals("tool", StringComparison.OrdinalIgnoreCase)) return;
            if (normalizedChannel.Equals("unknown", StringComparison.OrdinalIgnoreCase)) return;

            var normalizedDisplay = string.IsNullOrWhiteSpace(displayName)
                ? normalizedChannel
                : displayName.Trim();

            if (!channels.ContainsKey(normalizedChannel) ||
                channels[normalizedChannel].Equals(normalizedChannel, StringComparison.OrdinalIgnoreCase))
            {
                channels[normalizedChannel] = normalizedDisplay;
            }
        }

        private static string GetDisplayName(
            List<DeeplinkDailyReportChannel> channels,
            string channelName
        )
        {
            return channels
                .FirstOrDefault(item => item.channel_name.Equals(
                    channelName,
                    StringComparison.OrdinalIgnoreCase
                ))
                ?.display_name ?? channelName;
        }

        private static async Task<List<DeeplinkDailyReportChannel>> GetChannelStatsAsync(
            List<DeeplinkDailyReportChannel> channels,
            string dateKey
        )
        {
            var tasks = channels.Select(async channel =>
            {
                var channelTotalCount = await TkLogCore.GetTotalAsync($":parse_total:{channel.channel_name}:{dateKey}");

                return new DeeplinkDailyReportChannel
                {
                    channel_name = channel.channel_name,
                    display_name = channel.display_name,
                    request_count = channelTotalCount,
                    total_count = channelTotalCount,
                    success_count = await TkLogCore.GetTotalAsync($":parse_total:{channel.channel_name}:success:{dateKey}"),
                    fail_count = await TkLogCore.GetTotalAsync($":parse_total:{channel.channel_name}:fail:{dateKey}")
                };
            });

            var results = await Task.WhenAll(tasks);
            return results.ToList();
        }
    }

    public class DeeplinkDailyReportChannel
    {
        public string channel_name { get; set; } = string.Empty;
        public string display_name { get; set; } = string.Empty;
        public long request_count { get; set; } = 0;
        public long total_count { get; set; } = 0;
        public long success_count { get; set; } = 0;
        public long fail_count { get; set; } = 0;
    }

    public class DeeplinkDailyReportRow
    {
        public string row_key { get; set; } = string.Empty;
        public string row_type { get; set; } = "daily";
        public string report_date { get; set; } = string.Empty;
        public string channel_name { get; set; } = string.Empty;
        public string display_name { get; set; } = string.Empty;
        public long request_count { get; set; } = 0;
        public long total_count { get; set; } = 0;
        public long success_count { get; set; } = 0;
        public long fail_count { get; set; } = 0;
        public List<DeeplinkDailyReportRow>? children { get; set; }
    }

    public class DeeplinkDailyReportSummary
    {
        public long request_count { get; set; } = 0;
        public long total_count { get; set; } = 0;
        public long success_count { get; set; } = 0;
        public long fail_count { get; set; } = 0;
    }
}
