using Dapper;
using EVCB_OCPP.Packet.Features;
using EVCB_OCPP20.Packet.Messages;
using EVCB_OCPP.Packet.Messages.SubTypes;
using EVCB_OCPP.WEBAPI.Models.WebAPI;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Data.SqlClient;
using EVCB_OCPP.WEBAPI.Helpers;
using EVCB_OCPP.Domain;

namespace EVCB_OCPP.WEBAPI.Services.Integration
{

    public interface IServerTriggerService
    {
        void AddMessage(string ChargeBoxId, string uuid, IRequest request);

    }

    public enum Internal_ExecutionCode
    {

        Accepted = 1,
        Blocked,
        Expired,
        Invalid,
        ConcurrentTx,
        Scheduled,
        Inoperative,
        Operative,
        NotSupported,
        Unknown,
        RebootRequired,
        UnknownMessageId,
        UnknownVendorId,
        Idle,
        Uploaded,
        UploadFailed,
        Uploading,
        Downloaded,
        DownloadFailed,
        Downloading,
        Installing,
        InstallationFailed,
        Installed,
        Pending,
        Faulted,
        Occupied,
        Unavailable,
        Unlocked,
        UnlockFailed,
        VersionMismatch,
        Rejected,
        Failed,



    }

    public enum Internal_Actions
    {
        ChangeAvailability = 1,
        ChangeConfiguration,
        GetConfiguration,
        CancelReservation,
        ClearCache,
        ClearChargingProfile,
        GetCompositeSchedule,
        GetDiagnostics,
        GetLocalListVersion,
        RemoteStartTransaction,
        RemoteStopTransaction,
        ReserveNow,
        Reset,
        SendLocalList,
        SetChargingProfile,
        TriggerMessage,
        UnlockConnector,
        DataTransfer

    }




    public class ServerTriggerService : IServerTriggerService
    {
        private readonly SqlConnectionFactory<MainDBContext> mainDbConnectionFactory;

        //readonly string mainConnectionString;
        public ServerTriggerService(SqlConnectionFactory<MainDBContext> mainDbConnectionFactory)
        {
            var dt = new DateTime(1991, 1, 1).AddHours(8).ToUniversalTime();
            long ticks = dt.Ticks;
            this.mainDbConnectionFactory = mainDbConnectionFactory;

            //mainConnectionString = configuration.GetConnectionString("MainDBContext");
        }

