using EVCB_OCPP.Domain;
using EVCB_OCPP.WSServer.Helper;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OCPPServer.Protocol;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace EVCB_OCPP.WSServer.Service;

public interface IConnectionLogdbService
{
    void WarmUpLog();
    void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false);
}

public class ConnectionLogdbService : IConnectionLogdbService
{
    public const string LimitConfigKey = "ConnectionLogDbLimit";

    public ConnectionLogdbService(
        IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory,
        ILogger<ConnectionLogdbService> logger,
        IConfiguration configuration)
    {
        this.connectionLogdbContextFactory = connectionLogdbContextFactory;
        this.logger = logger;
        var opLimit = GetLimit(configuration);
        this.queueHandler = new(WriteMachineLog, opLimit);
    }

    private readonly IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory;
    private readonly ILogger<ConnectionLogdbService> logger;
    private readonly QueueHandler<MachineLog> queueHandler;

    public void WarmUpLog()
    {
        try
        {
            using (var log = connectionLogdbContextFactory.CreateDbContext())
            {
                log.MachineConnectionLog.ToList();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    public void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
    {
        var log = new MachineLog(clientData, data, messageType, errorMsg, isSent);
        queueHandler.Enqueue(log);
    }

    private async Task WriteMachineLog(MachineLog log)
    {
        try
        {
            if (log.clientData == null || string.IsNullOrEmpty(log.data)) return;

            if (log.clientData.ChargeBoxId == null)
            {
                logger.LogCritical(log.clientData.Path + "]********************session ChargeBoxId null sessionId=" + log.clientData.SessionID);
            }
            using (var db = connectionLogdbContextFactory.CreateDbContext())
            {
                string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
                      "@ChargeBoxId,@MessageType,@Data,@Msg,@IsSent,@EVSEEndPoint,@Session";
                var dd = DateTime.UtcNow;
                SqlParameter[] parameter =
                {
                           new SqlParameter("CreatedOn", SqlDbType.DateTime){ Value = dd },
                           new SqlParameter("ChargeBoxId", SqlDbType.NVarChar, 50){ Value= log.clientData.ChargeBoxId==null?"unknown":log.clientData.ChargeBoxId.Replace("'","''") },
                           new SqlParameter("MessageType", SqlDbType.NVarChar , 50){ Value =  log.messageType.Replace("'","''")},
                           new SqlParameter("Data", SqlDbType.NVarChar, -1) { Value = log.data.Replace("'", "''") },
                           new SqlParameter("Msg", SqlDbType.NVarChar, 200) { Value = log.errorMsg.Replace("'", "''") },
                           new  SqlParameter("IsSent", SqlDbType.Bit) { Value = log.isSent },
                           new  SqlParameter("EVSEEndPoint", SqlDbType.NVarChar, 25) { Value = log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString() },
                           new  SqlParameter("Session", SqlDbType.NVarChar, 36) { Value = log.clientData.SessionID == null ? "123" : log.clientData.SessionID }
                    };

                await db.Database.ExecuteSqlRawAsync(sp, parameter);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    private int GetLimit(IConfiguration configuration)
    {
        var limitConfig = configuration[LimitConfigKey];
        int limit = 10;
        if (limitConfig != default)
        {
            int.TryParse(limitConfig, out limit);
        }
        return limit;
    }
}

internal record MachineLog(ClientData clientData, string data, string messageType, string errorMsg, bool isSent);