namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
///
/// Parsed ab://gateway[:port]/cip-path host-address string used by the AbCip driver
/// as the hostName key across ,
/// , and the Polly bulkhead key
/// (DriverInstanceId, hostName) per v2 plan decision #144.
///
///
/// Format matches what libplctag's gateway=... + path=... attributes
/// consume, so no translation is needed at the wire layer — the parsed
/// is handed to the native library verbatim.
///
/// - ab://10.0.0.5/1,0 — single-chassis ControlLogix, CPU in slot 0.
/// - ab://10.0.0.5/1,4 — CPU in slot 4.
/// - ab://10.0.0.5/1,2,2,192.168.50.20,1,0 — bridged ControlLogix.
/// - ab://10.0.0.5/ (empty path) — Micro800 / MicroLogix without backplane routing.
/// - ab://10.0.0.5:44818/1,0 — explicit EIP port (default 44818).
///
/// Opaque to the rest of the stack: Admin UI, telemetry, and logs display the full
/// string so an incident ticket can be matched to the exact gateway + CIP route.
///
public sealed record AbCipHostAddress(string Gateway, int Port, string CipPath)
{
/// Default EtherNet/IP TCP port — spec-reserved.
public const int DefaultEipPort = 44818;
/// Recompose the canonical ab://... form.
public override string ToString() => Port == DefaultEipPort
? $"ab://{Gateway}/{CipPath}"
: $"ab://{Gateway}:{Port}/{CipPath}";
///
/// Parse . Returns null on any malformed input — callers
/// should treat a null return as a config-validation failure rather than catching.
///
public static AbCipHostAddress? TryParse(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return null;
const string prefix = "ab://";
if (!value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return null;
var remainder = value[prefix.Length..];
var slashIdx = remainder.IndexOf('/');
if (slashIdx < 0) return null;
var authority = remainder[..slashIdx];
var cipPath = remainder[(slashIdx + 1)..];
if (string.IsNullOrEmpty(authority)) return null;
var port = DefaultEipPort;
var colonIdx = authority.LastIndexOf(':');
string gateway;
if (colonIdx >= 0)
{
gateway = authority[..colonIdx];
if (!int.TryParse(authority[(colonIdx + 1)..], out port) || port <= 0 || port > 65535)
return null;
}
else
{
gateway = authority;
}
if (string.IsNullOrEmpty(gateway)) return null;
return new AbCipHostAddress(gateway, port, cipPath);
}
}