        public void AddMessage(string ChargeBoxId, string uuid, IRequest request)
        {
            string sql_MachineOperateRecord = "INSERT INTO [dbo].[MachineOperateRecord](SerialNo, RequestType, Status, CreatedOn, FinishedOn,"
         + " EVSE_Value, EVSE_Status, ChargeBoxId, Action, ReportedOn) VALUES (@SerialNo, @RequestType,  @Status, @CreatedOn, @FinishedOn, @EVSE_Value, "
         + "@EVSE_Status, @ChargeBoxId, @Action,@ReportedOn);";

            string sql_ServerMessage = "INSERT INTO [dbo].[ServerMessage] (SerialNo, OutAction, OutRequest, InMessage, CreatedOn, CreatedBy, ReceivedOn, ChargeBoxId" +
                ", UpdatedOn)  VALUES (@SerialNo, @OutAction, @OutRequest, @InMessage, @CreatedOn, @CreatedBy, @ReceivedOn, @ChargeBoxId" +
                ", @UpdatedOn);";

            string key = string.Empty;
            var parameters = new DynamicParameters();
            parameters.Add("@ChargeBoxId", ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
            parameters.Add("@RequestType", 1, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@RequestContent", JsonConvert.SerializeObject(request, EVCBConfiguration.JSONSERIALIZER_FORMAT), DbType.String, ParameterDirection.Input);
            parameters.Add("@Status", 0, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@CreatedOn", DateTime.Now.ToUniversalTime(), DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@FinishedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@EVSE_Value", string.Empty, DbType.String, ParameterDirection.Input);
            parameters.Add("@EVSE_Status", 0, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@SerialNo", uuid, DbType.String, ParameterDirection.Input, 36);
            parameters.Add("@Action", request.Action, DbType.String, ParameterDirection.Input, 30);

            parameters.Add("@OutAction", request.Action, DbType.String, ParameterDirection.Input, 30);
            parameters.Add("@OutRequest", JsonConvert.SerializeObject(request, EVCBConfiguration.JSONSERIALIZER_FORMAT), DbType.String, ParameterDirection.Input);
            parameters.Add("@InMessage", "", DbType.String, ParameterDirection.Input);
            parameters.Add("@CreatedOn", DateTime.Now.ToUniversalTime(), DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@CreatedBy", "WebAPI", DbType.String, ParameterDirection.Input, 36);
            parameters.Add("@ReceivedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@UpdatedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@ReportedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);


            using (SqlConnection conn = mainDbConnectionFactory.Create())
            {
                //conn.Open();
                using (var transaction = conn.BeginTransaction())
                {
                    try
                    {
                        conn.Execute(sql_MachineOperateRecord, parameters, transaction);
                        conn.Execute(sql_ServerMessage, parameters, transaction);
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();
                        throw;
                    }
                }

            }
        }

        public void AddMessage(string ChargeBoxId, string uuid, EVCB_OCPP.Packet.Messages.IRequest request)
        {
            string sql_MachineOperateRecord = "INSERT INTO [dbo].[MachineOperateRecord](SerialNo, RequestType, RequestContent,Status, CreatedOn, FinishedOn,"
         + " EVSE_Value, EVSE_Status, ChargeBoxId, Action, ReportedOn) VALUES (@SerialNo, @RequestType, @RequestContent, @Status, @CreatedOn, @FinishedOn, @EVSE_Value, "
         + "@EVSE_Status, @ChargeBoxId, @Action,@ReportedOn);";

            string sql_ServerMessage = "INSERT INTO [dbo].[ServerMessage] (SerialNo, OutAction, OutRequest, InMessage, CreatedOn, CreatedBy, ReceivedOn, ChargeBoxId" +
                ", UpdatedOn)  VALUES (@SerialNo, @OutAction, @OutRequest, @InMessage, @CreatedOn, @CreatedBy, @ReceivedOn, @ChargeBoxId" +
                ", @UpdatedOn);";

            string key = string.Empty;
            var parameters = new DynamicParameters();
            parameters.Add("@ChargeBoxId", ChargeBoxId, DbType.String, ParameterDirection.Input);
            parameters.Add("@RequestType", 1, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@RequestContent", JsonConvert.SerializeObject(request, EVCBConfiguration.JSONSERIALIZER_FORMAT), DbType.String, ParameterDirection.Input);
            parameters.Add("@Status", 0, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@CreatedOn", DateTime.Now.ToUniversalTime(), DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@FinishedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@EVSE_Value", string.Empty, DbType.String, ParameterDirection.Input);
            parameters.Add("@EVSE_Status", 0, DbType.Int32, ParameterDirection.Input);
            parameters.Add("@SerialNo", uuid, DbType.String, ParameterDirection.Input);
            parameters.Add("@Action", request.Action, DbType.String, ParameterDirection.Input);

            parameters.Add("@OutAction", request.Action, DbType.String, ParameterDirection.Input);
            parameters.Add("@OutRequest", JsonConvert.SerializeObject(request, EVCBConfiguration.JSONSERIALIZER_FORMAT), DbType.String, ParameterDirection.Input);
            parameters.Add("@InMessage", "", DbType.String, ParameterDirection.Input);
            parameters.Add("@CreatedOn", DateTime.Now.ToUniversalTime(), DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@CreatedBy", "WebAPI", DbType.String, ParameterDirection.Input);
            parameters.Add("@ReceivedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@UpdatedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);
            parameters.Add("@ReportedOn", EVCBConfiguration.DefaultTime, DbType.DateTime, ParameterDirection.Input);


            using (SqlConnection conn = mainDbConnectionFactory.Create())
            {
                //conn.Open();
                using (var transaction = conn.BeginTransaction())
                {
                    try
                    {
                        conn.Execute(sql_MachineOperateRecord, parameters, transaction);
                        conn.Execute(sql_ServerMessage, parameters, transaction);
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();
                        throw;
                    }
                }

            }
        }


        public ComandExecution GetExecution(string uuid, Internal_Actions action, string chargeBoxId)
        {
            if (string.IsNullOrEmpty(chargeBoxId) || string.IsNullOrEmpty(uuid)) return null;
            ComandExecution excution = new ComandExecution();

            try
            {
                var parameters = new DynamicParameters();
                parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
                parameters.Add("@SerialNo", uuid, DbType.String, ParameterDirection.Input, 36);
                parameters.Add("@Action", action.ToString(), DbType.String, ParameterDirection.Input, 30);

                MachineOperation operation = new MachineOperation();

                using (SqlConnection conn = mainDbConnectionFactory.Create())
                {
                    string strSql = "Select Top(1) Status,EVSE_Value,EVSE_Status,Action from [dbo].[MachineOperateRecord] where ChargeBoxId=@ChargeBoxId and SerialNo=@SerialNo and Action=@Action; ";
                    operation = conn.Query<MachineOperation>(strSql, parameters, null, true, EVCBConfiguration.DB_DefaultConnectionTimeout).FirstOrDefault();
                }

                if (operation.EVSE_Status > 0)
                {
                    excution.IsRepliedbyEVSE = true;

                    if (operation.Action == "GetLocalListVersion")
                    {
                        excution.Code = 1;
                        excution.Detail = operation.EVSE_Value;
                    }
                    else
                    {
                        excution.Code = ConverttoCode(operation.EVSE_Value);
                        excution.Code = excution.Code == -1 ? (operation.EVSE_Status == 1 ? 1 : (operation.EVSE_Status == 255 ? 254 : excution.Code)) : excution.Code;
                        excution.Detail = operation.EVSE_Value;
                    }

                }
                else
                {
                    excution.IsRepliedbyEVSE = false;
                    if (operation.Status == 0)
                    {
                        excution.Code = 0;
                        //excution.IsWaited = true;
                        excution.Detail = "Waited";

                    }
                    if (operation.Status == -1)
                    {
                      //  excution.IsTimeout = true;
                        excution.Code = 0;
                        excution.Detail = "Timeout";
                    }


                }


            }
            catch
            {
                excution = null;
            }



            return excution;

        }

        public ComandExecution GetExecution_ocpp201(string uuid, Internal_Actions action, string chargeBoxId)
        {
            if (string.IsNullOrEmpty(chargeBoxId) || string.IsNullOrEmpty(uuid)) return null;
            ComandExecution excution = new ComandExecution();

            try
            {
                var parameters = new DynamicParameters();
                parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input);
                parameters.Add("@SerialNo", uuid, DbType.String, ParameterDirection.Input);
                parameters.Add("@Action", action.ToString(), DbType.String, ParameterDirection.Input);

                MachineOperation operation = new MachineOperation();

                using (SqlConnection conn = mainDbConnectionFactory.Create())
                {
                    string strSql = "Select Top(1) Status,EVSE_Value,EVSE_Status,Action from [dbo].[MachineOperateRecord] where ChargeBoxId=@ChargeBoxId and SerialNo=@SerialNo and Action=@Action; ";
                    operation = conn.Query<MachineOperation>(strSql, parameters, null, true, EVCBConfiguration.DB_DefaultConnectionTimeout).FirstOrDefault();
                }

                if (operation.EVSE_Status > 0)
                {
                    excution.IsRepliedbyEVSE = true;

                    if (operation.Action == "GetLocalListVersion")
                    {
                        excution.Code = 1;
                        excution.Detail = operation.EVSE_Value;
                    }
                    else
                    {
                        excution.Code = ConverttoCode(operation.EVSE_Value);
                        excution.Code = excution.Code == -1 ? (operation.EVSE_Status == 1 ? 1 : (operation.EVSE_Status == 255 ? 254 : excution.Code)) : excution.Code;
                        excution.Detail = operation.EVSE_Value;
                    }

                }
                else
                {
                    excution.IsRepliedbyEVSE = false;
                    if (operation.Status == 0)
                    {
                        excution.Code = 0;
                        //excution.IsWaited = true;
                        excution.Detail = "Waited";

                    }
                    if (operation.Status == -1)
                    {
                        //  excution.IsTimeout = true;
                        excution.Code = 0;
                        excution.Detail = "Timeout";
                    }


                }


            }
            catch
            {
                excution = null;
            }



            return excution;

        }




        public int ConverttoCode(string statusName)
        {
            int code = -1;

            if (string.IsNullOrEmpty(statusName)) return code;
            var tt = AuthorizationStatus.Accepted.ToString();

            Internal_ExecutionCode result = Internal_ExecutionCode.Accepted;
            if (Enum.TryParse<Internal_ExecutionCode>(statusName, out result))
            {
                code = (int)result;
            }

            return code;
        }
    }
}