using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Memory;
using System.Globalization;
using economy.Helpers;
using economy.Models;
using economy.Models.Financial;
using economy.Models.Price.Global;
using NaturalGas = economy.Models.Price.Global.NaturalGas;
using Commodity = economy.Models.Price.Global.Commodity;

namespace economy.Controllers.Price.Global
{
    public class GlobalController : Controller
    {
        private readonly Alpha_API _alphaAPI;
        private readonly KoreaEximGoKR _koreaEximGoKR;
        private readonly IMemoryCache _cache;
        private readonly EconomyContext _context;
        private Dictionary<string, string> _queryString;

        // 커모디티 화이트리스트 (Alpha Vantage 함수, 화면 제목, 원화 표시 여부)
        // SitemapController 가 품목 목록을 여기서 읽어 sitemap 을 만든다(단일 원천).
        internal static readonly Dictionary<string, (string Function, string Title, bool HasKrw)> CommodityMap = new(StringComparer.OrdinalIgnoreCase)
        {
            ["copper"]         = ("COPPER",          "구리 시세",       true),
            ["aluminum"]       = ("ALUMINUM",        "알루미늄 시세",   true),
            ["wheat"]          = ("WHEAT",           "밀 시세",         true),
            ["cotton"]         = ("COTTON",          "섬유(면화) 시세", true),
            ["sugar"]          = ("SUGAR",           "설탕 시세",       true),
            ["coffee"]         = ("COFFEE",          "커피 시세",       true),
            ["allcommodities"] = ("ALL_COMMODITIES", "원자재 종합지수", false), // 지수(2016=100)라 원화 환산 없음
        };

        public GlobalController(Alpha_API alphaAPI, KoreaEximGoKR koreaEximGoKR, IMemoryCache cache, EconomyContext context)
        {
            _alphaAPI = alphaAPI;
            _koreaEximGoKR = koreaEximGoKR;
            _cache = cache;
            _context = context;
            _queryString = [];
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));

            ViewBag.QueryString = _queryString;

