using System.Text.Json.Serialization;

namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Flattening;

/// <summary>
/// The fully resolved configuration for a single instance, produced by the flattening algorithm.
/// All inheritance, composition, overrides, and connection bindings have been applied.
/// This is the canonical representation sent to sites for deployment.
/// </summary>
public sealed record FlattenedConfiguration
{
    /// <summary>Gets the instance unique name.</summary>
    public string InstanceUniqueName { get; init; } = string.Empty;
    /// <summary>Gets the template ID.</summary>
    public int TemplateId { get; init; }
    /// <summary>Gets the site ID.</summary>
    public int SiteId { get; init; }
    /// <summary>Gets the area ID, if any.</summary>
    public int? AreaId { get; init; }
    /// <summary>Gets the resolved attributes.</summary>
    public IReadOnlyList<ResolvedAttribute> Attributes { get; init; } = [];
    /// <summary>Gets the resolved alarms.</summary>
    public IReadOnlyList<ResolvedAlarm> Alarms { get; init; } = [];
    /// <summary>Gets the resolved scripts.</summary>
    public IReadOnlyList<ResolvedScript> Scripts { get; init; } = [];
    /// <summary>Gets the resolved native alarm source bindings.</summary>
    public IReadOnlyList<ResolvedNativeAlarmSource> NativeAlarmSources { get; init; } = [];
    /// <summary>Gets the UTC timestamp when this configuration was generated.</summary>
    public DateTimeOffset GeneratedAtUtc { get; init; } = DateTimeOffset.UtcNow;

    /// <summary>
    /// Connection configurations keyed by connection name, each containing the
    /// protocol-specific settings (e.g. OPC UA endpoint, publish interval).
    /// Populated during flattening from the instance's connection bindings.
    /// </summary>
    public IReadOnlyDictionary<string, ConnectionConfig>? Connections { get; init; }
}

/// <summary>
/// Connection configuration included in the flattened deployment package.
/// </summary>
public sealed record ConnectionConfig
{
    /// <summary>Gets the protocol name (e.g., "OpcUa").</summary>
    public string Protocol { get; init; } = string.Empty;
    /// <summary>Gets the primary configuration as JSON.</summary>
    public string? ConfigurationJson { get; init; }
    /// <summary>Gets the backup configuration as JSON.</summary>
    public string? BackupConfigurationJson { get; init; }
    /// <summary>Gets the number of failover retries.</summary>
    public int FailoverRetryCount { get; init; } = 3;
}

/// <summary>
/// A fully resolved attribute with its canonical name, value, data type, and optional data source binding.
/// </summary>
public sealed record ResolvedAttribute
{
    /// <summary>
    /// Path-qualified canonical name. For composed modules: "[ModuleInstanceName].[MemberName]".
    /// For direct attributes: just the attribute name.
    /// </summary>
    public string CanonicalName { get; init; } = string.Empty;

    /// <summary>Gets the attribute value.</summary>
    public string? Value { get; init; }
    /// <summary>Gets the data type name.</summary>
    public string DataType { get; init; } = string.Empty;
    /// <summary>For List attributes: the element scalar type name; null otherwise.</summary>
    public string? ElementDataType { get; init; }
    /// <summary>Gets whether the attribute is locked.</summary>
    public bool IsLocked { get; init; }
    /// <summary>Gets the attribute description.</summary>
    public string? Description { get; init; }

    /// <summary>
    /// If data-sourced: the relative tag path within the connection.
    /// </summary>
    public string? DataSourceReference { get; init; }

    /// <summary>
    /// If data-sourced: the resolved connection ID from the instance binding.
    /// </summary>
    public int? BoundDataConnectionId { get; init; }

    /// <summary>
    /// If data-sourced: the resolved connection name.
    /// </summary>
    public string? BoundDataConnectionName { get; init; }

    /// <summary>
    /// If data-sourced: the connection protocol (e.g. "OpcUa").
    /// </summary>
    public string? BoundDataConnectionProtocol { get; init; }

    /// <summary>
    /// The source of this attribute value: "Template", "Inherited", "Composed", "Override".
    /// </summary>
    public string Source { get; init; } = "Template";
}

