using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

internal static class CurrencyConverter
{
	private static readonly Regex _pattern = new Regex("^(\\d+(?:\\.\\d+)?)\\s+([A-Za-z]{3})\\s+(?:to|in)\\s+([A-Za-z]{3})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Dictionary<string, (DateTime At, Dictionary<string, double> Rates)> _cache = new Dictionary<string, (DateTime, Dictionary<string, double>)>();

	private static readonly HttpClient _http = new HttpClient
	{
		Timeout = TimeSpan.FromSeconds(5.0)
	};

	private static readonly TimeSpan CacheTtl = TimeSpan.FromHours(1.0);

	private static readonly Dictionary<string, string> _names = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
	{
		["KRW"] = "원",
		["USD"] = "달러",
		["EUR"] = "유로",
		["JPY"] = "엔",
		["GBP"] = "파운드",
		["CNY"] = "위안",
		["HKD"] = "홍콩달러",
		["SGD"] = "싱가포르달러",
		["CAD"] = "캐나다달러",
		["AUD"] = "호주달러",
		["CHF"] = "스위스프랑",
		["TWD"] = "대만달러",
		["MXN"] = "멕시코페소",
		["BRL"] = "브라질헤알",
		["INR"] = "인도루피",
		["RUB"] = "루블",
		["THB"] = "바트",
		["VND"] = "동",
		["IDR"] = "루피아",
		["MYR"] = "링깃",
		["PHP"] = "페소",
		["NZD"] = "뉴질랜드달러",
		["SEK"] = "크로나",
		["NOK"] = "크로나(노르)",
		["DKK"] = "크로나(덴)",
		["AED"] = "디르함",
		["SAR"] = "리얄"
	};

	public static bool IsCurrencyQuery(string input)
	{
		return _pattern.IsMatch(input.Trim());
	}

	public static async Task<IEnumerable<LauncherItem>> ConvertAsync(string input, CancellationToken ct)
	{
		Match m = _pattern.Match(input.Trim());
		if (!m.Success)
		{
			return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("통화 형식 오류", "예: 100 USD to KRW", null, null, null, "\uea39"));
		}
		if (!double.TryParse(m.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var amount))
		{
			return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("숫자 형식 오류", "", null, null, null, "\uea39"));
		}
		string from = m.Groups[2].Value.ToUpperInvariant();
		string to = m.Groups[3].Value.ToUpperInvariant();
		try
		{
			Dictionary<string, double> rates = await GetRatesAsync(from, ct);
			if (rates == null)
			{
				return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("환율 조회 실패", "네트워크 연결을 확인하세요", null, null, null, "\ue7ba"));
			}
			if (!rates.TryGetValue(to, out var rate))
			{
				return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("지원하지 않는 통화: " + to, "3자리 ISO 4217 코드를 입력하세요", null, null, null, "\uea39"));
			}
			double result = amount * rate;
			string fn;
			string fromName = (_names.TryGetValue(from, out fn) ? fn : from);
			string tn;
			string toName = (_names.TryGetValue(to, out tn) ? tn : to);
			string text;
			switch (to)
			{
			default:
				text = result.ToString("N2", CultureInfo.CurrentCulture);
				break;
			case "KRW":
			case "JPY":
			case "IDR":
			case "VND":
				text = result.ToString("N0", CultureInfo.CurrentCulture);
				break;
			}
			string resultStr = text;
			string rateStr = ((rate < 0.01) ? rate.ToString("G4", CultureInfo.InvariantCulture) : rate.ToString("N4", CultureInfo.CurrentCulture));
			string display = resultStr + " " + to;
			return new _003C_003Ez__ReadOnlyArray<LauncherItem>(new LauncherItem[2]
			{
				new LauncherItem($"{amount:N2} {from}  =  {display}", $"1 {from}({fromName}) = {rateStr} {to}({toName})  ·  Enter로 복사", null, display, null, "\ue8ef"),
				new LauncherItem("숫자만: " + resultStr, "Enter로 숫자만 복사", null, resultStr, null, "\ue8ef")
			});
		}
		catch (OperationCanceledException)
		{
			return Array.Empty<LauncherItem>();
		}
		catch (Exception ex2)
		{
			LogService.Warn("환율 조회 오류: " + ex2.Message);
			return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("환율 조회 실패", ex2.Message, null, null, null, "\ue7ba"));
		}
	}

	private static async Task<Dictionary<string, double>?> GetRatesAsync(string baseCurrency, CancellationToken ct)
	{
		if (_cache.TryGetValue(baseCurrency, out var cached) && DateTime.Now - cached.At < CacheTtl)
		{
			return cached.Rates;
		}
		string url = "https://open.er-api.com/v6/latest/" + baseCurrency;
		using JsonDocument doc = JsonDocument.Parse(await _http.GetStringAsync(url, ct));
		JsonElement root = doc.RootElement;
		if (!root.TryGetProperty("result", out var resultEl) || resultEl.GetString() != "success")
		{
			return null;
		}
		if (!root.TryGetProperty("rates", out var ratesEl))
		{
			return null;
		}
		Dictionary<string, double> rates = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
		foreach (JsonProperty prop in ratesEl.EnumerateObject())
		{
			rates[prop.Name] = prop.Value.GetDouble();
		}
		_cache[baseCurrency] = (DateTime.Now, rates);
		return rates;
	}
}
