using System.Text.Json;
using System.Text.Json.Serialization;
using TeleWave.Domain.Programming;

namespace TeleWave.Application.Programming.Templates;

/// <summary>Слот в снимке. Идентификатор сохраняется — по нему откат правит слот на месте.</summary>
public sealed record SlotSnapshot(
    Guid Id,
    int? Weekday,
    TimeOnly TargetStart,
    int TargetDurationMinutes,
    string Title,
    Daypart Daypart,
    SlotKind SlotKind,
    Guid? GroupId,
    string? StrategyJson,
    string? RepeatSourceJson,
    SlotBlockMode BlockMode,
    int BlockValue,
    OverflowPolicy OverflowPolicy,
    bool IsAnchor,
    int MaxDriftMinutes,
    int? SnapToMinutes,
    Guid? JunctionBetweenId,
    Guid? JunctionAfterId
);

public sealed record LayerSnapshot(
    Guid Id,
    string Name,
    int Priority,
    string? ApplicabilityJson,
    bool IsEnabled,
    bool IsBackground,
    IReadOnlyList<SlotSnapshot> Slots
);

/// <summary>
/// Слепок шаблона: слои, слоты и настройки на момент применения. Снимается при каждом применении —
/// это ровно то, по чему идёт эфир, и потому корректная точка отката.
///
/// Хранится JSON-строкой в самом шаблоне, а не отдельной таблицей: снимок читается только целиком
/// и только при откате, и своя таблица со слоями и слотами задвоила бы всю схему сетки — включая
/// внешние ключи на группы, которые к моменту отката могут быть уже удалены.
/// </summary>
public sealed record TemplateSnapshot(
    string Name,
    Guid? FallbackGroupId,
    Guid? DefaultJunctionId,
    string? RulesJson,
    IReadOnlyList<LayerSnapshot> Layers
)
{
    private static readonly JsonSerializerOptions Options = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        Converters = { new JsonStringEnumConverter() },
    };

    public static TemplateSnapshot Capture(ScheduleTemplate template) =>
        new(
            template.Name,
            template.FallbackGroupId,
            template.DefaultJunctionId,
            template.RulesJson,
            [
                .. template.Layers.Select(layer => new LayerSnapshot(
                    layer.Id,
                    layer.Name,
                    layer.Priority,
                    layer.ApplicabilityJson,
                    layer.IsEnabled,
                    layer.IsBackground,
                    [
                        .. layer.Slots.Select(slot => new SlotSnapshot(
                            slot.Id,
                            slot.Weekday,
                            slot.TargetStart,
                            slot.TargetDurationMinutes,
                            slot.Title,
                            slot.Daypart,
                            slot.SlotKind,
                            slot.GroupId,
                            slot.StrategyJson,
                            slot.RepeatSourceJson,
                            slot.BlockMode,
                            slot.BlockValue,
                            slot.OverflowPolicy,
                            slot.IsAnchor,
                            slot.MaxDriftMinutes,
                            slot.SnapToMinutes,
                            slot.JunctionBetweenId,
                            slot.JunctionAfterId
                        )),
                    ]
                )),
            ]
        );

    public string ToJson() => JsonSerializer.Serialize(this, Options);

    /// <summary>Разбирает снимок. Повреждённый JSON — как отсутствие снимка: откатывать не к чему.</summary>
    public static TemplateSnapshot? FromJson(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
            return null;

        try
        {
            return JsonSerializer.Deserialize<TemplateSnapshot>(json, Options);
        }
        catch (JsonException)
        {
            return null;
        }
    }
}