/// <summary>
/// A fully resolved alarm with trigger definition containing resolved attribute references.
/// </summary>
public sealed record ResolvedAlarm
{
    /// <summary>Gets the path-qualified canonical name.</summary>
    public string CanonicalName { get; init; } = string.Empty;
    /// <summary>Gets the alarm description.</summary>
    public string? Description { get; init; }
    /// <summary>Gets the priority level.</summary>
    public int PriorityLevel { get; init; }
    /// <summary>Gets whether the alarm is locked.</summary>
    public bool IsLocked { get; init; }
    /// <summary>Gets the trigger type.</summary>
    public string TriggerType { get; init; } = string.Empty;

    /// <summary>
    /// JSON trigger configuration with resolved attribute references (canonical names).
    /// </summary>
    public string? TriggerConfiguration { get; init; }

    /// <summary>
    /// Canonical name of the on-trigger script, if any.
    /// </summary>
    public string? OnTriggerScriptCanonicalName { get; init; }

    /// <summary>Gets the source of this alarm value: "Template", "Inherited", "Composed", or "Override".</summary>
    public string Source { get; init; } = "Template";
}

/// <summary>
/// A fully resolved native alarm source binding (data connection + source
/// reference). Conditions under the source are discovered at runtime; this
/// record only carries the binding, not individual alarms.
/// </summary>
public sealed record ResolvedNativeAlarmSource
{
    /// <summary>Gets the path-qualified canonical name.</summary>
    public string CanonicalName { get; init; } = string.Empty;
    /// <summary>Gets the data connection name that owns the alarm feed.</summary>
    public string ConnectionName { get; init; } = string.Empty;
    /// <summary>Gets the source reference (OPC UA SourceNode/notifier nodeId, or MxAccess object/area).</summary>
    public string SourceReference { get; init; } = string.Empty;
    /// <summary>Gets the optional condition filter; null = mirror all conditions under the source.</summary>
    public string? ConditionFilter { get; init; }
    /// <summary>Gets the source of this binding: "Template", "Inherited", "Composed", or "Override".</summary>
    public string Source { get; init; } = "Template";
    /// <summary>
    /// Gets whether this native alarm source is locked at the template level. A locked
    /// source cannot be repointed by an instance override — mirrors the attribute/alarm
    /// lock semantics.
    /// </summary>
    public bool IsLocked { get; init; }
}

/// <summary>
/// A fully resolved script with code, trigger config, parameters, and return definition.
/// </summary>
public sealed record ResolvedScript
{
    /// <summary>Gets the path-qualified canonical name.</summary>
    public string CanonicalName { get; init; } = string.Empty;
    /// <summary>Gets the script code.</summary>
    public string Code { get; init; } = string.Empty;
    /// <summary>Gets whether the script is locked.</summary>
    public bool IsLocked { get; init; }
    /// <summary>Gets the trigger type.</summary>
    public string? TriggerType { get; init; }
    /// <summary>Gets the trigger configuration.</summary>
    public string? TriggerConfiguration { get; init; }

    /// <summary>
    /// JSON-serialized parameter definitions.
    /// </summary>
    public string? ParameterDefinitions { get; init; }

    /// <summary>
    /// JSON-serialized return type definition.
    /// </summary>
    public string? ReturnDefinition { get; init; }

    /// <summary>Gets the minimum time between script executions.</summary>
    public TimeSpan? MinTimeBetweenRuns { get; init; }

    /// <summary>
    /// Per-script execution timeout in seconds, or null to use the site's global
    /// default. A non-positive value is treated as null (use global) by the Site
    /// Runtime. Seconds (not TimeSpan) to match the global option it overrides.
    /// </summary>
    public int? ExecutionTimeoutSeconds { get; init; }

    /// <summary>Gets the source of this script.</summary>
    public string Source { get; init; } = "Template";

    /// <summary>
    /// Where the script sits in the composition tree. Seeded into ScriptGlobals
    /// so the script's <c>Attributes</c> / <c>Children</c> / <c>Parent</c>
    /// accessors resolve canonical names with the right path-prefix.
    /// </summary>
    public ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts.ScriptScope Scope { get; init; }
        = ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts.ScriptScope.Root;
}
