using System.Data;
using System.Data.Common;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using EasyDevCore.Common;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace EasyDevCore.Database.EntityFrameworkCore
{
    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="Microsoft.EntityFrameworkCore.Diagnostics.DbCommandInterceptor" />
    public class EasyCommandInterceptor: DbCommandInterceptor
    {       
        /// <summary>
        /// Initializes a new instance of the <see cref="EasyCommandInterceptor"/> class.
        /// </summary>
        public EasyCommandInterceptor()
        {
        }

        /// <summary>
        /// Called when execution of a command has failed with an exception.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <param name="eventData">Contextual information about the command and execution.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A <see cref="T:System.Threading.Tasks.Task" /> representing the asynchronous operation.
        /// </returns>
        public override Task CommandFailedAsync(DbCommand command, CommandErrorEventData eventData, CancellationToken cancellationToken = default)
        {
            if (eventData.Exception != null) {
                eventData.Exception.Data["CommandText"] = GetSQLText(command);
            }
            return base.CommandFailedAsync(command, eventData, cancellationToken);
        }

        /// <summary>
        /// Called when execution of a command has failed with an exception.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <param name="eventData">Contextual information about the command and execution.</param>
        public override void CommandFailed(DbCommand command, CommandErrorEventData eventData)
        {
            if(eventData.Exception != null) {
                eventData.Exception.Data["CommandText"] = GetSQLText(command);
            }
            base.CommandFailed(command, eventData);
        }

        /// <summary>
        /// Gets the SQL text.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <returns></returns>
        private static string GetSQLText(DbCommand command)
        {
            StringBuilder sb = new();
            sb.AppendLine($"Source : {command.Connection.DataSource}");
            sb.AppendLine($"Database : {command.Connection.Database}");
            sb.AppendLine($"Connection time out : {command.Connection.ConnectionTimeout}");
            sb.AppendLine($"Query Type : {command.CommandType}");
            sb.AppendLine($"Command time out : {command.CommandTimeout}");
            sb.AppendLine($"SQL Text : {command.CommandText}");
            foreach(DbParameter p in command.Parameters) {
                sb.AppendLine(string.Format("  ?{0}{1}({2}) = {3}", (p.Direction == ParameterDirection.Input ? "" : "="), p.ParameterName, p.DbType, (p.DbType.ToString().HasAny(System.StringComparison.InvariantCultureIgnoreCase, "string", "date", "time") ? $"'{p.Value}'" : $"{p.Value}")));
            }
            return sb.ToString();
        }
    }
}
