using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace StellaOps.Feedser.Storage.Mongo.MergeEvents;

[BsonIgnoreExtraElements]
public sealed class MergeEventDocument
{
    [BsonId]
    public string Id { get; set; } = string.Empty;

    [BsonElement("advisoryKey")]
    public string AdvisoryKey { get; set; } = string.Empty;

    [BsonElement("beforeHash")]
    public byte[] BeforeHash { get; set; } = Array.Empty<byte>();

    [BsonElement("afterHash")]
    public byte[] AfterHash { get; set; } = Array.Empty<byte>();

    [BsonElement("mergedAt")]
    public DateTime MergedAt { get; set; }

    [BsonElement("inputDocuments")]
    public List<string> InputDocuments { get; set; } = new();

    [BsonElement("fieldDecisions")]
    [BsonIgnoreIfNull]
    public List<MergeFieldDecisionDocument>? FieldDecisions { get; set; }
}

internal static class MergeEventDocumentExtensions
{
    public static MergeEventDocument FromRecord(MergeEventRecord record)
        => new()
        {
            Id = record.Id.ToString(),
            AdvisoryKey = record.AdvisoryKey,
            BeforeHash = record.BeforeHash,
            AfterHash = record.AfterHash,
            MergedAt = record.MergedAt.UtcDateTime,
            InputDocuments = record.InputDocumentIds.Select(static id => id.ToString()).ToList(),
            FieldDecisions = record.FieldDecisions.Count == 0
                ? null
                : record.FieldDecisions.Select(MergeFieldDecisionDocument.FromRecord).ToList(),
        };

    public static MergeEventRecord ToRecord(this MergeEventDocument document)
        => new(
            Guid.Parse(document.Id),
            document.AdvisoryKey,
            document.BeforeHash,
            document.AfterHash,
            DateTime.SpecifyKind(document.MergedAt, DateTimeKind.Utc),
            document.InputDocuments.Select(static value => Guid.Parse(value)).ToList(),
            document.FieldDecisions is null
                ? Array.Empty<MergeFieldDecision>()
                : document.FieldDecisions.Select(static decision => decision.ToRecord()).ToList());
}

[BsonIgnoreExtraElements]
public sealed class MergeFieldDecisionDocument
{
    [BsonElement("field")]
    public string Field { get; set; } = string.Empty;

    [BsonElement("selectedSource")]
    [BsonIgnoreIfNull]
    public string? SelectedSource { get; set; }

    [BsonElement("decisionReason")]
    public string DecisionReason { get; set; } = string.Empty;

    [BsonElement("selectedModified")]
    [BsonIgnoreIfNull]
    public DateTime? SelectedModified { get; set; }

    [BsonElement("consideredSources")]
    public List<string> ConsideredSources { get; set; } = new();

    public static MergeFieldDecisionDocument FromRecord(MergeFieldDecision record)
        => new()
        {
            Field = record.Field,
            SelectedSource = record.SelectedSource,
            DecisionReason = record.DecisionReason,
            SelectedModified = record.SelectedModified?.UtcDateTime,
            ConsideredSources = record.ConsideredSources.ToList(),
        };

    public MergeFieldDecision ToRecord()
        => new(
            Field,
            SelectedSource,
            DecisionReason,
            SelectedModified.HasValue ? DateTime.SpecifyKind(SelectedModified.Value, DateTimeKind.Utc) : null,
            ConsideredSources);
}
