using LLNet.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Net;
using System.Text;

namespace LLNet.Tienda.MainForms.Forms.Zunify
{
    public class ZunifyUtils
    {
        private string url;
        private Dictionary<string, string> headers;
        private string ChargeCode;
        private string TerminalCode;
        private byte[] TerminalCodeQR;
        private string Currency;
        private string ErrorCode;
        private string ErrorMessage;
        private string ErrorRequestID;
        private string Status;
        private string CajaEkono;
        private string SucursalEkono;
        private string Documento;

        public ZunifyUtils(string Documento)
        {
            this.headers = new Dictionary<string, string>();
            this.headers.Add("Authorization", BacTasa0.Logica.GET_CG_PARAMETERS_VALUE("ZWHA"));
            this.url = BacTasa0.Logica.GET_CG_PARAMETERS_VALUE("ZWHD");
            //this.url = "https://public-api.dev.zunify.co";
            this.Currency = "CRC";
            this.CajaEkono = Program.Caja.ToString();
            this.SucursalEkono = BacTasa0.Logica.GET_CG_PARAMETERS_VALUE("BREE");
            this.Documento = Documento;
            Get_ZunifyTerminalCode();
        }
        public ZunifyUtils(string api, Dictionary<string, string> headers, string currency, string terminalCode)
        {
            this.headers = new Dictionary<string, string>();
            this.headers = headers;
            this.url = api;
            this.Currency = currency;
            this.TerminalCode = terminalCode;
        }
        /// <summary>
        /// Realiza la petición para crear un cargo al API de Zunify
        /// </summary>
        /// <param name="amount">Monto del cargo</param>
        /// <returns></returns>
        public bool CreateCharge(decimal amount)
        {
            object body = new { amount, terminalCode = this.TerminalCode, currency = this.Currency };
            ZunifyRespuesta result = ZunifyRequest("/public/charges/", "POST", body);
            if (result.success)
            {
                string chargeCode = result.data["chargeCode"];
                this.ChargeCode = chargeCode;
                Add_ZunifyTran("TS", amount, chargeCode, this.Currency, this.TerminalCode, this.Documento);
                return true;
            }
            else
            {
                this.ErrorCode = result.data["code"];
                this.ErrorMessage = result.data["message"];
                this.ErrorRequestID = result.data["requestId"];
                QueryParams qp = new QueryParams();
                string id = GUI.CApp.bll.GetSingleValue("GetId_ZUN_TRAN", qp, "PDV").ToString();
                Add_ZunifyLog(id, "Code " + this.ErrorCode + " Message " + this.ErrorMessage + " RequestID " + this.ErrorRequestID, "TE");
                return false;
            }

        }
        /// <summary>
        /// Realiza la petición para eliminar un cargo en específico al API de Zunify
        /// </summary>
        /// <param name="chargeCode">Código de Cargo</param>
        /// <returns></returns>
        public bool DeleteCharge()
        {
            object body = new { };
            ZunifyRespuesta result = ZunifyRequest("/public/charges/" + this.ChargeCode + "/", "DELETE", body);
            if (result.success)
            {
                string message = result.data["message"];
                Update_ZunifyTran("AC");
                return true;
            }
            else
            {
                this.ErrorCode = result.data["code"];
                this.ErrorMessage = result.data["message"];
                this.ErrorRequestID = result.data["requestId"];
                Add_ZunifyLog(Get_ZunifyTran(this.ChargeCode), "Code " + this.ErrorCode + " Message " + this.ErrorMessage + " RequestID " + this.ErrorRequestID, "AE");
                return false;
            }
        }
        /// <summary>
        /// Realiza la petición para obtener estado de un cargo en específico al API de Zunify
        /// </summary>
        /// <param name="chargeCode">Código del cargo</param>
        /// <returns></returns>
        public bool GetCharge()
        {
            object body = new { };
            ZunifyRespuesta result = ZunifyRequest("/public/charges/" + this.ChargeCode + "/", "GET", body);
            if (result.success)
            {
                string status = result.data["status"];
                this.Status = status;
                if (this.Status.Equals("Paid")) {
                    QueryParams qp = new QueryParams();
                    qp.AddReplaceParam("p_C_CARGO", this.ChargeCode);
                    string estado_registrado = GUI.CApp.bll.GetSingleValue("Get_ZUN_TRANESTADO", qp, "PDV").ToString();
                    if (!estado_registrado.Equals("TC"))
                    {
                        Update_ZunifyTran("TCV");
                    }
                }
                return true;
            }
            else
            {
                this.ErrorCode = result.data["code"];
                this.ErrorMessage = result.data["message"];
                this.ErrorRequestID = result.data["requestId"];
                Add_ZunifyLog(Get_ZunifyTran(this.ChargeCode), "Code " + this.ErrorCode + " Message " + this.ErrorMessage + " RequestID " + this.ErrorRequestID, "VE");
                return false;
            }
        }
        /// <summary>
        /// Realiza la petición para obtener retorno de un cargo en específico al API de Zunify
        /// </summary>
        /// <param name="amount">Monto a solicitar del cargo</param>
        /// <param name="currency">Tipo de moneda</param>
        /// <param name="chargeCode">Código del cargo</param>
        /// <returns></returns>
        public bool RefundCharge(decimal amount, string currency, string chargeCode)
        {
            object body = new { amount, currency, chargeCode };
            ZunifyRespuesta result = ZunifyRequest("/public/refunds/", "POST", body);
            if (result.success)
            {
                string transactionId = result.data["transactionId"];
                string count = result.data["amount"];
                string coin = result.data["currency"];
                string date = result.data["date"];
                return true;
            }
            else
            {
                string code = result.data["code"];
                string message = result.data["message"];
                string requestId = result.data["requestId"];
                this.ErrorCode = code;
                this.ErrorMessage = message;
                this.ErrorRequestID = requestId;
                return false;
            }
        }
        /// <summary>
        /// Realiza la petición para enviar la URL del Webhook en donde se van a recibir las confirmaciones de pagos provenientes del API de Zunify
        /// </summary>
        /// <param name="webhook">URL del Webhook</param>
        /// <param name="key">Token para acceder al Webhook</param>
        /// <returns></returns>
        public bool SetWebhook(string webhook, string key)
        {
            string urlwithkey = webhook + key;
            object body = new { url = urlwithkey, token = key };
            ZunifyRespuesta result = ZunifyRequest("/public/webhook/", "POST", body);
            if (result.success)
            {
                string message = result.data["message"];
                return true;
            }
            else
            {
                this.ErrorCode = result.data["code"];
                this.ErrorMessage = result.data["message"];
                Add_ZunifyLog("-4", "Code " + this.ErrorCode + " Message " + this.ErrorMessage + " RequestID " + this.ErrorRequestID, "AE");
                return false;
            }

        }

