using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading;
using System.Threading.Tasks;

namespace ZB.MOM.WW.LmxProxy.Client.Domain;

// ────────────────────────────────────────────────────────────────
// Service contract
// ────────────────────────────────────────────────────────────────

[ServiceContract(Name = "scada.ScadaService")]
public interface IScadaService
{
    ValueTask<ConnectResponse> ConnectAsync(ConnectRequest request);
    ValueTask<DisconnectResponse> DisconnectAsync(DisconnectRequest request);
    ValueTask<GetConnectionStateResponse> GetConnectionStateAsync(GetConnectionStateRequest request);
    ValueTask<ReadResponse> ReadAsync(ReadRequest request);
    ValueTask<ReadBatchResponse> ReadBatchAsync(ReadBatchRequest request);
    ValueTask<WriteResponse> WriteAsync(WriteRequest request);
    ValueTask<WriteBatchResponse> WriteBatchAsync(WriteBatchRequest request);
    ValueTask<WriteBatchAndWaitResponse> WriteBatchAndWaitAsync(WriteBatchAndWaitRequest request);
    IAsyncEnumerable<VtqMessage> SubscribeAsync(SubscribeRequest request, CancellationToken cancellationToken = default);
    ValueTask<CheckApiKeyResponse> CheckApiKeyAsync(CheckApiKeyRequest request);
}

// ────────────────────────────────────────────────────────────────
// Typed Value System (v2)
// ────────────────────────────────────────────────────────────────

/// <summary>
/// Carries a value in its native type via a protobuf oneof.
/// Exactly one property will be non-default. All-default = null value.
/// protobuf-net uses the first non-default field in field-number order for oneof.
/// </summary>
[DataContract]
public class TypedValue
{
    // Tracks which oneof field was set (by property setter during deserialization or manual assignment).
    private TypedValueCase _setCase = TypedValueCase.None;

    private bool _boolValue;
    private int _int32Value;
    private long _int64Value;
    private float _floatValue;
    private double _doubleValue;
    private string? _stringValue;
    private byte[]? _bytesValue;
    private long _datetimeValue;
    private ArrayValue? _arrayValue;

    [DataMember(Order = 1)]
    public bool BoolValue { get => _boolValue; set { _boolValue = value; _setCase = TypedValueCase.BoolValue; } }

    [DataMember(Order = 2)]
    public int Int32Value { get => _int32Value; set { _int32Value = value; _setCase = TypedValueCase.Int32Value; } }

    [DataMember(Order = 3)]
    public long Int64Value { get => _int64Value; set { _int64Value = value; _setCase = TypedValueCase.Int64Value; } }

    [DataMember(Order = 4)]
    public float FloatValue { get => _floatValue; set { _floatValue = value; _setCase = TypedValueCase.FloatValue; } }

    [DataMember(Order = 5)]
    public double DoubleValue { get => _doubleValue; set { _doubleValue = value; _setCase = TypedValueCase.DoubleValue; } }

    [DataMember(Order = 6)]
    public string? StringValue { get => _stringValue; set { _stringValue = value; _setCase = TypedValueCase.StringValue; } }

    [DataMember(Order = 7)]
    public byte[]? BytesValue { get => _bytesValue; set { _bytesValue = value; _setCase = TypedValueCase.BytesValue; } }

    [DataMember(Order = 8)]
    public long DatetimeValue { get => _datetimeValue; set { _datetimeValue = value; _setCase = TypedValueCase.DatetimeValue; } }

    [DataMember(Order = 9)]
    public ArrayValue? ArrayValue { get => _arrayValue; set { _arrayValue = value; _setCase = TypedValueCase.ArrayValue; } }

    /// <summary>
    /// Indicates which oneof case is set. Tracked via property setters so default values
    /// (false, 0, 0.0) are correctly distinguished from "not set".
    /// </summary>
    public TypedValueCase GetValueCase() => _setCase;
}

/// <summary>Identifies which field in TypedValue is set.</summary>
public enum TypedValueCase
{
    None = 0,
    BoolValue = 1,
    Int32Value = 2,
    Int64Value = 3,
    FloatValue = 4,
    DoubleValue = 5,
    StringValue = 6,
    BytesValue = 7,
    DatetimeValue = 8,
    ArrayValue = 9
}

/// <summary>Container for typed arrays. Exactly one field will be set.</summary>
[DataContract]
public class ArrayValue
{
    [DataMember(Order = 1)]
    public BoolArray? BoolValues { get; set; }

    [DataMember(Order = 2)]
    public Int32Array? Int32Values { get; set; }

    [DataMember(Order = 3)]
    public Int64Array? Int64Values { get; set; }

    [DataMember(Order = 4)]
    public FloatArray? FloatValues { get; set; }

    [DataMember(Order = 5)]
    public DoubleArray? DoubleValues { get; set; }

    [DataMember(Order = 6)]
    public StringArray? StringValues { get; set; }

    [DataMember(Order = 7)]
    public DatetimeArray? DatetimeValues { get; set; }
}

[DataContract]
public class BoolArray
{
    [DataMember(Order = 1)]
    public List<bool> Values { get; set; } = [];
}

[DataContract]
public class Int32Array
{
    [DataMember(Order = 1)]
    public List<int> Values { get; set; } = [];
}

[DataContract]
public class Int64Array
{
    [DataMember(Order = 1)]
    public List<long> Values { get; set; } = [];
}

[DataContract]
public class FloatArray
{
    [DataMember(Order = 1)]
    public List<float> Values { get; set; } = [];
}

[DataContract]
public class DoubleArray
{
    [DataMember(Order = 1)]
    public List<double> Values { get; set; } = [];
}

