using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.net.Enums.Public;
using Luski.net.JsonTypes.Public;

namespace Luski.net.Structures.Public;

public class SocketChannel
{
    public PublicServer Server { get; init; } = default!;
    public Color Color { get; init; }
    public long ID { get; internal set; }
    internal long CategoryID { get; set; }
    internal long[] RoleOverides { get; set; }
    internal long[] UserOverides { get; set; }
    SocketCategory? RawParent = null;
    List<RoleOveride>? RawRoleOverides = null;
    List<UserOveride>? RawUserOverides = null;
    public PictureType PictureType { get; internal set; }
    
    public async Task<IReadOnlyList<SocketMessage>> GetMessages(CancellationToken CancellationToken, int count = 50)
    {
        try
        {
            if (count > 200)
            {
                throw new Exception("You can not request more than 200 messages at a time");
            }
            else if (count < 1)
            {
                throw new Exception("You must request at least 1 message");
            }
            else
            {
                PublicSocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage",
                    PublicSocketBulkMessageContext.Default.PublicSocketBulkMessage,
                    CancellationToken,
                    new KeyValuePair<string, string?>("id", ID.ToString()),
                    new KeyValuePair<string, string?>("messages", count.ToString()));
                if (data is not null && !data.Error.HasValue)
                {
                    int num = Convert.ToInt32(6);
                    if (num == 0) num = 1;
                    //string key = Server.EncryptionHandler.GetKey(enc)
                    //string key = Server.EncryptionHandler.GetChannelKey(ID);
                    if (data.Messages is null) data.Messages = Array.Empty<PublicMessage>();
                    List<SocketMessage> mmmm = new();
                    ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions()
                    {
                        MaxDegreeOfParallelism = num
                    }, i =>
                    {
                        if (i.EncryptionKey == 0)
                        {
                            if (string.IsNullOrEmpty(i.Context))
                            {
                                i.Context = "";
                            }
                            else i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
                                .GetString(Convert.FromBase64String(i.Context));
                        }
                        else
                        {
                            LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.EncryptionKey);
                            switch (key.EncryptionType)
                            {
                                case EncryptionType.RSA:
                                    i.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
                                        i.EncoderType);
                                    break;
                                default:
                                    i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
                                        .GetString(Convert.FromBase64String(i.Context));
                                    break;
                            }
                        }

                        if (i.Files.Length > 0)
                        {
                            for (int j = 0; j < i.Files.Length; j++)
                            {
                                if (i.Files[j].Key == 0)
                                {
                                    if (string.IsNullOrEmpty(i.Files[j].Name))
                                    {
                                        i.Files[j].Name = "";
                                    }
                                    else i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
                                        .GetString(Convert.FromBase64String(i.Files[j].Name));
                                }
                                else
                                {
                                    LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.Files[j].NameKey);
                                    switch (key.EncryptionType)
                                    {
                                        case EncryptionType.RSA:
                                            i.Files[j].Name = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
                                                i.Files[j].NameEncoder);
                                            break;
                                        default:
                                            i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
                                                .GetString(Convert.FromBase64String(i.Context));
                                            break;
                                    }
                                }
                            }
                        }
                    });
                    
                    foreach (PublicMessage i in data.Messages)
                    {
                        var ff = new List<long>();
                        List<SocketFile> sf = new();
                        foreach (pFile v in i.Files)
                        {
                            sf.Add(new()
                            {
                                ID = v.ID,
                                Size = v.Size,
                                Name = v.Name,
                                Encoder = v.Encoder,
                                NameEncoder = v.NameEncoder,
                                Key = v.Key,
                                NameKey = v.NameKey,
                                Channel = v.Channel,
                                Server = Server
                            });
                            ff.Add(v.ID);
                        }
                        mmmm.Add(new()
                        {
                            Server = Server,
                            ID = i.ID,
                            ChannelID = ID,
                            AuthorID = i.AuthorID,
                            TimeStamp = i.TimeStamp,
                            Context = i.Context,
                            EncryptionKey = i.EncryptionKey,
                            EncoderType = i.EncoderType,
                            FileIDs = ff.ToArray(),
                            _Files = sf
                        });
                    }
                    return await Task.FromResult(mmmm.AsReadOnly());
                }
                else
                {
                    throw data?.Error switch
                    {
                        ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"),
                        ErrorCode.ServerError => new Exception($"Error from server: {data.ErrorMessage}"),
                        ErrorCode.InvalidHeader => new Exception(data.ErrorMessage),
                        ErrorCode.MissingHeader => new Exception("The header sent to the server was not found. This may be because you app is couropt or you are using the wron API version"),
                        ErrorCode.Forbidden => new Exception("You are not allowed to do this request"),
                        _ => new Exception(data?.Error.ToString()),
                    };
                }
            }
        }
        catch (Exception)
        {
            throw;
        }
    }
    
    public async Task<Stream> GetPicture(CancellationToken CancellationToken)
    {
        bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString());
        if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString(), CancellationToken,
            new KeyValuePair<string, string?>("id", ID.ToString()));
        return Server.Storage.GetResourceStream(StorageDirectory.ChannelIcons, ID.ToString());
    }

    public async Task<SocketCategory> GetParent()
    {
        if (CategoryID != -1 && RawParent is null)
        {
            RawParent = await Server.GetCategory<SocketCategory>(CategoryID, CancellationToken.None);
        }

        return RawParent!;
    }
    public ChannelType Type { get; internal set; }
    public DateTime Epoch { get; internal set; }
    public string Name { get; internal set; }
    public string Description { get; internal set; }
    public Task<RoleOveride[]> GetRoleOverides()
    {
        if (RawRoleOverides is null)
        {
            RawRoleOverides = new();
        }

        return Task.FromResult(RawRoleOverides!.ToArray());
    }
    public Task<UserOveride[]> GetUserOveride()
    {
        if (RawUserOverides is null)
        {
            RawUserOverides = new();
        }

        return Task.FromResult(RawUserOverides!.ToArray());
    }
    public long TitleEncryptionKey { get; internal set; }
    public long DescriptionEncryptionKey { get; internal set; }
    public long[] EncryptionKeys { get; internal set; }
    public EncoderType TitleEncoderType { get; internal set; }
    public EncoderType DescriptionEncoderType { get; internal set; }
    public EncoderType[] EncoderTypes { get; internal set; }
}