        private string Add_ZunifyTran(string estado, decimal monto, string cargo, string moneda, string terminal, string documento) {
            QueryParams qp = new QueryParams();
            string id = GUI.CApp.bll.GetSingleValue("GetId_ZUN_TRAN", qp, "PDV").ToString();
            qp.AddReplaceParam("p_N_ID", id);
            qp.AddReplaceParam("p_C_ESTADO", estado);
            qp.AddReplaceParam("p_N_MONTO", monto.ToString());
            qp.AddReplaceParam("p_C_CARGO", cargo);
            qp.AddReplaceParam("p_C_MONEDA", moneda);
            qp.AddReplaceParam("p_C_TERMINAL", terminal);
            qp.AddReplaceParam("p_N_DOCUMENTO", documento);

            //Program.Caja.ToString()

            try
            {
                GUI.CApp.bll.ExecuteSql("Add_ZUN_TRAN", qp, "PDV");
                Add_ZunifyLog(id, "Cargo " + cargo + " Terminal " + terminal + " MONTO " + monto.ToString(), "TS");
                return id;
            }
            catch (Exception e)
            {
                Add_ZunifyLog(id, "Erro en " + id + " Cargo " + cargo + " Mensaje: " + e.Message , "TE");
                return id;
            }
        }
        private string Update_ZunifyTran(string estado)
        {
            QueryParams qp = new QueryParams();
            qp.AddReplaceParam("p_C_ESTADO", estado);
            qp.AddReplaceParam("p_C_CARGO", this.ChargeCode);
            try
            {
                GUI.CApp.bll.ExecuteSql("Upt_ZUN_TRAN", qp, "PDV");
                Add_ZunifyLog(Get_ZunifyTran(this.ChargeCode), "Cargo " + this.ChargeCode, estado);
                return this.ChargeCode;
            }
            catch (Exception e)
            {
                Add_ZunifyLog(Get_ZunifyTran(this.ChargeCode), "Erro en " + this.ChargeCode, estado);
                return this.ChargeCode;
            }
        }
        private void Add_ZunifyLog(string Id_ZunTran, string req_res, string estado) {
            QueryParams qp = new QueryParams();
            qp.AddReplaceParam("p_N_ID_ZUNIFY_TRAN", Id_ZunTran);
            qp.AddReplaceParam("p_C_REQ_RES", req_res);
            qp.AddReplaceParam("p_C_ESTADO", estado);
            qp.AddReplaceParam("p_C_USUARIO", GUI.CApp.usuario);
            qp.AddReplaceParam("p_C_JORNADA", Program.Jornada.ToString());
            try
            {
                GUI.CApp.bll.ExecuteSql("Add_ZUN_LOG", qp, "PDV");
            }
            catch (Exception e) {
                string error_message = e.Message;
                string error_trace = e.StackTrace;
            }

        }
        private string Get_ZunifyTran(string cargo) {
            QueryParams qp = new QueryParams();
            qp.AddReplaceParam("p_C_CARGO", cargo);
            DataSet datos = GUI.CApp.bll.getDataSet("PDV", "Get_ZUN_TRAN", qp);
            try
            {
                return datos.Tables[0].Rows[0]["N_ID"].ToString();
            }
            catch (Exception e) {
                return GUI.CApp.bll.GetSingleValue("GetId_ZUN_TRAN", qp, "PDV").ToString();
            }
        }
        public DataSet Get_ZunifyTranList() {

            DataSet datos;
            Utils.QueryParams qp = new LLNet.Utils.QueryParams();
            qp.AddReplaceParam("p_C_TERMINAL", this.TerminalCode);
            datos = GUI.CApp.bll.getDataSet("PDV", "Get_ZUN_TRAN_LIST", qp);
            return datos;
        }
        private void Get_ZunifyTerminalCode() {
            QueryParams qp = new QueryParams();
            qp.AddReplaceParam("p_C_CODE_CAJA", this.CajaEkono);
            qp.AddReplaceParam("p_C_SUCE_TIENDA", this.SucursalEkono);
            DataSet datos = GUI.CApp.bll.getDataSet("PDV", "Get_ZUN_TERMCODE", qp);
            this.TerminalCode = datos.Tables[0].Rows[0]["C_CODZ_TERMINAL"].ToString();
            /*
            try {
                this.TerminalCodeQR = datos.Tables[0].Rows[0]["I_QR_Z_TERMINAL"];
            }
            */

        }
        public bool Change_ZunifyWMSDocument(string cargo, string documento) {
            object body = new { };
            ZunifyRespuesta result = ZunifyRequest("/public/charges/" + cargo + "/", "GET", body);
            if (result.success)
            {
                string status = result.data["status"];
                if (status.Equals("Paid"))
                {
                    QueryParams qp = new QueryParams();
                    qp.AddReplaceParam("p_C_CARGO", cargo);
                    string estado_registrado = GUI.CApp.bll.GetSingleValue("Get_ZUN_TRANESTADO", qp, "PDV").ToString();
                    if (!estado_registrado.Equals("TC"))
                    {
                        qp = new QueryParams();
                        qp.AddReplaceParam("p_C_ESTADO", "TCV");
                        qp.AddReplaceParam("p_C_CARGO", cargo);
                        try
                        {
                            GUI.CApp.bll.ExecuteSql("Upt_ZUN_TRAN", qp, "PDV");
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Cargo " + cargo, "TCV");
                        }
                        catch (Exception e)
                        {
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Erro en " + cargo, "TCE");
                        }
                    }

                    int count = 0;
                    qp = new LLNet.Utils.QueryParams();
                    qp.AddReplaceParam("p_C_DOCUMENTO_PADRE", documento);
                    count = Convert.ToInt32(GUI.CApp.bll.GetSingleValue("Get_ZUN_VALIDAPEDIDO", qp, "PDV"));

                    // Actualiza el cargo con el nuevo numero de pedido
                    if (count < 1)
                    {
                        qp = new QueryParams();
                        qp.AddReplaceParam("p_N_DOCUMENTO", this.Documento);
                        qp.AddReplaceParam("p_C_CARGO", cargo);
                        try
                        {
                            GUI.CApp.bll.ExecuteSql("Upt_ZUN_TRANDOCU", qp, "PDV");
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Cargo " + cargo + " Pedido Anterior: " + documento + " Pedido Nuevo: " + this.Documento, estado);
                            return true;
                        }
                        catch (Exception e)
                        {
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Error en " + cargo + " Pedido: " + documento + " Pedido Nuevo: " + this.Documento + " Error : " + e.Message + " " + e.StackTrace, "TCDE");
                            return false;
                        }
                    }
                }
                if (status.Equals("Pending")) {
                    System.Windows.Forms.MessageBox.Show("El cargo se encuentra Pendiente. Se procede a Anular el cargo.", "Verificar Cargo");
                    body = new { };
                    result = ZunifyRequest("/public/charges/" + cargo + "/", "DELETE", body);
                    if (result.success)
                    {
                        QueryParams qp = new QueryParams();
                        qp.AddReplaceParam("p_C_ESTADO", "AC");
                        qp.AddReplaceParam("p_C_CARGO", cargo);
                        try
                        {
                            GUI.CApp.bll.ExecuteSql("Upt_ZUN_TRAN", qp, "PDV");
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Cargo " + cargo, "AC");
                        }
                        catch (Exception e)
                        {
                            Add_ZunifyLog(Get_ZunifyTran(cargo), "Erro en " + cargo, "AE");
                        }
                    }
                    return false;
                }
                else
                {
                    return false;
                }
            }
            return false;
            
        }
        
