using Newtonsoft.Json;
using Prism.Mvvm;  // 引入 Prism 的 BindableBase
using System;
using System.Collections.Generic;

namespace BOB.Models
{
    public class ParameterModel : BindableBase
    {
        #region 构造函数

        public ParameterModel()
        {

        }

        public ParameterModel(ParameterModel source)
        {
            if (source == null) return;

            ID = source.ID;
            Name = source.Name;
            Type = source.Type;
            Category = source.Category;
            IsUseVar = source.IsUseVar;
            IsSave = source.IsSave;
            VariableName = source.VariableName;
            VariableID = source.VariableID;
            IsGlobal = source.IsGlobal;
            Value = source.Value;
            LowerLimit = source.LowerLimit;
            UpperLimit = source.UpperLimit;
        }

        #endregion

        private Guid _id = Guid.NewGuid();
        public Guid ID
        {
            get => _id;
            set => SetProperty(ref _id, value);
        }

        private bool _isVisible = true;
        public bool IsVisible
        {
            get => _isVisible;
            set => SetProperty(ref _isVisible, value);
        }

        private string _name;
        public string Name
        {
            get => _name;
            set => SetProperty(ref _name, value);
        }

        private Type _type = typeof(string);
        public Type Type
        {
            get => _type;
            set => SetProperty(ref _type, value);
        }

        private ParameterCategory _category = ParameterCategory.Temp;
        public ParameterCategory Category
        {
            get => _category;
            set => SetProperty(ref _category, value);
        }

        private bool _isGlobal;
        public bool IsGlobal
        {
            get => _isGlobal;
            set => SetProperty(ref _isGlobal, value);
        }

        private object? _value;
        public object? Value
        {
            get => _value;
            set => SetProperty(ref _value, value);
        }

        private object? _lowerLimit;
        public object? LowerLimit
        {
            get => _lowerLimit;
            set => SetProperty(ref _lowerLimit, value);
        }

        private object? _upperLimit;
        public object? UpperLimit
        {
            get => _upperLimit;
            set => SetProperty(ref _upperLimit, value);
        }

        private bool _result = true;
        public bool Result
        {
            get => _result;
            set => SetProperty(ref _result, value);
        }

        private bool _isUseVar;
        public bool IsUseVar
        {
            get => _isUseVar;
            set => SetProperty(ref _isUseVar, value);
        }

        private bool _isSave;
        public bool IsSave
        {
            get => _isSave;
            set => SetProperty(ref _isSave, value);
        }

        private string? _variableName;
        public string? VariableName
        {
            get => _variableName;
            set => SetProperty(ref _variableName, value);
        }

        private Guid? _variableID;
        public Guid? VariableID
        {
            get => _variableID;
            set => SetProperty(ref _variableID, value);
        }

        public enum ParameterCategory
        {
            Input,
            Output,
            Temp
        }

        public object? GetActualValue(Dictionary<Guid, ParameterModel> paraList)
        {
            HashSet<Guid> visitedIds = new HashSet<Guid>();
            ParameterModel current = this;

            while (current != null)
            {
                if (!current.IsUseVar)
                {
                    return current.Value;
                }

                if (visitedIds.Contains(current.ID))
                {
                    return null;
                }
                visitedIds.Add(current.ID);

                if (current.VariableID == null)
                {
                    if (Type != null && Value != null)
                    {
                        try
                        {
                            return Convert.ChangeType(Value, Type);
                        }
                        catch
                        {
                            return Value;
                        }
                    }
                }

                ParameterModel? next = paraList[(Guid)current.VariableID!];
                if (next == null)
                {
                    return null;
                }

                current = next;
            }

            return null;
        }

        public ParameterModel? GetCurrentParameter(Dictionary<Guid, ParameterModel> paraList)
        {
            HashSet<Guid> visitedIds = new HashSet<Guid>();
            ParameterModel current = this;

            while (current != null)
            {
                if (current.VariableID == null)
                {
                    return current;
                }

                if (visitedIds.Contains(current.ID))
                {
                    return null;
                }
                visitedIds.Add(current.ID);

                if (current.VariableID == null)
                {
                    if (Type != null && Value != null)
                    {
                        try
                        {
                            return current;
                        }
                        catch
                        {
                            return null;
                        }
                    }
                }

                ParameterModel? next = paraList[(Guid)current.VariableID!];
                if (next == null)
                {
                    return null;
                }

                current = next;
            }

            return null;
        }

        public (bool, string?) GetResult()
        {
            if (Type == typeof(string) && (!string.IsNullOrWhiteSpace(LowerLimit?.ToString()) || !string.IsNullOrWhiteSpace(UpperLimit?.ToString())))
            {
                return (true, $"参数 [ {Name}({Type}) ] 不可比较");
            }

            if (Value == null || (LowerLimit == null && UpperLimit == null))
            {
                return (true, null);
            }

            if (string.IsNullOrWhiteSpace(Value?.ToString()) || (string.IsNullOrWhiteSpace(LowerLimit?.ToString()) && string.IsNullOrWhiteSpace(UpperLimit?.ToString())))
            {
                return (true, null);
            }

            try
            {
                object? comparableValue = ConvertToComparable(Value);
                object? comparableLower = LowerLimit != null ? ConvertToComparable(LowerLimit) : null;
                object? comparableUpper = UpperLimit != null ? ConvertToComparable(UpperLimit) : null;

                if (comparableValue == null)
                {
                    return (true, $"参数 [ {Name}({Type}) ] 不可比较");
                }

                bool lowerValid = true;
                bool upperValid = true;

                if (comparableLower != null)
                {
                    lowerValid = CompareValues(comparableValue, comparableLower) >= 0;
                }

                if (comparableUpper != null)
                {
                    upperValid = CompareValues(comparableValue, comparableUpper) <= 0;
                }

                return (lowerValid && upperValid, null);
            }
            catch (Exception ex)
            {
                return (true, $"参数 [ {Name}({Type}) ] 上下限比较失败:{ex.Message}");
            }
        }

        private static object? ConvertToComparable(object value)
        {
            if (value is IConvertible convertible)
            {
                try
                {
                    return convertible.ToDouble(null);
                }
                catch { }

                try
                {
                    return convertible.ToDateTime(null);
                }
                catch { }
            }

            return null;
        }

        private static int CompareValues(object a, object b)
        {
            if (a is double aDouble && b is double bDouble)
            {
                return aDouble.CompareTo(bDouble);
            }

            if (a is DateTime aDate && b is DateTime bDate)
            {
                return aDate.CompareTo(bDate);
            }

            return 0;
        }
    }
}
