using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace StellaOps.Feedser.Storage.Mongo.Documents;

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

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

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

    [BsonElement("fetchedAt")]
    public DateTime FetchedAt { get; set; }

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

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

    [BsonElement("contentType")]
    [BsonIgnoreIfNull]
    public string? ContentType { get; set; }

    [BsonElement("headers")]
    [BsonIgnoreIfNull]
    public BsonDocument? Headers { get; set; }

    [BsonElement("metadata")]
    [BsonIgnoreIfNull]
    public BsonDocument? Metadata { get; set; }

    [BsonElement("etag")]
    [BsonIgnoreIfNull]
    public string? Etag { get; set; }

    [BsonElement("lastModified")]
    [BsonIgnoreIfNull]
    public DateTime? LastModified { get; set; }

    [BsonElement("expiresAt")]
    [BsonIgnoreIfNull]
    public DateTime? ExpiresAt { get; set; }

    [BsonElement("gridFsId")]
    [BsonIgnoreIfNull]
    public ObjectId? GridFsId { get; set; }
}

internal static class DocumentDocumentExtensions
{
    public static DocumentDocument FromRecord(DocumentRecord record)
    {
        return new DocumentDocument
        {
            Id = record.Id.ToString(),
            SourceName = record.SourceName,
            Uri = record.Uri,
            FetchedAt = record.FetchedAt.UtcDateTime,
            Sha256 = record.Sha256,
            Status = record.Status,
            ContentType = record.ContentType,
            Headers = ToBson(record.Headers),
            Metadata = ToBson(record.Metadata),
            Etag = record.Etag,
            LastModified = record.LastModified?.UtcDateTime,
            GridFsId = record.GridFsId,
            ExpiresAt = record.ExpiresAt?.UtcDateTime,
        };
    }

    public static DocumentRecord ToRecord(this DocumentDocument document)
    {
        IReadOnlyDictionary<string, string>? headers = null;
        if (document.Headers is not null)
        {
            headers = document.Headers.Elements.ToDictionary(
                static e => e.Name,
                static e => e.Value?.ToString() ?? string.Empty,
                StringComparer.Ordinal);
        }

        IReadOnlyDictionary<string, string>? metadata = null;
        if (document.Metadata is not null)
        {
            metadata = document.Metadata.Elements.ToDictionary(
                static e => e.Name,
                static e => e.Value?.ToString() ?? string.Empty,
                StringComparer.Ordinal);
        }

        return new DocumentRecord(
            Guid.Parse(document.Id),
            document.SourceName,
            document.Uri,
            DateTime.SpecifyKind(document.FetchedAt, DateTimeKind.Utc),
            document.Sha256,
            document.Status,
            document.ContentType,
            headers,
            metadata,
            document.Etag,
            document.LastModified.HasValue ? DateTime.SpecifyKind(document.LastModified.Value, DateTimeKind.Utc) : null,
            document.GridFsId,
            document.ExpiresAt.HasValue ? DateTime.SpecifyKind(document.ExpiresAt.Value, DateTimeKind.Utc) : null);
    }

    private static BsonDocument? ToBson(IReadOnlyDictionary<string, string>? values)
    {
        if (values is null)
        {
            return null;
        }

        var document = new BsonDocument();
        foreach (var kvp in values)
        {
            document[kvp.Key] = kvp.Value;
        }

        return document;
    }

}
