using Newtonsoft.Json;
using NLog;
using NLog.Config;
using ProfitGroup.Rdnl.Extensions;
using ProfitGroup.SampleService.Core.Services;
using ProfitGroup.SampleService.Models;
using RnD.API;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Web.Http;
using System.Xml.Serialization;
using RnD.Interspec.EF.Specification;
using ILogger = NLog.ILogger;
using ISampleService = ProfitGroup.SampleService.Core.Services.ISampleService;

namespace ProfitGroup.SampleService.Controllers
{
    [Authorize]
    public class SampleController : ApiController
    {
        private ILogger Logger
        {
            get
            {
                var loggerConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "nlog.config");
                LogManager.Setup().LoadConfigurationFromFile(loggerConfig);
                LogManager.Configuration = new XmlLoggingConfiguration(loggerConfig);

                return LogManager.GetCurrentClassLogger();
            }
        }

        private RDnLManager _manager;
        private IKSSSInfoService _ksssInfoService;
        private ISampleService _sampleService;
        private readonly API _api;

        public SampleController(
            RDnLManager manager,
            IKSSSInfoService ksssInfoService,
            ISampleService sampleService,
            API api)
        {
            var loggerConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "nlog.config");
            LogManager.Setup().LoadConfigurationFromFile(loggerConfig);
            LogManager.Configuration = new XmlLoggingConfiguration(loggerConfig);

