using System;

namespace ZB.MOM.WW.LmxProxy.Host.Domain
{
    /// <summary>
    ///     Value, Timestamp, and Quality structure for SCADA data.
    /// </summary>
    public readonly struct Vtq : IEquatable<Vtq>
    {
        /// <summary>Gets the value. Null represents an unset/missing value.</summary>
        public object? Value { get; }

        /// <summary>Gets the UTC timestamp when the value was read.</summary>
        public DateTime Timestamp { get; }

        /// <summary>Gets the quality of the value.</summary>
        public Quality Quality { get; }

        public Vtq(object? value, DateTime timestamp, Quality quality)
        {
            Value = value;
            Timestamp = timestamp;
            Quality = quality;
        }

        public static Vtq New(object? value, Quality quality) => new(value, DateTime.UtcNow, quality);
        public static Vtq New(object? value, DateTime timestamp, Quality quality) => new(value, timestamp, quality);
        public static Vtq Good(object value) => new(value, DateTime.UtcNow, Quality.Good);
        public static Vtq Bad(object? value = null) => new(value, DateTime.UtcNow, Quality.Bad);
        public static Vtq Uncertain(object value) => new(value, DateTime.UtcNow, Quality.Uncertain);

        public bool Equals(Vtq other) =>
            Equals(Value, other.Value) && Timestamp.Equals(other.Timestamp) && Quality == other.Quality;

        public override bool Equals(object obj) => obj is Vtq other && Equals(other);

        public override int GetHashCode()
        {
            unchecked
            {
                int hashCode = Value != null ? Value.GetHashCode() : 0;
                hashCode = (hashCode * 397) ^ Timestamp.GetHashCode();
                hashCode = (hashCode * 397) ^ (int)Quality;
                return hashCode;
            }
        }

        public override string ToString() =>
            $"{{Value={Value}, Timestamp={Timestamp:yyyy-MM-dd HH:mm:ss.fff}, Quality={Quality}}}";

        public static bool operator ==(Vtq left, Vtq right) => left.Equals(right);
        public static bool operator !=(Vtq left, Vtq right) => !left.Equals(right);
    }
}