        /// <summary>
        /// Este método se utilza para realizar peticines al API de Zunify
        /// </summary>
        /// <param name="url_file">Dirección a la que realiza la consulta</param>
        /// <param name="method">Método de la consulta</param>
        /// <param name="body">Objeto que se envía</param>
        /// <returns>ZunifyRespuesta</returns>
        private ZunifyRespuesta ZunifyRequest(string url_file, string method, object body)
        {
            try
            {
                ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
                /// Request                
                WebRequest wRequest = WebRequest.Create(this.url + url_file);
                wRequest.Method = method;

                foreach (KeyValuePair<string, string> header in this.headers)
                {
                    wRequest.Headers.Add(header.Key, header.Value);
                }

                if (method.Equals("POST"))
                {
                    string json = JsonConvert.SerializeObject(body);
                    byte[] byteArray = Encoding.UTF8.GetBytes(json);

                    wRequest.ContentLength = byteArray.Length;
                    wRequest.ContentType = "application/json; charset=UTF-8";

                    var reqStream = wRequest.GetRequestStream();
                    reqStream.Write(byteArray, 0, byteArray.Length);
                }

                /// Response
                var response = wRequest.GetResponse();
                Console.WriteLine(((HttpWebResponse)response).StatusDescription);

                var respStream = response.GetResponseStream();

                var reader = new StreamReader(respStream);
                string data_str = reader.ReadToEnd();
                Dictionary<string, dynamic> data_json = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(data_str);
                bool success = data_json["success"];
                if (success)
                {
                    dynamic data_result = data_json["data"];
                    dynamic data = JsonConvert.DeserializeObject<Dictionary<string, string>>(JsonConvert.SerializeObject(data_result));
                    return new ZunifyRespuesta(success, data);
                }
                else
                {
                    dynamic data_result = data_json["error"];
                    dynamic data = JsonConvert.DeserializeObject<Dictionary<string, string>>(JsonConvert.SerializeObject(data_result));
                    return new ZunifyRespuesta(success, data);
                }
            }catch(Exception e)
            {
                string error = e.Message;
                Dictionary<string, string> data = new Dictionary<string, string>();
                data.Add("code", "-2");
                data.Add("message", "No se puede consumir el API. " + error);
                data.Add("requestId", "-2");
                return new ZunifyRespuesta(false, data);
            }

        }

        /// Getters and Setters
        /// 
        public string Get_ErrorCode() {
            return this.ErrorCode;
        }
        public string Get_ErrorMessage()
        {
            return this.ErrorMessage;
        }
        public string Get_ErrorRequestID()
        {
            return this.ErrorRequestID;
        }
        public string Get_Status() {
            return this.Status;
        }
        public string Get_ChargeCode() {
            return this.ChargeCode;
        }
        public byte[] Get_TerminalCodeQR() {
            return this.TerminalCodeQR;
        }

    }

    public class ZunifyRespuesta
    {
        public ZunifyRespuesta(bool success, Dictionary<string, string> data)
        {
            this.success = success;
            this.data = data;
        }
        public bool success;
        public Dictionary<string, string> data;
    }


}