            _manager = manager;
            _ksssInfoService = ksssInfoService;
            _sampleService = sampleService;
            _api = api;

        }

        [HttpPost]
        public async Task<IHttpActionResult> Create()
        {
            PushLogSeparator();

            #region 0. Получение данных из тела запроса

            MT_InspLot request = null;

            var content = await ActionContext.Request.Content.ReadAsStringAsync();

            Logger.Log(LogLevel.Info, "Тело запроса:" + content);

            if (string.IsNullOrEmpty(content))
            {
                const string message = "В теле запроса отсутствуют данные";
                Logger.Error(message);
                return BadRequest(message);
            }

            try
            {
                var serializer = new XmlSerializer(typeof(MT_InspLot));
                using (var reader = new StringReader(content))
                request = (MT_InspLot)serializer.Deserialize(reader);
            }
            catch (Exception ex)
            {
                string message = "Не удалось считать данные из полученного запроса" + ex.Message;
                Logger.Error(ex, message);
                return BadRequest(message);
            }

            #endregion
            
            try
            {
                #region 1. Проверка данных

                #region 1.1 Проверка на то, что пришли необходимые данные

                if (string.IsNullOrEmpty(request.Document.Plant))
                {
                    const string message = "Отсутствует параметр Plant (Площадка)";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                if (string.IsNullOrEmpty(request.Document.InspectonType))
                {
                    const string message = "Отсутствует параметр ControlType (Вид контроля)";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                if (string.IsNullOrEmpty(request.Document.Material))
                {
                    const string message = "Отсутствует параметр ksss (Код KSSS)";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                var customSampleCreateDto = new CustomSampleCreateDto
                {
                    PlantName = request.Document.Plant
                };

                #endregion

                #region 1.2 Проверки на верность полученных данных

                var plant = await _ksssInfoService.GetPlantByName(request.Document.Plant);

                if (plant == null)
                {
                    var message = $"Не удалось найти площадку с наименованием: {request.Document.Plant}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                #endregion

                #endregion

                #region 2. Поиск инфополя, со значением, соответствующему полученному KSSS коду

                var ksssCodeInfoFields = await _ksssInfoService.GetInfoFieldsByKSSSCode(request.Document.Material);

                var groupedInfoFields = ksssCodeInfoFields.GroupBy(c => c.SP);

                if (groupedInfoFields.Count() == 0)
                {
                    var message = $"Отсутствует спецификация с кодом KSSS: {request.Document.Material}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                if (groupedInfoFields.Count() > 1)
                {
                    var message = $"Нашлось более одного инфополя с кодом KSSS: {request.Document.Material}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                #endregion

                #region 3. Получение спецификации

                var specification = await _ksssInfoService.FindActiveSpecificationById(ksssCodeInfoFields[0].SP);

                if (specification == null)
                {
                    var message = $"Не удалось определить спецификацию по инфополю: {ksssCodeInfoFields[0].IIVALUE}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                #endregion

                #region 4. Получение материала из спецификации

                customSampleCreateDto.ProductCode =
                    _ksssInfoService.GetProductCodeFromSpecificationKSSS(specification, out decimal? frameId);

                if (string.IsNullOrEmpty(customSampleCreateDto.ProductCode))
                {
                    var message = $"Не удалось определить тип продукта (Материал)";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                #endregion

                #region 5. Маппинг вида контроля в формате OpcenterRdnL

                var mappedControl =
                    await _ksssInfoService.MapOperationControl(plant.LO, frameId, request.Document.InspectonType);

                if (mappedControl == null)
                {
                    var message =
                        $"Не удалось определить вид контроля: LO: {plant.LO}, InspectonType: {request.Document.InspectonType}, FrameId: {frameId}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                var controlType = await _ksssInfoService.GetControlTypeByCode(mappedControl.ControlTypeCode);

                if (controlType == null)
                {
                    var message = $"В таблице видов контроля отсутствует вид с кодом: {mappedControl.ControlTypeCode}";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                customSampleCreateDto.OperationControlName = controlType.NAME_RU;

                #endregion

                #region 6. Определение упаковки (Вида тары), у объектов, которые не имеют стадии

                //  customSampleCreateDto.OperationControl != OperationControl.InputControl
                // Таблица: OpcenterRDnL.RndSuite.SiemtControlTypes
                // Если не входной контроль
                if (controlType.CODE != "IC" && string.IsNullOrEmpty(mappedControl.Stage))
                {
                    //Обработка частного случая //ToDo: Вынести логику обработки частного случая
                    if (request.Document.InspectonType == "L010")
                    {
                        if (decimal.TryParse(request.Document.InspLotSize, out var lotSize))
                        {
                            if (request.Document.InspectonType == "L010")
                            {
                                customSampleCreateDto.Package = DetectPackageServiceByRangeValue.Detect("А/Ц", "В/Ц", 50_000, lotSize);
                            }

                            //customSampleCreateDto.Package = request.Document.InspectonType switch
                            //{
                            //    // ToDo: Убедиться что >= указано верно в пользу В/Ц
                            //    "L010" => DetectPackageServiceByRangeValue.Detect("А/Ц", "В/Ц", 50_000, lotSize),
                            //    _ => customSampleCreateDto.Package
                            //};
                        }
                        else
                        {
                            
                            var message = $@"Для вида контроля S4 ""{request.Document.InspectonType}"" не удалось определить упаковку, не удалось получить числовое значение InspLotSize";
                            Logger.Error(message);
                            return BadRequest(message);
                        }
                    }
                    else
                    {
                        customSampleCreateDto.Package =
                            _ksssInfoService.GetPackageNameFromSpecificationKSSS(specification);
                    }
                    
                    if (string.IsNullOrEmpty(customSampleCreateDto.Package) && specification.FrameId != 30)
                    {
                        var message = $"У инфокарты спецификации не заполнено инфополе: KD_Pack";
                        Logger.Error(message);
                        return BadRequest(message);
                    }
                }

                #endregion

                #region 7. Определение SampleType

                customSampleCreateDto.SampleType = _ksssInfoService.GetSampleType(
                    customSampleCreateDto.ProductCode,
                    customSampleCreateDto.OperationControlName,
                    customSampleCreateDto.Package,
                    mappedControl.Stage,
                    plant.LO
                        );

                if (customSampleCreateDto.SampleType == null)
                {
                    var message =
                        $"Не удалось определить SampleType, ProductCode: {customSampleCreateDto.ProductCode}, OperationControlName: {customSampleCreateDto.OperationControlName}, Упаковка: \"{customSampleCreateDto.Package}\", Стадия: \"{mappedControl.Stage}\"";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                #endregion

                #region 8. Определение ключей создания пробы + заполнение

                var taskFilterDtos = _sampleService
                    .GetTaskKeys(request.Document.Plant, 
                        customSampleCreateDto.OperationControlName,
                        customSampleCreateDto.Package ?? mappedControl.Stage
                        )
                    .ToList();

                if (taskFilterDtos.Count == 0)
                {
                    var message =
                        $"Не было задано ни одного ключа создания объекта, либо не удалось определить контекстные ключи";
                    Logger.Error(message);
                    return BadRequest(message);
                }

                // Получаем только незаполненные параметры
                foreach (var taskFilter in taskFilterDtos.Where(taskFilter => string.IsNullOrEmpty(taskFilter.Value)))
                {
                    switch (taskFilter.Field)
                    {
                        case "SHORT_DESC@RndvSt":
                            taskFilter.Value = customSampleCreateDto.SampleType.SHORT_DESC;
                                break;
                        case "43@RndvStAu":
                            taskFilter.Value = customSampleCreateDto.Package.Replace(",", ".");
                                break;
                        case "38@RndvStAu":
                            taskFilter.Value = mappedControl.Stage;
                                break;

                        default:
                            break;
                    }

                    //taskFilter.Value = taskFilter.Field switch
                    //{
                    //    // Краткое описание
                    //    "SHORT_DESC@RndvSt" => customSampleCreateDto.SampleType.SHORT_DESC,

                    //    // Упаковка
                    //    "43@RndvStAu" => customSampleCreateDto.Package.Replace(",", "."),

                    //    // Стадия
                    //    "38@RndvStAu" => mappedControl.Stage,

                    //    // Возвращаем значение
                    //    _ => taskFilter.Value
                    //};
                }

                #endregion

                #region 9. Создание пробы

                var sampleFullDto = _manager.EventAwareObjectFactory.Sample.Create(
                    customSampleCreateDto.SampleType.ST,
                    customSampleCreateDto.SampleType.VERSION,
                    taskFilterDtos);

                _manager.EventAwareObjectFactory.SaveChanges();

                #endregion

                #region 10. Заполнение инфокарт

                var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");

                var appSettings = JsonConvert.DeserializeObject<Appsettings>(File.ReadAllText(path));

                foreach (var card in appSettings.InfoCardSettings)
                {
                    var parameter = GetPropertyValue(request.Document, card.Name);

                    if (parameter != null)
                    {
                        card.Value = parameter.ToString();
                    }
                    else
                    {
                        Logger.Error(
                            $"Значение не было записано на инфокарту {card.Name}. Полученный запрос не имеет соответствующего поля/значения для записи");
                    }
                }

                _sampleService.FillSampleInfoCards(Convert.ToDecimal(sampleFullDto.ID), appSettings.InfoCardSettings);

                #endregion

                #region 11. Отправка Confirmation

                await _sampleService.SendConfirmation(request, sampleFullDto);

                #endregion

                #region 12. Возврат ответа на запрос

                var requestMessage =
                    $"Запрос был обработан, позже будет выслано отдельное подтверждение с Message Guid: {request.GuigMsgOutb}";

                Logger.Log(LogLevel.Info, requestMessage);

                return Ok(new
                {
                    Message = requestMessage
                });

                #endregion
            }
            catch (Exception ex)
            {
                Logger.Error(ex.Message + "\n\n" + ex.StackTrace);
                return InternalServerError(ex);
            }finally
            {
                PushLogSeparator(10);
            }
        }

        private void PushLogSeparator(int newLineCount = 0)
        {
            Logger.Info($"{new string('=', 100)}{new string('\n', newLineCount)}");
        }

        private object GetPropertyValue(object objectDocument, string propertyName)
        {
            Type type = objectDocument.GetType();
            PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);

            if (propertyInfo != null)
                return propertyInfo.GetValue(objectDocument, null);
            else
                return null;
        }
    }
}