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

namespace DysonNetwork.Drive.Storage;

public class CloudFile : 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; } = string.Empty;
    [MaxLength(4096)] public string? Description { get; set; }
    [Column(TypeName = "jsonb")] public Dictionary<string, object?>? FileMeta { get; set; }
    [Column(TypeName = "jsonb")] public Dictionary<string, object?>? UserMeta { get; set; }
    [Column(TypeName = "jsonb")] public List<ContentSensitiveMark>? SensitiveMarks { get; set; } = [];
    [MaxLength(256)] public string? MimeType { get; set; }
    [MaxLength(256)] public string? Hash { get; set; }
    public Instant? ExpiredAt { get; set; }
    public long Size { get; set; }
    public Instant? UploadedAt { get; set; }
    public bool HasCompression { get; set; } = false;
    public bool HasThumbnail { get; set; } = false;
    public bool IsEncrypted { get; set; } = false;

    public FilePool? Pool { get; set; }
    public Guid? PoolId { get; set; }
    [JsonIgnore] public FileBundle? Bundle { get; set; }
    public Guid? BundleId { 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 ICollection<CloudFileReference> References { get; set; } = new List<CloudFileReference>();

    public Guid AccountId { get; set; }

    public CloudFileReferenceObject ToReferenceObject()
    {
        return new CloudFileReferenceObject
        {
            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 Shared.Proto.CloudFile ToProtoValue()
    {
        var proto = new Shared.Proto.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(),
            // Convert file metadata
            FileMeta = GrpcTypeHelper.ConvertObjectToByteString(FileMeta),
            // Convert user metadata
            UserMeta = GrpcTypeHelper.ConvertObjectToByteString(UserMeta),
            SensitiveMarks = GrpcTypeHelper.ConvertObjectToByteString(SensitiveMarks)
        };

        return proto;
    }
}

public class CloudFileReference : ModelBase
{
    public Guid Id { get; set; } = Guid.NewGuid();
    [MaxLength(32)] public string FileId { get; set; } = null!;
    public CloudFile File { get; set; } = null!;
    [MaxLength(1024)] public string Usage { get; set; } = null!;
    [MaxLength(1024)] public string ResourceId { get; set; } = null!;

    /// <summary>
    /// Optional expiration date for the file reference
    /// </summary>
    public Instant? ExpiredAt { get; set; }

    /// <summary>
    /// Converts the CloudFileReference to a protobuf message
    /// </summary>
    /// <returns>The protobuf message representation of this object</returns>
    public Shared.Proto.CloudFileReference ToProtoValue()
    {
        return new Shared.Proto.CloudFileReference
        {
            Id = Id.ToString(),
            FileId = FileId,
            File = File?.ToProtoValue(),
            Usage = Usage,
            ResourceId = ResourceId,
            ExpiredAt = ExpiredAt?.ToTimestamp()
        };
    }
}
