using System.Text.Json.Serialization;

namespace ScadaLink.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
{
    public string InstanceUniqueName { get; init; } = string.Empty;
    public int TemplateId { get; init; }
    public int SiteId { get; init; }
    public int? AreaId { get; init; }
    public IReadOnlyList<ResolvedAttribute> Attributes { get; init; } = [];
    public IReadOnlyList<ResolvedAlarm> Alarms { get; init; } = [];
    public IReadOnlyList<ResolvedScript> Scripts { get; init; } = [];
    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
{
    public string Protocol { get; init; } = string.Empty;
    public string? ConfigurationJson { get; init; }
    public string? BackupConfigurationJson { get; init; }
    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;

    public string? Value { get; init; }
    public string DataType { get; init; } = string.Empty;
    public bool IsLocked { get; init; }
    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
{
    public string CanonicalName { get; init; } = string.Empty;
    public string? Description { get; init; }
    public int PriorityLevel { get; init; }
    public bool IsLocked { get; init; }
    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; }

    public string Source { get; init; } = "Template";
}

/// <summary>
/// A fully resolved script with code, trigger config, parameters, and return definition.
/// </summary>
public sealed record ResolvedScript
{
    public string CanonicalName { get; init; } = string.Empty;
    public string Code { get; init; } = string.Empty;
    public bool IsLocked { get; init; }
    public string? TriggerType { get; init; }
    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; }

    public TimeSpan? MinTimeBetweenRuns { get; init; }
    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 ScadaLink.Commons.Types.Scripts.ScriptScope Scope { get; init; }
        = ScadaLink.Commons.Types.Scripts.ScriptScope.Root;
}
