using InABox.Clients;
using InABox.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace InABox.IPC.Shared
{
    public enum RequestError
    {
        NONE,
        DISCONNECTED,
        UNKNOWN
    }

    public enum Method
    {
        None,
        Query,
        Save,
        MultiSave,
        Delete,
        MultiDelete,
        QueryMultiple,
        Validate,
        Check2FA,
        Ping,
        Info
    }

    [Serializable]
    public class PipeRequest
    {
        public Guid RequestID;
        public Method Method;
        public string? Type;
        public string Data;

        [NonSerialized]
        public RequestError ErrorCode;

        private PipeRequest(Guid requestID, Method method, string? type, string data, RequestError error = RequestError.NONE)
        {
            RequestID = requestID;
            Method = method;
            Type = type;
            Data = data;
            ErrorCode = error;
        }

        public PipeRequest Respond<TResponse>(TResponse response) where TResponse : Response
        {
            return new PipeRequest(RequestID, Method.None, Type, Serialization.Serialize(response));
        }

        public TRequest GetRequest<TRequest>()
        {
            return Serialization.Deserialize<TRequest>(Data);
        }

        public TResponse GetResponse<TResponse>() where TResponse : Response, new()
        {
            var start = DateTime.Now;
            var response = Serialization.Deserialize<TResponse>(Data);
            if (response == null) response = new TResponse();
            switch (ErrorCode)
            {
                case RequestError.NONE:
                    break;
                case RequestError.DISCONNECTED:
                    response.Status = StatusCode.Error;
                    response.Messages.Add("Server disconnected");
                    break;
                case RequestError.UNKNOWN:
                    response.Status = StatusCode.Error;
                    response.Messages.Add("Unknown Error");
                    break;
            }
            return response;
        }

        public static PipeRequest Query<T>(QueryRequest<T> request) where T : Entity, new()
        {
            return new PipeRequest(Guid.NewGuid(), Method.Query, typeof(T).Name, Serialization.Serialize(request));
        }

        public static PipeRequest Save<T>(SaveRequest<T> request) where T : Entity, new()
        {
            return new PipeRequest(Guid.NewGuid(), Method.Save, typeof(T).Name, Serialization.Serialize(request));
        }

        public static PipeRequest MultiSave<T>(MultiSaveRequest<T> request) where T : Entity, new()
        {
            return new PipeRequest(Guid.NewGuid(), Method.MultiSave, typeof(T).Name, Serialization.Serialize(request));
        }

        public static PipeRequest Delete<T>(DeleteRequest<T> request) where T : Entity, new()
        {
            return new PipeRequest(Guid.NewGuid(), Method.Delete, typeof(T).Name, Serialization.Serialize(request));
        }

        public static PipeRequest MultiDelete<T>(MultiDeleteRequest<T> request) where T : Entity, new()
        {
            return new PipeRequest(Guid.NewGuid(), Method.MultiDelete, typeof(T).Name, Serialization.Serialize(request));
        }

        public static PipeRequest QueryMultiple(MultiQueryRequest request)
        {
            return new PipeRequest(Guid.NewGuid(), Method.QueryMultiple, null, Serialization.Serialize(request));
        }

        public static PipeRequest Validate(ValidateRequest request)
        {
            return new PipeRequest(Guid.NewGuid(), Method.Validate, null, Serialization.Serialize(request));
        }

        public static PipeRequest Check2FA(Check2FARequest request)
        {
            return new PipeRequest(Guid.NewGuid(), Method.Check2FA, null, Serialization.Serialize(request));
        }

        public static PipeRequest Error(RequestError error)
        {
            return new PipeRequest(Guid.NewGuid(), Method.None, null, "", error);
        }

        public static PipeRequest Ping(PingRequest request)
        {
            return new PipeRequest(Guid.NewGuid(), Method.Ping, null, Serialization.Serialize(request));
        }
        
        public static PipeRequest Info(InfoRequest request)
        {
            return new PipeRequest(Guid.NewGuid(), Method.Info, null, Serialization.Serialize(request));
        }
    }
}
