using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Web;
using System.Windows;
using ChatController.Core;
using ChatController.Data;
using ChatController.LoginKlassen;
using ChatController.Utilities;

using Newtonsoft.Json;
using RestSharp;


namespace ChatController.HauptKlassen
{
    public class Login
    {
        private string _BaseUrl = string.Empty;

        private const string DictionaryServerUrl = "https://dict.ownchat.de/api/server/resolve/1/";

        public string ServerUrl { get; set; } = string.Empty;

        private string _AuthToken = string.Empty;

        private readonly string _UserName;
        private readonly string _Password;
        private readonly string _Tenant;
        private readonly string _ChatCode;
        private readonly string _ApiKey;

        private long? _UserId;

        private bool _OwnchatVerbindungsaufbauOk;
        private bool _GruppeholenverbindungsaufbauOk;

        private UserDaten _LoggedInUser;
        private ChatGruppenDaten _AllGroups;

        private readonly bool _ShouldShowMessageBox = true;

        public Login(string pTenant, string pChatCode, string pUserName, string pPassword)
        {
            _Tenant   = pTenant;
            _ChatCode = pChatCode;
            _UserName = pUserName;
            _Password = pPassword;
            _ApiKey   = "0000";
        }
        
        // Wird im BeWoPlaner benutzt
        public Login(string pTenant, string pChatCode, string pUserName, string pPassword, string pApiKey)
        {
            _Tenant = pTenant;
            _ChatCode = pChatCode;
            _UserName = pUserName;
            _Password = pPassword;
            _ApiKey = pApiKey;
        }
        
        // Wird im BeWoPlaner benutzt
        public Login(string tenant, Action<string> callback)
        {
            _Tenant = tenant;
            _ShouldShowMessageBox = false;

            // TODO: Ändern, sonst wird keine Fehlermeldung angezeigt!
            LookupServerUrlAsync(isSuccessful => { callback(ServerUrl); }, null);
        }

        // Wird im BeWoPlaner benutzt
        public Login(string tenant, Action<string> callback, Action<string> exceptionCallback)
        {
            _Tenant = tenant;
            _ShouldShowMessageBox = false;

            LookupServerUrlAsync(isSuccessful => { callback(ServerUrl); }, exceptionCallback);
        }

        // Wird im BeWoPlaner benutzt
        public ChatDatenUebergabe AnmeldevorgangDurchFuehren()
        {
            if(ServerErmittlung())
            {
                var dic = new Dictionary<string, string>
                {
                   { "username", _UserName},
                   { "password", _Password},
                   { "chatcode", _ChatCode}
                };

                VerbindeMitChatServer(dic);

                if(_OwnchatVerbindungsaufbauOk)
                {
                    GetGroups();
                }

                if(_GruppeholenverbindungsaufbauOk)
                {
                    var maxUploadSize = GetMaiximumAllowedFileUploadSize(_AuthToken, _Tenant);

                    return new ChatDatenUebergabe(_LoggedInUser, _AllGroups, ServerUrl, _AuthToken, _UserId, _Tenant, _ApiKey, maxUploadSize);
                }
            }

            return null;
        }

        public void DoLoginAsync(Action<ChatDatenUebergabe> callback, Action<string> exceptionCallback)
        {
            try
            {
                LookupServerUrlAsync(isConnectedWithServer =>
                {
                    if(isConnectedWithServer)
                    {
                        ConnectWithChatServerAsync(
                            isSuccessfullyLoggedIn =>
                            {
                                if (isSuccessfullyLoggedIn)
                                {
                                    GetGroupsAsync(wasSuccessful =>
                                    {
                                        if (wasSuccessful)
                                        {
                                            GetMaximumAllowedFileUploadSizeAsync(maxUploadFileSize =>
                                            {
                                                callback?.Invoke(new ChatDatenUebergabe(_LoggedInUser, _AllGroups, ServerUrl, _AuthToken, _UserId, _Tenant, _ApiKey, maxUploadFileSize));
                                            });
                                        } 
                                    });
                                }
                            }, 

                            errorMessage => { exceptionCallback?.Invoke(errorMessage); });
                    }
                }, errorMessage => { exceptionCallback?.Invoke(errorMessage); });
            }
            catch (Exception exception)
            {
                callback?.Invoke(null);
            }
        }

