using DynamicExpresso;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace MesWork
{
    /// <summary>
    /// 重量标定公式计算工具。
    /// </summary>
    public static class WeightCalibrationHelper
    {
        private class CalibrationCache
        {
            public string ToolName;
            public string Formula;
            public string Description;
            public DateTime LoadedAt;
        }

        private static readonly object CacheLock = new object();
        private static readonly Dictionary<string, CalibrationCache> Cache = new Dictionary<string, CalibrationCache>(StringComparer.OrdinalIgnoreCase);
        private static readonly TimeSpan CacheLife = TimeSpan.FromSeconds(3);

        public static bool Apply(string opName, decimal rawWeight, out decimal calibratedWeight, out string formula, out string message)
        {
            calibratedWeight = rawWeight;
            formula = "x";
            message = "";

            if (!TryGetConfig(opName, rawWeight, out CalibrationCache config, out string queryMsg))
            {
                if (!string.IsNullOrWhiteSpace(queryMsg))
                {
                    message = queryMsg;
                    return false;
                }

                formula = "x";
                calibratedWeight = rawWeight;
                return true;
            }

            formula = NormalizeFormula(config.Formula);
            return TryEvaluateFormula(formula, rawWeight, out calibratedWeight, out message);
        }

        public static void ClearCache()
        {
            lock (CacheLock)
            {
                Cache.Clear();
            }
        }

        public static bool ValidateFormula(string formula, out string message)
        {
            decimal _;
            return TryEvaluateFormula(NormalizeFormula(formula), 1M, out _, out message);
        }

        private static string NormalizeFormula(string formula)
        {
            return string.IsNullOrWhiteSpace(formula) ? "" : formula.Trim();
        }

        private static bool TryEvaluateFormula(string formula, decimal xValue, out decimal result, out string message)
        {
            result = xValue;
            message = "";

            if (string.IsNullOrWhiteSpace(formula))
            {
                message = "计算公式不能为空，至少填写 x";
                return false;
            }

            try
            {
                var interpreter = new Interpreter();
                interpreter.Reference(typeof(Math));
                double x = Convert.ToDouble(xValue);
                interpreter.SetVariable("x", x);
                interpreter.SetVariable("X", x);

                object value = interpreter.Eval(formula);
                if (value == null)
                {
                    message = "计算公式未返回数值";
                    return false;
                }

                result = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
                return true;
            }
            catch (Exception err)
            {
                message = $"计算公式错误：{err.Message}";
                return false;
            }
        }

        private static bool TryGetConfig(string opName, decimal rawWeight, out CalibrationCache config, out string message)
        {
            config = null;
            message = "";
            string station = string.IsNullOrWhiteSpace(opName) ? "" : opName.Trim();
            string key = station + "|" + rawWeight.ToString("F3", CultureInfo.InvariantCulture);

            lock (CacheLock)
            {
                if (Cache.TryGetValue(key, out config) && DateTime.Now - config.LoadedAt < CacheLife)
                {
                    return true;
                }
            }

            if (!B_DB_Opera.QueryToolCalibration(station, rawWeight, out string toolName, out string formula, out string description, out message))
            {
                return false;
            }

            config = new CalibrationCache
            {
                ToolName = toolName,
                Formula = formula,
                Description = description,
                LoadedAt = DateTime.Now
            };

            lock (CacheLock)
            {
                Cache[key] = config;
            }
            return true;
        }
    }
}