            base.OnActionExecuting(context);
        }

        // 환율 조회 (1시간 캐시 — 실패값 0은 캐시하지 않음)
        private async Task<int> GetExchangeValueAsync()
        {
            if (_cache.TryGetValue("ExchangeValue_USD", out int cachedValue))
            {
                return cachedValue;
            }

            // 환율 API가 느리거나 장애일 때 시세 페이지까지 지연되지 않도록 최대 3초만 대기 (실패 시 원화 미표시)
            var exchangeTask = new FinancialModel(_koreaEximGoKR).GetExchangeValue();
            var completedTask = await Task.WhenAny(exchangeTask, Task.Delay(TimeSpan.FromSeconds(3)));

            int exchangeValue = (completedTask == exchangeTask) ? await exchangeTask : 0;

            if (exchangeValue > 0)
            {
                _cache.Set("ExchangeValue_USD", exchangeValue, TimeSpan.FromHours(1));
            }
            else
            {
                // 환율 API 장애 중 매 요청마다 3초씩 대기하지 않도록 실패값은 5분간만 유지
                _cache.Set("ExchangeValue_USD", 0, TimeSpan.FromMinutes(5));
            }

            return exchangeValue;
        }

        // 국제 시세 응답을 차트 데이터로 변환 (최신 60개, 오름차순)
        private static ChartData BuildChart(string name, IEnumerable<(string Date, string Value)> rows)
        {
            var points = rows
                .Select(r => (r.Date, Parsed: decimal.TryParse(r.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal value) ? value : (decimal?)null))
                .Where(p => p.Parsed.HasValue)
                .Take(60)
                .Reverse()
                .ToList();

            if (points.Count < 2)
            {
                return new ChartData();
            }

            return new ChartData
            {
                Labels = points.Select(p => p.Date).ToList(),
                Series = [new ChartSeries { Name = name, Values = points.Select(p => p.Parsed).ToList() }]
            };
        }

        [HttpGet("Price/Global/NaturalGas")]
        public async Task<IActionResult> NaturalGas(NaturalGas.Request request)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (!new[] { "daily", "weekly", "monthly" }.Contains(request.Interval))
            {
                return BadRequest("Invalid input provided.");
            }

            int exchangeValue = await GetExchangeValueAsync();

            GlobalModel globalModel = new GlobalModel(_alphaAPI, _cache, _context);
            NaturalGas.Response itemList = await globalModel.GetNaturalGasPriceInfo(request);

            ChartData? chart = null;
            int total = 0;

            if (itemList.Data != null && itemList.Data.Any())
            {
                chart = BuildChart("천연가스", itemList.Data.Select(d => (d.Date, d.Value)));

                total = itemList.Data.Count;
                int listNum = Common.CalcListNumber(total, request.PageNo, request.NumOfRows);

                // 페이징 처리
                int skip = (request.PageNo - 1) * request.NumOfRows;
                int take = request.NumOfRows;

                itemList.Data = itemList.Data.Skip(skip).Take(take).Select((row, index) =>
                {
                    row.Num = (listNum - index);

                    if (decimal.TryParse(row.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal value) && value > 0)
                    {
                        row.KRW = (int)Math.Round(value * exchangeValue);
                    }

                    return row;
                }).ToList();
            }
            else
            {
                ViewBag.isError = true;
                ViewBag.errorMessage = "국제 시세 데이터를 가져오지 못했습니다. (API 한도 초과 또는 일시 장애)";
            }

            var viewModel = new View<NaturalGas.Request, NaturalGas.Response>();
            viewModel.SelectedListPerPage = request.NumOfRows;
            viewModel.Request = request;
            viewModel.Response = itemList;
            viewModel.Chart = chart;

            var queryString = new
            {
                interval = request.Interval
            };

            var pagination = new Pagination(total, request.PageNo, request.NumOfRows, queryString);
            viewModel.Pagination = pagination;

            return View("/Views/Price/Global/NaturalGas.cshtml", viewModel);
        }

        [HttpGet("Price/Global/{commodity}")]
        public async Task<IActionResult> Commodity(Commodity.Request request)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // 화이트리스트에 없는 상품은 404
            if (request.Commodity is null || !CommodityMap.TryGetValue(request.Commodity, out var meta))
            {
                return NotFound();
            }

            // 원자재 계열은 monthly/quarterly/annual만 지원 (잘못된 값은 Alpha Vantage가 조용히 monthly로 대체하므로 사전 차단)
            if (!new[] { "monthly", "quarterly", "annual" }.Contains(request.Interval))
            {
                return BadRequest("Invalid input provided.");
            }

            int exchangeValue = meta.HasKrw ? await GetExchangeValueAsync() : 0;

            GlobalModel globalModel = new GlobalModel(_alphaAPI, _cache, _context);
            Commodity.Response itemList = await globalModel.GetCommodityPriceInfo(meta.Function, request.Interval);

            ChartData? chart = null;
            int total = 0;

            if (itemList.Data != null && itemList.Data.Any())
            {
                chart = BuildChart(meta.Title, itemList.Data.Select(d => (d.Date, d.Value)));

                total = itemList.Data.Count;
                int listNum = Common.CalcListNumber(total, request.PageNo, request.NumOfRows);

                // 페이징 처리
                int skip = (request.PageNo - 1) * request.NumOfRows;
                int take = request.NumOfRows;

                itemList.Data = itemList.Data.Skip(skip).Take(take).Select((row, index) =>
                {
                    row.Num = (listNum - index);

                    if (decimal.TryParse(row.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal value) && value > 0)
                    {
                        row.KRW = Math.Round(value * exchangeValue);
                    }

                    row.Value = Common.NumberFormat(row.Value, "#,##0.##");

                    return row;
                }).ToList();
            }
            else
            {
                ViewBag.isError = true;
                ViewBag.errorMessage = "국제 시세 데이터를 가져오지 못했습니다. (API 한도 초과 또는 일시 장애)";
            }

            ViewBag.Title = meta.Title;
            ViewBag.HasKrw = meta.HasKrw;

            var viewModel = new View<Commodity.Request, Commodity.Response>();
            viewModel.SelectedListPerPage = request.NumOfRows;
            viewModel.Request = request;
            viewModel.Response = itemList;
            viewModel.Chart = chart;

            var queryString = new
            {
                interval = request.Interval
            };

            var pagination = new Pagination(total, request.PageNo, request.NumOfRows, queryString);
            viewModel.Pagination = pagination;

            return View("/Views/Price/Global/Commodity.cshtml", viewModel);
        }
    }
}
