using EasyDevCore.Common;
using System.Data;
using System.Data.Common;

namespace EasyDev.Database
{
    /// <summary>
    /// 
    /// </summary>
    public static class ParameterUtils
    {
        /// <summary>
        /// Gathers the output parameters.
        /// </summary>
        /// <param name="rowsAffected">The rows affected.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public static Dictionary<string, object> GatherOutputParameters(int rowsAffected, IEnumerable<DbParameter> parameters)
        {
            Dictionary<string, object> results = new Dictionary<string, object>();
            foreach (DbParameter param in parameters.OfType<DbParameter>().Where(p => p.Direction.In(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)))
            {
                switch (param.ParameterName)
                {
                    case "RETURN_ROWS_EFFECTED":
                        results.Add(param.ParameterName, rowsAffected);
                        break;
                    default:
                        results.Add(param.ParameterName, param.Value);
                        break;
                }
            }
            return results;
        }

        /// <summary>
        /// Gets the parameters.
        /// </summary>
        /// <param name="isNumberParam">if set to <c>true</c> [is number parameter].</param>
        /// <param name="args">The arguments.</param>
        /// <returns></returns>
        public static EasyDbParameterCollection GetParameters(bool isNumberParam, params object[] args)
        {
            EasyDbParameterCollection parameters = null;
            bool isProccsed = false;

            if (args[args.Length - 1] is object[])
            {
                var lastArgs = args[args.Length - 1] as object[];
                args = args.Where((source, index) => index != args.Length - 1).ToArray();
                args = args.Concat(lastArgs).ToArray();
            }

            if (!isNumberParam)
            {
                if (args.Length == 1 && args[0] is EasyDbParameterCollection)
                {
                    parameters = (EasyDbParameterCollection)args[0];
                    isProccsed = true;
                }
                else if (args.Length == 1 && !args[0].IsBaseSystemType())
                {
                    IDictionary<string, object> argValues = ArgumentHelper.GetArgValues(args);
                    if (args.Length > 0)
                    {
                        parameters = CreateParamters(string.Join(",", argValues.Keys.Select((s) =>
                        {
                            if (s.EndsWith("_out", StringComparison.InvariantCultureIgnoreCase))
                            {
                                return "=" + s.Left(s.Length - 4);
                            }
                            else if (s.EndsWith("_ret", StringComparison.InvariantCultureIgnoreCase))
                            {
                                return "==" + s.Left(s.Length - 4);
                            }
                            return s;
                        })), argValues.Values.ToArray());
                    }
                    isProccsed = true;
                }
                else if (args.Length >= 2 && args[0] is string)
                {
                    string paramNames = (string)args[0];
                    parameters = CreateParamters(paramNames, args.Skip(1).ToArray());
                    isProccsed = true;
                }
            }
            if (!isProccsed)
            {
                parameters = new EasyDbParameterCollection();
                for (int i = 0; i < args.Length; i++)
                {
                    parameters.Add(CreateParamter("p" + i.ToString(), args[i]));
                }
            }
            return parameters;
        }

        /// <summary>
        /// Creates the paramter.
        /// </summary>
        /// <param name="paramName">Name of the parameter (prefix = means output, == means return).</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static EasyDbParameter CreateParamter(string paramName, object value)
        {
            bool isInOut = paramName.StartsWith("=");
            bool isReturn = paramName.StartsWith("==");
            paramName = paramName.TrimStart(new[] { '=', '?' });
            return CreateParamter(paramName, value, isInOut ? ParameterDirection.InputOutput : isReturn ? ParameterDirection.ReturnValue : ParameterDirection.Input);
        }

        /// <summary>
        /// Creates the paramter.
        /// </summary>
        /// <param name="paramName">Name of the parameter.</param>
        /// <param name="value">The value.</param>
        /// <param name="direction">The direction.</param>
        /// <returns></returns>
        public static EasyDbParameter CreateParamter(string paramName, object value, ParameterDirection direction)
        {
            EasyDbParameter param = new EasyDbParameter() { ParameterName = paramName, Value = value, Direction = direction };
            return param;
        }

        /// <summary>
        /// Creates the paramters.
        /// </summary>
        /// <param name="paramNames">The parameter names.</param>
        /// <param name="values">The values.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">Length of paramNames and values are not the same !</exception>
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
        public static EasyDbParameterCollection CreateParamters(string paramNames, params object[] values)
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
        {
            string[] names = paramNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (names.Length != values.Length)
            {
                throw new ArgumentException("Length of paramNames and values are not the same !");
            }
            EasyDbParameterCollection parameters = new EasyDbParameterCollection();
            for (int i = 0; i < values.Length; i++)
            {
                var param = parameters.Add(CreateParamter(names[i], values[i]));
            }
            parameters.Add(CreateParamter("RETURN_ROWS_EFFECTED", -1, ParameterDirection.Output));
            parameters.Add(CreateParamter("RETURN_VALUE", null, ParameterDirection.Output));
            return parameters;
        }
    }
}
