using System;
using System.Text.RegularExpressions;
using IoTClient.Clients.PLC;
using IoTClient.Common.Enums;

namespace StandardScene.Signal.Plc
{
    /// <summary>
    /// 西门子 S7 长连接封装（IoTClient）。一个机构一个实例，由对应 Worker 独占。
    /// </summary>
    /// <remarks>
    /// 字节地址兼容老 Hsl 写法 <c>DB1.0</c> 与 IoTClient 写法 <c>DB1.DBB0</c>；
    /// 心跳位地址保持原样（如 <c>DB1.DBX2.0</c>），不做 DBB 转换。
    /// 连接失败时读/写返回 false，由 Worker 标断线，不在这里抛给扫车线程。
    /// </remarks>
    public sealed class SiemensPlcSession : IDisposable
    {
        /// <summary>匹配 Hsl 风格「DB块号.字节偏移」，捕获组 1=DB，2=偏移。</summary>
        private static readonly Regex BareDbByte = new Regex(@"^DB(\d+)\.(\d+)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        private readonly SiemensVersion _version;
        private readonly string _ip;
        private readonly int _port;
        private readonly byte _slot;
        private SiemensClient _client;
        private readonly object _sync = new object();

        /// <param name="version">进程字段 PlcVersion，如 S7_1500。</param>
        /// <param name="ip">机构 IP。</param>
        /// <param name="port">S7 端口，通常 102。</param>
        /// <param name="slot">机架槽号，S7-1500 常见 1。</param>
        public SiemensPlcSession(string version, string ip, int port, int slot)
        {
            _version = ParseVersion(version);
            _ip = ip ?? "";
            _port = port > 0 ? port : 102;
            _slot = (byte)Math.Max(0, slot);
        }

        /// <summary>当前客户端是否已 Open 成功。未创建客户端时为 false。</summary>
        public bool Connected
        {
            get
            {
                lock (_sync) return _client?.Connected == true;
            }
        }

        /// <summary>惰性创建客户端并 Open。已连接则直接 true；失败返回 false 不抛。</summary>
        public bool EnsureOpen()
        {
            lock (_sync)
            {
                try
                {
                    if (_client == null)
                        _client = new SiemensClient(_version, _ip, _port, _slot);
                    if (_client.Connected)
                        return true;
                    var r = _client.Open();
                    return r != null && r.IsSucceed;
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>读一字节。地址会先规范化；未连接或失败时 value=0 且返回 false。</summary>
        public bool TryReadByte(string address, out byte value)
        {
            value = 0;
            if (string.IsNullOrWhiteSpace(address) || !EnsureOpen())
                return false;
            try
            {
                var r = _client.ReadByte(NormalizeByteAddress(address));
                if (r == null || !r.IsSucceed)
                    return false;
                value = r.Value;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>写一字节（进入中/到位等）。失败返回 false。</summary>
        public bool TryWriteByte(string address, byte value)
        {
            if (string.IsNullOrWhiteSpace(address) || !EnsureOpen())
                return false;
            try
            {
                var r = _client.Write(NormalizeByteAddress(address), value);
                return r != null && r.IsSucceed;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>读心跳位。地址不做 DBB 转换。</summary>
        public bool TryReadBool(string address, out bool value)
        {
            value = false;
            if (string.IsNullOrWhiteSpace(address) || !EnsureOpen())
                return false;
            try
            {
                var r = _client.ReadBoolean(address.Trim());
                if (r == null || !r.IsSucceed)
                    return false;
                value = r.Value;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>写心跳位（通常写当前值的反码，让 PLC 看到翻转）。</summary>
        public bool TryWriteBool(string address, bool value)
        {
            if (string.IsNullOrWhiteSpace(address) || !EnsureOpen())
                return false;
            try
            {
                var r = _client.Write(address.Trim(), value);
                return r != null && r.IsSucceed;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>关闭并丢弃客户端，下次 EnsureOpen 会新建。</summary>
        public void Close()
        {
            lock (_sync)
            {
                try { _client?.Close(); }
                catch { }
                _client = null;
            }
        }

        public void Dispose() => Close();

        /// <summary>
        /// <c>DB1.0</c> → <c>DB1.DBB0</c>；已是 DBB/DBW/DBX 的地址原样返回。
        /// </summary>
        internal static string NormalizeByteAddress(string address)
        {
            var addr = address.Trim();
            var m = BareDbByte.Match(addr);
            return m.Success ? $"DB{m.Groups[1].Value}.DBB{m.Groups[2].Value}" : addr;
        }

        /// <summary>空或无法解析时默认 S7_1500。允许把 S7-1500 写成带连字符。</summary>
        internal static SiemensVersion ParseVersion(string version)
        {
            if (string.IsNullOrWhiteSpace(version))
                return SiemensVersion.S7_1500;
            return Enum.TryParse(version.Replace("-", "_"), true, out SiemensVersion parsed)
                ? parsed
                : SiemensVersion.S7_1500;
        }
    }
}
