using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Proto;
using NodaTime;
using NodaTime.Serialization.Protobuf;

namespace DysonNetwork.Shared.Models;

public class SnCloudFile : ModelBase, ICloudFile, IIdentifiedResource
{
    /// The id generated by TuS, basically just UUID remove the dash lines
    [MaxLength(32)]
    public string Id { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty);

    [MaxLength(1024)] public string Name { get; set; }
    [MaxLength(4096)] public string? Description { get; set; }
    [Column(TypeName = "jsonb")] public Dictionary<string, object?>? UserMeta { get; set; }
    [Column(TypeName = "jsonb")] public List<ContentSensitiveMark>? SensitiveMarks { get; set; } = [];

    [NotMapped]
    [Column(TypeName = "jsonb")]
    public Dictionary<string, object?> FileMeta => Object?.Meta ?? [];
    [NotMapped] [MaxLength(256)] public string? MimeType => Object?.MimeType;
    [NotMapped] [MaxLength(256)] public string? Hash => Object?.Hash;
    public Instant? ExpiredAt { get; set; }
    [NotMapped] public long Size => Object?.Size ?? 0;
    public Instant? UploadedAt { get; set; }
    [NotMapped] public bool HasCompression => Object?.HasCompression ?? false;
    [NotMapped] public bool HasThumbnail => Object?.HasThumbnail ?? false;

    [MaxLength(32)] public string? ObjectId { get; set; }
    public SnFileObject? Object { get; set; }

    [JsonIgnore] public SnFileBundle? Bundle { get; set; }
    public Guid? BundleId { get; set; }
    [JsonIgnore] public List<SnCloudFileIndex> FileIndexes { get; set; } = [];

    /// <summary>
    /// The field is set to true if the recycling job plans to delete the file.
    /// Due to the unstable of the recycling job, this doesn't really delete the file until a human verifies it.
    /// </summary>
    public bool IsMarkedRecycle { get; set; } = false;

    /// The object name which stored remotely,
    /// multiple cloud file may have same storage id to indicate they are the same file
    ///
    /// If the storage id was null and the uploaded at is not null, means it is an embedding file,
    /// The embedding file means the file is store on another site,
    /// or it is a webpage (based on mimetype)
    [MaxLength(32)]
    public string? StorageId { get; set; }

    /// This field should be null when the storage id is filled
    /// Indicates the off-site accessible url of the file
    [MaxLength(4096)]
    public string? StorageUrl { get; set; }

    [NotMapped]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? FastUploadLink { get; set; }

    public Guid AccountId { get; set; }

    public SnCloudFileReferenceObject ToReferenceObject()
    {
        return new SnCloudFileReferenceObject
        {
            CreatedAt = CreatedAt,
            UpdatedAt = UpdatedAt,
            DeletedAt = DeletedAt,
            Id = Id,
            Name = Name,
            FileMeta = FileMeta,
            UserMeta = UserMeta ?? [],
            SensitiveMarks = SensitiveMarks,
            MimeType = MimeType,
            Hash = Hash,
            Size = Size,
            HasCompression = HasCompression
        };
    }

    public string ResourceIdentifier => $"file:{Id}";

    /// <summary>
    /// Converts the CloudFile to a protobuf message
    /// </summary>
    /// <returns>The protobuf message representation of this object</returns>
    public CloudFile ToProtoValue()
    {
        var proto = new CloudFile
        {
            Id = Id,
            Name = Name,
            MimeType = MimeType ?? string.Empty,
            Hash = Hash ?? string.Empty,
            Size = Size,
            HasCompression = HasCompression,
            Url = StorageUrl ?? string.Empty,
            ContentType = MimeType ?? string.Empty,
            UploadedAt = UploadedAt?.ToTimestamp(),
            FileMeta = GrpcTypeHelper.ConvertObjectToByteString(FileMeta),
            UserMeta = GrpcTypeHelper.ConvertObjectToByteString(UserMeta),
            SensitiveMarks = GrpcTypeHelper.ConvertObjectToByteString(SensitiveMarks)
        };

        if (FileMeta.TryGetValue("width", out var width) && width is int w)
            proto.Width = w;
        if (FileMeta.TryGetValue("height", out var height) && height is int h)
            proto.Height = h;
        if (FileMeta.TryGetValue("blurhash", out var blurhash) && blurhash is string bh && !string.IsNullOrEmpty(bh))
            proto.Blurhash = bh;

        if (Object != null)
        {
            proto.Object = new Proto.FileObject
            {
                Id = Object.Id,
                Size = Object.Size,
                Meta = GrpcTypeHelper.ConvertObjectToByteString(Object.Meta),
                MimeType = Object.MimeType ?? string.Empty,
                Hash = Object.Hash ?? string.Empty,
                HasCompression = Object.HasCompression,
                HasThumbnail = Object.HasThumbnail
            };
        }

        return proto;
    }
}