        public void LookupServerUrlAsync(Action<bool> callback, Action<string> exceptionCallback)
        {
            try
            {
                if(!string.IsNullOrWhiteSpace(ServerUrl))
                {
                    callback?.Invoke(false);
                    return;
                }

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

                var client = new RestClient(DictionaryServerUrl);
                var request = new RestRequest(_Tenant);

                client.ExecuteAsync(request, response =>
                {
                    var lookupResult = JsonConvert.DeserializeObject<LookupResult>(response.Content);

                    _BaseUrl = lookupResult.Url;
                    ServerUrl = _BaseUrl;

                    var result = false;

                    switch (lookupResult.Status)
                    {
                        case 0:
                            result = true;
                            break;
                        case 1:
                            exceptionCallback?.Invoke("Fehler: Kundennummer unbekannt.\nBitte überprüfen Sie die Anmeldeinformationen.");
                            break;
                        case 2:
                            exceptionCallback?.Invoke("Der Diensttyp ist für die angegebene Kundennummer nicht definiert.");
                            MessageBox.Show("Der Diensttyp ist für die angegebene Kundennummer nicht definiert.", "ownChat Info", MessageBoxButton.OK, MessageBoxImage.Asterisk);
                            break;
                    }

                    callback?.Invoke(result);
                });
            }
            catch (Exception exception)
            {
                exceptionCallback?.Invoke("Fehler: Es konnte keine Verbindung aufgebaut werden.");
            }
        }