[DataContract]
public class StringArray
{
    [DataMember(Order = 1)]
    public List<string> Values { get; set; } = [];
}

[DataContract]
public class DatetimeArray
{
    [DataMember(Order = 1)]
    public List<long> Values { get; set; } = [];
}

// ────────────────────────────────────────────────────────────────
// Quality Code (v2)
// ────────────────────────────────────────────────────────────────

/// <summary>
/// OPC UA-style quality code with numeric status code and symbolic name.
/// </summary>
[DataContract]
public class QualityCode
{
    [DataMember(Order = 1)]
    public uint StatusCode { get; set; }

    [DataMember(Order = 2)]
    public string SymbolicName { get; set; } = string.Empty;

    /// <summary>Returns true if quality category is Good (high bits 0x00).</summary>
    public bool IsGood => (StatusCode & 0xC0000000) == 0x00000000;

    /// <summary>Returns true if quality category is Uncertain (high bits 0x40).</summary>
    public bool IsUncertain => (StatusCode & 0xC0000000) == 0x40000000;

    /// <summary>Returns true if quality category is Bad (high bits 0x80).</summary>
    public bool IsBad => (StatusCode & 0xC0000000) == 0x80000000;
}

// ────────────────────────────────────────────────────────────────
// VTQ message (v2)
// ────────────────────────────────────────────────────────────────

[DataContract]
public class VtqMessage
{
    [DataMember(Order = 1)]
    public string Tag { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public TypedValue? Value { get; set; }

    [DataMember(Order = 3)]
    public long TimestampUtcTicks { get; set; }

    [DataMember(Order = 4)]
    public QualityCode? Quality { get; set; }
}

// ────────────────────────────────────────────────────────────────
// Connect
// ────────────────────────────────────────────────────────────────

[DataContract]
public class ConnectRequest
{
    [DataMember(Order = 1)]
    public string ClientId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public string ApiKey { get; set; } = string.Empty;
}

[DataContract]
public class ConnectResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public string SessionId { get; set; } = string.Empty;
}

// ────────────────────────────────────────────────────────────────
// Disconnect
// ────────────────────────────────────────────────────────────────

[DataContract]
public class DisconnectRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;
}

[DataContract]
public class DisconnectResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;
}

// ────────────────────────────────────────────────────────────────
// GetConnectionState
// ────────────────────────────────────────────────────────────────

[DataContract]
public class GetConnectionStateRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;
}

[DataContract]
public class GetConnectionStateResponse
{
    [DataMember(Order = 1)]
    public bool IsConnected { get; set; }

    [DataMember(Order = 2)]
    public string ClientId { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public long ConnectedSinceUtcTicks { get; set; }
}

// ────────────────────────────────────────────────────────────────
// Read
// ────────────────────────────────────────────────────────────────

[DataContract]
public class ReadRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public string Tag { get; set; } = string.Empty;
}

[DataContract]
public class ReadResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public VtqMessage? Vtq { get; set; }
}

[DataContract]
public class ReadBatchRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public List<string> Tags { get; set; } = [];
}

[DataContract]
public class ReadBatchResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public List<VtqMessage> Vtqs { get; set; } = [];
}

// ────────────────────────────────────────────────────────────────
// Write
// ────────────────────────────────────────────────────────────────

[DataContract]
public class WriteRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public string Tag { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public TypedValue? Value { get; set; }
}

[DataContract]
public class WriteResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;
}

[DataContract]
public class WriteItem
{
    [DataMember(Order = 1)]
    public string Tag { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public TypedValue? Value { get; set; }
}

[DataContract]
public class WriteResult
{
    [DataMember(Order = 1)]
    public string Tag { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public bool Success { get; set; }

    [DataMember(Order = 3)]
    public string Message { get; set; } = string.Empty;
}

[DataContract]
public class WriteBatchRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public List<WriteItem> Items { get; set; } = [];
}

[DataContract]
public class WriteBatchResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public List<WriteResult> Results { get; set; } = [];
}

// ────────────────────────────────────────────────────────────────
// WriteBatchAndWait
// ────────────────────────────────────────────────────────────────

[DataContract]
public class WriteBatchAndWaitRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public List<WriteItem> Items { get; set; } = [];

    [DataMember(Order = 3)]
    public string FlagTag { get; set; } = string.Empty;

    [DataMember(Order = 4)]
    public TypedValue? FlagValue { get; set; }

    [DataMember(Order = 5)]
    public int TimeoutMs { get; set; }

    [DataMember(Order = 6)]
    public int PollIntervalMs { get; set; }
}

[DataContract]
public class WriteBatchAndWaitResponse
{
    [DataMember(Order = 1)]
    public bool Success { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 3)]
    public List<WriteResult> WriteResults { get; set; } = [];

    [DataMember(Order = 4)]
    public bool FlagReached { get; set; }

    [DataMember(Order = 5)]
    public int ElapsedMs { get; set; }
}

// ────────────────────────────────────────────────────────────────
// Subscribe
// ────────────────────────────────────────────────────────────────

[DataContract]
public class SubscribeRequest
{
    [DataMember(Order = 1)]
    public string SessionId { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public List<string> Tags { get; set; } = [];

    [DataMember(Order = 3)]
    public int SamplingMs { get; set; }
}

// ────────────────────────────────────────────────────────────────
// CheckApiKey
// ────────────────────────────────────────────────────────────────

[DataContract]
public class CheckApiKeyRequest
{
    [DataMember(Order = 1)]
    public string ApiKey { get; set; } = string.Empty;
}

[DataContract]
public class CheckApiKeyResponse
{
    [DataMember(Order = 1)]
    public bool IsValid { get; set; }

    [DataMember(Order = 2)]
    public string Message { get; set; } = string.Empty;
}
