using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;

namespace AxCopilot.Handlers;

internal static class UnitConverter
{
	private static readonly Regex Pattern = new Regex("^(-?\\d+(?:\\.\\d+)?)\\s*([a-z°/²³µ]+)\\s+(?:in|to)\\s+([a-z°/²³µ]+)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Dictionary<string, double> _length = new Dictionary<string, double>
	{
		["km"] = 1000.0,
		["m"] = 1.0,
		["cm"] = 0.01,
		["mm"] = 0.001,
		["mi"] = 1609.344,
		["mile"] = 1609.344,
		["miles"] = 1609.344,
		["ft"] = 0.3048,
		["feet"] = 0.3048,
		["foot"] = 0.3048,
		["in"] = 0.0254,
		["inch"] = 0.0254,
		["inches"] = 0.0254,
		["yd"] = 0.9144,
		["yard"] = 0.9144,
		["yards"] = 0.9144,
		["nm"] = 1E-09
	};

	private static readonly Dictionary<string, double> _weight = new Dictionary<string, double>
	{
		["t"] = 1000.0,
		["ton"] = 1000.0,
		["tonnes"] = 1000.0,
		["kg"] = 1.0,
		["g"] = 0.001,
		["mg"] = 1E-06,
		["lb"] = 0.453592,
		["lbs"] = 0.453592,
		["pound"] = 0.453592,
		["pounds"] = 0.453592,
		["oz"] = 0.0283495,
		["ounce"] = 0.0283495,
		["ounces"] = 0.0283495
	};

	private static readonly Dictionary<string, double> _speed = new Dictionary<string, double>
	{
		["m/s"] = 1.0,
		["mps"] = 1.0,
		["km/h"] = 5.0 / 18.0,
		["kmh"] = 5.0 / 18.0,
		["kph"] = 5.0 / 18.0,
		["mph"] = 0.44704,
		["kn"] = 0.514444,
		["knot"] = 0.514444,
		["knots"] = 0.514444
	};

	private static readonly Dictionary<string, double> _data = new Dictionary<string, double>
	{
		["b"] = 1.0,
		["byte"] = 1.0,
		["bytes"] = 1.0,
		["kb"] = 1024.0,
		["kib"] = 1024.0,
		["mb"] = 1048576.0,
		["mib"] = 1048576.0,
		["gb"] = 1073741824.0,
		["gib"] = 1073741824.0,
		["tb"] = 1099511627776.0,
		["tib"] = 1099511627776.0,
		["pb"] = 1125899906842624.0
	};

	private static readonly Dictionary<string, double> _area = new Dictionary<string, double>
	{
		["m²"] = 1.0,
		["m2"] = 1.0,
		["km²"] = 1000000.0,
		["km2"] = 1000000.0,
		["cm²"] = 0.0001,
		["cm2"] = 0.0001,
		["ha"] = 10000.0,
		["acre"] = 4046.86,
		["acres"] = 4046.86,
		["ft²"] = 0.092903,
		["ft2"] = 0.092903
	};

	private static readonly List<Dictionary<string, double>> _tables = new List<Dictionary<string, double>> { _length, _weight, _speed, _data, _area };

	public static bool TryConvert(string input, out string? result)
	{
		result = null;
		Match match = Pattern.Match(input.Trim());
		if (!match.Success)
		{
			return false;
		}
		if (!double.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
		{
			return false;
		}
		string text = match.Groups[2].Value.ToLowerInvariant();
		string text2 = match.Groups[3].Value.ToLowerInvariant();
		if (TryConvertTemperature(result2, text, text2, out var r))
		{
			result = FormatNum(r) + " " + TemperatureLabel(text2);
			return true;
		}
		foreach (Dictionary<string, double> table in _tables)
		{
			if (table.TryGetValue(text, out var value) && table.TryGetValue(text2, out var value2))
			{
				double v = result2 * value / value2;
				result = FormatNum(v) + " " + text2;
				return true;
			}
		}
		return false;
	}

	private static bool TryConvertTemperature(double v, string from, string to, out double r)
	{
		r = 0.0;
		double num;
		switch (from)
		{
		case "c":
		case "°c":
		case "celsius":
			num = v;
			break;
		case "f":
		case "°f":
		case "fahrenheit":
			num = (v - 32.0) * 5.0 / 9.0;
			break;
		case "k":
		case "kelvin":
			num = v - 273.15;
			break;
		default:
			return false;
		}
		switch (to)
		{
		case "c":
		case "°c":
		case "celsius":
			r = num;
			break;
		case "f":
		case "°f":
		case "fahrenheit":
			r = num * 9.0 / 5.0 + 32.0;
			break;
		case "k":
		case "kelvin":
			r = num + 273.15;
			break;
		default:
			return false;
		}
		return true;
	}

	private static string TemperatureLabel(string unit)
	{
		if (1 == 0)
		{
		}
		string result;
		switch (unit)
		{
		case "c":
		case "°c":
		case "celsius":
			result = "°C";
			break;
		case "f":
		case "°f":
		case "fahrenheit":
			result = "°F";
			break;
		case "k":
		case "kelvin":
			result = "K";
			break;
		default:
			result = unit;
			break;
		}
		if (1 == 0)
		{
		}
		return result;
	}

	private static string FormatNum(double v)
	{
		if (v == Math.Floor(v) && Math.Abs(v) < 1000000000000.0)
		{
			return ((long)v).ToString("N0", CultureInfo.CurrentCulture);
		}
		return v.ToString("G6", CultureInfo.InvariantCulture);
	}
}