        public bool ServerErmittlung()
        {
            if (string.IsNullOrEmpty(ServerUrl))
            {
                try
                {
                    var serverURl = DictionaryServerUrl + _Tenant;
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
                    var request = WebRequest.Create(serverURl);

                    request.Credentials = CredentialCache.DefaultCredentials;

                    WebResponse response;

                    try
                    {
                        response = request.GetResponse();
                    }
                    catch (Exception e)
                    {
                        if (_ShouldShowMessageBox)
                        {
                            MessageBox.Show("Fehler: Es konnte keine Verbindung aufgebaut werden.", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                        
                        return false;
                    }

                    var responseFromServer = Utils.ReadStream(response);

                    try
                    {
                        var jsonDatentyp = JsonConvert.DeserializeObject<AuthenticationDataType1>(responseFromServer);

                        switch(jsonDatentyp.Status)
                        {
                            case 1:
                                if (_ShouldShowMessageBox)
                                {
                                    MessageBox.Show("Fehler: Kundennummer unbekannt.\nBitte überprüfen Sie die Anmeldeinformationen.", "ownChat Info", MessageBoxButton.OK, MessageBoxImage.Asterisk);
                                }
                            
                                return false;
                            case 2:
                                if (_ShouldShowMessageBox)
                                {
                                    MessageBox.Show("Der Diensttyp ist für die angegebene Kundennummer nicht definiert.", "ownChat Info", MessageBoxButton.OK, MessageBoxImage.Asterisk);
                                }
                            
                                return false;
                        }

                        _BaseUrl = jsonDatentyp.Url;
                    }
                    catch (Exception eds)
                    {
                        try
                        {
                            var jsonDatentyp = JsonConvert.DeserializeObject<AuthenticationDataType2>(responseFromServer);

                            switch(jsonDatentyp.Status)
                            {
                                case 1:
                                    if (_ShouldShowMessageBox)
                                    {
                                        MessageBox.Show("Fehler: Kundennummer unbekannt.\nBitte überprüfen Sie die Anmeldeinformationen.", "ownChat Info", MessageBoxButton.OK, MessageBoxImage.Asterisk);
                                    }
                                    
                                    return false;
                                case 2:
                                    if (_ShouldShowMessageBox)
                                    {
                                        MessageBox.Show("Der Diensttyp ist für die angegebene Kundennummer nicht definiert.", "ownChat Info", MessageBoxButton.OK, MessageBoxImage.Asterisk);
                                    }
                                    
                                    return false;
                            }

                            _BaseUrl = jsonDatentyp.Url;
                        }
                        catch (Exception e)
                        {
                            if (_ShouldShowMessageBox)
                            {
                                MessageBox.Show("Fehler: " + e.Message, "ownChat Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                            }
                        }
                    }

                    response.Close();

                    ServerUrl = _BaseUrl;
                    return true;
                }
                catch (Exception e)
                {
                    if (_ShouldShowMessageBox)
                    {
                        MessageBox.Show("Fehler: " + e.Message, "ownChat Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                    
                    return false;
                }
            }
            
            return true;
        }

        private void VerbindeMitChatServer(Dictionary<string, string> postparameter)
        {
            try
            {
                var endurl = _BaseUrl + Constants.LoginWithChatCodeUrl; 

                var requestBody = postparameter.Keys.Aggregate(string.Empty, (current, key) => current + HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(postparameter[key]) + "&");
                
                var client = new RestClient(endurl);
                var request = new RestRequest(Method.POST);

                request.AddHeader("content-type", "application/x-www-form-urlencoded");
                request.AddParameter(Constants.CustomerId, _Tenant, ParameterType.HttpHeader);
                request.AddParameter("application/x-www-form-urlencoded", requestBody, ParameterType.RequestBody);

                var response = client.Post(request);

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    Debug.WriteLine(response.Content);
                    var userDaten = JsonConvert.DeserializeObject<UserDaten>(response.Content);

                    if (userDaten.Success)
                    {
                        _AuthToken = userDaten.Response.User.Token;
                        _UserId = userDaten.Response.User.Oid;

                        Utils.AuthToken = _AuthToken;
                        Utils.Tenant = _Tenant;

                        _LoggedInUser = userDaten;

                        _OwnchatVerbindungsaufbauOk = true;
                    }
                    else
                    {
                        if (userDaten.Error?.ChatCodeFailed != null)
                        {
                            if (_ShouldShowMessageBox)
                            {
                                MessageBox.Show("Fehler: " + userDaten.Error.ChatCodeFailed[0] + "\nBitte überprüfen Sie die Anmeldeinformationen.", "Info", MessageBoxButton.OK, MessageBoxImage.Error);
                            }
                        }
                        else if (userDaten.Error?.LoginFailed != null)
                        {
                            if (_ShouldShowMessageBox)
                            {
                                MessageBox.Show("Fehler: " + userDaten.Error.LoginFailed[0] + "\nBitte überprüfen Sie die Anmeldeinformationen.", "Info", MessageBoxButton.OK, MessageBoxImage.Error);
                            }
                        }

                        _OwnchatVerbindungsaufbauOk = false;
                    }
                }
                else
                {
                    MessageBox.Show(response.Content, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            catch (Exception e)
            {
                if (_ShouldShowMessageBox)
                {
                    MessageBox.Show("Fehler:" + e.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void GetGroups()
        {
            try
            {
                var endurl = _BaseUrl + Constants.GetChatGroupsUrl;

                var request = WebRequest.Create(endurl);

                request.Credentials = CredentialCache.DefaultCredentials;
                request.Headers[Constants.Token] = _AuthToken;
                request.Headers[Constants.CustomerId] = _Tenant;

                using (var response = request.GetResponse())
                {
                    var serverResponse = Utils.ReadStream(response);
                    
                    if (!string.IsNullOrEmpty(serverResponse))
                    {
                        Debug.WriteLine($"'ChatGruppenDaten' (Gruppen): {serverResponse}");

                        var jsonDaten = JsonConvert.DeserializeObject<ChatGruppenDaten>(serverResponse);

                        _AllGroups = jsonDaten;

                        _GruppeholenverbindungsaufbauOk = true;
                    }
                }
            }
            catch (Exception e)
            {
                if (_ShouldShowMessageBox)
                {
                    MessageBox.Show("Fehler: " + e.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void ConnectWithChatServerAsync(Action<bool> callback, Action<string> exceptionCallback)
        {
            var url = _BaseUrl + Constants.LoginWithChatCodeUrl;
            var requestBody = $"{HttpUtility.UrlEncode("username")}={HttpUtility.UrlEncode(_UserName)}&{HttpUtility.UrlEncode("password")}={HttpUtility.UrlEncode(_Password)}&{HttpUtility.UrlEncode("chatcode")}={HttpUtility.UrlEncode(_ChatCode)}";
            
            var client = new RestClient(url);
            var request = new RestRequest(Method.POST);

            request.AddHeader(Constants.ContentTypeKey, Constants.FormUrlEncodedContentTypeValue);
            request.AddParameter(Constants.CustomerId, _Tenant, ParameterType.HttpHeader);
            request.AddParameter(Constants.FormUrlEncodedContentTypeValue, requestBody, ParameterType.RequestBody);

            client.PostAsync(request, (response, handle) =>
            {
                var connectionData = JsonConvert.DeserializeObject<UserDaten>(response.Content);

                if(connectionData != null && connectionData.Success)
                {
                    _AuthToken = connectionData.Response.User.Token;
                    _UserId = connectionData.Response.User.Oid;

                    Utils.AuthToken = _AuthToken;
                    Utils.Tenant = _Tenant;

                    _LoggedInUser = connectionData;

                    OwnChatCache.GetInstance().LoggedOnUserId = _LoggedInUser.Response.User.Oid;

                    callback?.Invoke(true);
                }
                else
                {
                    var errorMessage = string.Empty;

                    if (connectionData?.Error?.ChatCodeFailed != null)
                    {
                        errorMessage = $"Fehler: {connectionData.Error.ChatCodeFailed[0]}";
                    }
                    else if (connectionData?.Error?.LoginFailed != null)
                    {
                        errorMessage = $"Fehler: {connectionData.Error.LoginFailed[0]}";
                    }

                    if (!string.IsNullOrWhiteSpace(errorMessage))
                    {
                        exceptionCallback?.Invoke(errorMessage);
                    }
                }
            });
        }

        private void GetGroupsAsync(Action<bool> callback)
        {
            var url = _BaseUrl + Constants.GetChatGroupsUrl;

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var client = new RestClient(url);

            var request = new RestRequest();
            request.AddHeader(Constants.Token, _AuthToken);
            request.AddHeader(Constants.CustomerId, _Tenant);

            client.ExecuteAsync(request, response =>
            {
                var groups = JsonConvert.DeserializeObject<ChatGruppenDaten>(response.Content);

                _AllGroups = groups;

                callback?.Invoke(true);
            });
        }

        public long GetMaiximumAllowedFileUploadSize(string pToken, string pCustomerId)
        {
            long maximumFileSize;
            
            try
            {
                var endurl = ServerUrl + Constants.MaxUploadFileSizeUrl;

                var request = WebRequest.Create(endurl);
                request.Headers[Constants.Token] = pToken; 
                request.Headers[Constants.CustomerId] = pCustomerId;

                request.Credentials = CredentialCache.DefaultCredentials;
                request.Proxy = null;

                using (var response = request.GetResponse())
                {
                    var responseFromServer = Utils.ReadStream(response);

                    var definiti = new {file_upload_max_size = ""};

                    var jsonDatentyp = JsonConvert.DeserializeAnonymousType(responseFromServer, definiti);
                    maximumFileSize = Convert.ToInt64(jsonDatentyp.file_upload_max_size);
                }
            }
            catch (Exception e)
            {
                return 0;
            }

            return maximumFileSize;
        }

        public void GetMaximumAllowedFileUploadSizeAsync(Action<long> callback)
        {
            var url = ServerUrl + Constants.MaxUploadFileSizeUrl;

            var client = new RestClient(url);
            var request = new RestRequest();

            request.AddHeader(Constants.Token, _AuthToken);
            request.AddHeader(Constants.CustomerId, _Tenant);

            client.ExecuteAsync(request, response =>
            {
                var maxFileSizeAnonymous = JsonConvert.DeserializeAnonymousType(response.Content, new { file_upload_max_size = string.Empty });

                var maxUploadFileSize = Convert.ToInt64(maxFileSizeAnonymous.file_upload_max_size);

                callback?.Invoke(maxUploadFileSize);
            });
        }
    }
}
