using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using AyaNova.Biz;

namespace AyaNova.Api.ControllerHelpers
{



    public class ApiErrorResponse
    {

        [JsonIgnore]
        private ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger<ApiErrorResponse>();

        //Mandatory properties
        public ApiError Error { get; }




        //Generic error
        public ApiErrorResponse(ApiErrorCode apiCode, string target = null, string message = null)
        {

            //try to get a stock message if nothing specified
            if (message == null)
            {
                message = ApiErrorCodeStockMessage.GetMessage(apiCode);
            }

            Error = new ApiError(apiCode, message, target);

            log.LogDebug("apiCode={0}, target={1}, message={2}", apiCode, target, message);
        }


        //Bad request (MODELSTATE ISSUE) error response handling

        public ApiErrorResponse(ModelStateDictionary modelState)
        {

            if (modelState.IsValid)
            {
                throw new ArgumentException("ModelState must be invalid", nameof(modelState));
            }


            //Set outer error and then put validation in details

            Error = new ApiError(ApiErrorCode.VALIDATION_FAILED, ApiErrorCodeStockMessage.GetMessage(ApiErrorCode.VALIDATION_FAILED));


            //https://www.jerriepelser.com/blog/validation-response-aspnet-core-webapi/
            //Message = "Validation Failed";
            Error.Details = new List<ApiDetailError>();

            /*
            https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.modelerror?view=aspnetcore-2.2

            modelState["roles"].Errors
            Count = 1
            [0]:{Microsoft.AspNetCore.Mvc.ModelBinding.ModelError}
            ErrorMessage [string]:""
            Exception [Exception]:{Newtonsoft.Json.JsonSerializationException: Error converting value "" to type 'AyaNova.Biz.AuthorizationRoles'. Path 'roles', line 1, position 146. ---> System.ArgumentException: Must specify valid information for parsing in the string.\r\n   at Newtonsoft.Json.Utilities.EnumUtils.ParseEnum(Type enumType, NamingStrategy namingStrategy, String value, Boolean disallowNumber) in /_/Src/Newtonsoft.Json/Utilities/EnumUtils.cs:line 285\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 958\r\n   --- End of inner exception stack trace ---\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 989\r\n   at Newtonsoft.Json.Serialization.Jso...
            Data [IDictionary]:{System.Collections.ListDictionaryInternal}
            HResult [int]:-2146233088
            HelpLink [string]:null
            InnerException [Exception]:{System.ArgumentException: Must specify valid information for parsing in the string.\r\n   at Newtonsoft.Json.Utilities.EnumUtils.ParseEnum(Type enumType, NamingStrategy namingStrategy, String value, Boolean disallowNumber) in /_/Src/Newtonsoft.Json/Utilities/EnumUtils.cs:line 285\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 958}
            LineNumber [int]:1
            LinePosition [int]:146
            Message [string]:"Error converting value \"\" to type 'AyaNova.Biz.AuthorizationRoles'. Path 'roles', line 1, position 146."
            Path [string]:"roles"
            Source [string]:"Newtonsoft.Json"
            StackTrace [string]:"   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 989\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 1032\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 2386"
            TargetSite [MethodBase]:{System.Object EnsureType(Newtonsoft.Json.JsonReader, System.Object, System.Globalization.CultureInfo, Newtonsoft.Json.Serialization.JsonContract, System.Type)}
            Static members
            Non-Public members
            Raw View
            Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.1\System.Reflection.Metadata.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
            Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.1\System.IO.MemoryMappedFiles.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
            modelState["roles"].Errors[0].Exception.Message
            "Error converting value \"\" to type 'AyaNova.Biz.AuthorizationRoles'. Path 'roles', line 1, position 146."
            modelState["roles"].Errors
            Count = 1
            [0]:{Microsoft.AspNetCore.Mvc.ModelBinding.ModelError */


            //original method missing details:
            // Error.Details.AddRange(modelState.Keys
            //         .SelectMany(key => modelState[key].Errors
            //         .Select(x => new ApiDetailError() { Code = ((int)ApiErrorCode.VALIDATION_FAILED).ToString(), Target = key, Message = x.ErrorMessage, Error=ApiErrorCode.VALIDATION_FAILED.ToString() })));

            //var vErrors = modelState.Keys.SelectMany(key => modelState[key].Errors);
            foreach (var key in modelState.Keys)
            {
                var vErrors = modelState[key].Errors;
                foreach (ModelError m in vErrors)
                {
                    string msg = "";
                    if (!string.IsNullOrWhiteSpace(m.ErrorMessage))
                    {
                        msg += m.ErrorMessage + ". ";
                    }
                    if (m.Exception != null && !string.IsNullOrWhiteSpace(m.Exception.Message))
                    {
                        msg += "Exception: " + m.Exception.Message;
                    }
                    //example this produces
                    //
                    Error.Details.Add(new ApiDetailError() { Target = key, Message = msg, Error = ((int)ApiErrorCode.VALIDATION_INVALID_VALUE).ToString() });
                }
            }


            log.LogDebug("BadRequest - Validation error");
        }


        //Business rule validation error response
        public ApiErrorResponse(List<ValidationError> errors)
        {
            Error = new ApiError(ApiErrorCode.VALIDATION_FAILED, ApiErrorCodeStockMessage.GetMessage(ApiErrorCode.VALIDATION_FAILED));
            Error.Details = new List<ApiDetailError>();
            foreach (ValidationError v in errors)
            {
                Error.Details.Add(new ApiDetailError() { Target = v.Target, Message = v.Message, Error = ((int)v.Code).ToString() });
            }
            log.LogDebug("BadRequest - Validation error");
        }



        // public void AddDetailError(ApiErrorCode apiCode, string target = null, string message = null)
        // {
        //     if (Error.Details == null)
        //     {
        //         Error.Details = new List<ApiDetailError>();
        //     }

        //     //try to get a stock message if nothing specified
        //     if (message == null)
        //     {
        //         message = ApiErrorCodeStockMessage.GetMessage(apiCode);
        //     }

        //     Error.Details.Add(new ApiDetailError() { Code = ((int)apiCode).ToString(), Target = target, Message = message });
        // }


    }//eoc


}//eons