using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;

namespace ZB.MOM.WW.ScadaBridge.Transport.Serialization;

/// <summary>
/// In-memory aggregate of all bundle-eligible Commons entities. Matches the
/// shape of <see cref="BundleContentDto"/> but uses the real persistence-
/// ignorant POCO types — what <see cref="EntitySerializer"/> consumes/produces
/// on the application side of the bundle boundary.
/// </summary>
// ApiKeys is intentionally absent: inbound API keys are not transported between
// environments (re-arch C4). The aggregate only carries API methods.
public sealed record EntityAggregate(
    IReadOnlyList<TemplateFolder> TemplateFolders,
    IReadOnlyList<Template> Templates,
    IReadOnlyList<SharedScript> SharedScripts,
    IReadOnlyList<ExternalSystemDefinition> ExternalSystems,
    IReadOnlyList<ExternalSystemMethod> ExternalSystemMethods,
    IReadOnlyList<DatabaseConnectionDefinition> DatabaseConnections,
    IReadOnlyList<NotificationList> NotificationLists,
    IReadOnlyList<SmtpConfiguration> SmtpConfigurations,
    IReadOnlyList<ApiMethod> ApiMethods);

/// <summary>
/// Top-level serializable bundle payload. Lists are sequenced in dependency
/// order so importers can apply them inline. Lists are never null on the wire
/// — empty arrays are preferred over nulls so JSON consumers can rely on each
/// property being present — except <see cref="ApiKeys"/>, which is intentionally
/// null-defaulted (see below).
/// <para>
/// <see cref="ApiKeys"/> is a <b>legacy, read-only</b> field retained purely for
/// backward-compatible deserialization of bundles produced before the
/// inbound-API-key re-architecture (C4). New exports never populate it (it stays
/// <c>null</c> and is dropped from the JSON by the serializer's
/// <c>WhenWritingNull</c> policy); the importer counts any keys present in an old
/// bundle, ignores them, and surfaces a note — keys are re-created per environment.
/// </para>
/// </summary>
public sealed record BundleContentDto(
    IReadOnlyList<TemplateFolderDto> TemplateFolders,
    IReadOnlyList<TemplateDto> Templates,
    IReadOnlyList<SharedScriptDto> SharedScripts,
    IReadOnlyList<ExternalSystemDto> ExternalSystems,
    IReadOnlyList<DatabaseConnectionDto> DatabaseConnections,
    IReadOnlyList<NotificationListDto> NotificationLists,
    IReadOnlyList<SmtpConfigDto> SmtpConfigs,
    IReadOnlyList<ApiMethodDto> ApiMethods,
    IReadOnlyList<ApiKeyDto>? ApiKeys = null);

/// <summary>
/// Carved-off secret values for an entity. The outer DTO carries all non-
/// sensitive fields; secrets land here so a future "share without secrets"
/// export mode can drop this block without touching anything else.
/// </summary>
public sealed record SecretsBlock(IReadOnlyDictionary<string, string> Values);

public sealed record TemplateFolderDto(
    string Name,
    string? ParentName,
    int SortOrder);

public sealed record TemplateDto(
    string Name,
    string? FolderName,
    string? BaseTemplateName,
    string? Description,
    IReadOnlyList<TemplateAttributeDto> Attributes,
    IReadOnlyList<TemplateAlarmDto> Alarms,
    IReadOnlyList<TemplateScriptDto> Scripts,
    IReadOnlyList<TemplateCompositionDto> Compositions);

public sealed record TemplateAttributeDto(
    string Name,
    string? Value,
    DataType DataType,
    bool IsLocked,
    string? Description,
    string? DataSourceReference);

public sealed record TemplateAlarmDto(
    string Name,
    string? Description,
    int PriorityLevel,
    AlarmTriggerType TriggerType,
    string? TriggerConfiguration,
    bool IsLocked,
    string? OnTriggerScriptName);

public sealed record TemplateScriptDto(
    string Name,
    string Code,
    string? TriggerType,
    string? TriggerConfiguration,
    string? ParameterDefinitions,
    string? ReturnDefinition,
    bool IsLocked,
    TimeSpan? MinTimeBetweenRuns,
    // M2.5 (#9): per-script execution timeout (seconds). Additive trailing field;
    // null on bundles written before this field existed.
    int? ExecutionTimeoutSeconds = null);

public sealed record TemplateCompositionDto(
    string InstanceName,
    string ComposedTemplateName);

public sealed record SharedScriptDto(
    string Name,
    string Code,
    string? ParameterDefinitions,
    string? ReturnDefinition);

public sealed record ExternalSystemDto(
    string Name,
    string BaseUrl,
    string AuthType,
    int MaxRetries,
    TimeSpan RetryDelay,
    IReadOnlyList<ExternalSystemMethodDto> Methods,
    SecretsBlock? Secrets);

public sealed record ExternalSystemMethodDto(
    string Name,
    string HttpMethod,
    string Path,
    string? ParameterDefinitions,
    string? ReturnDefinition);

public sealed record DatabaseConnectionDto(
    string Name,
    int MaxRetries,
    TimeSpan RetryDelay,
    SecretsBlock? Secrets);

public sealed record NotificationListDto(
    string Name,
    NotificationType Type,
    IReadOnlyList<NotificationRecipientDto> Recipients);

public sealed record NotificationRecipientDto(
    string Name,
    string EmailAddress);

public sealed record SmtpConfigDto(
    string Host,
    int Port,
    string AuthType,
    string FromAddress,
    string? TlsMode,
    int ConnectionTimeoutSeconds,
    int MaxConcurrentConnections,
    int MaxRetries,
    TimeSpan RetryDelay,
    SecretsBlock? Secrets);

// Legacy DTO: only deserialized from pre-C4 bundles so the importer can count and
// ignore the keys they contain. New exports never emit an ApiKeys array.
public sealed record ApiKeyDto(
    string Name,
    string KeyHash,
    bool IsEnabled,
    SecretsBlock? Secrets);

// ApprovedApiKeyIds is intentionally absent: it linked methods to keys that are no
// longer transported (re-arch C4). Scopes are re-granted per environment. The field
// in any old bundle is simply ignored on read.
public sealed record ApiMethodDto(
    string Name,
    string Script,
    string? ParameterDefinitions,
    string? ReturnDefinition,
    int TimeoutSeconds);
