using NLog;
using ProfitGroup.Rdnl.Extensions;
using ProfitGroup.SampleService.Models;
using RnD.API;
using RnD.BusinessLayer.EF.Interfaces;
using RnD.BusinessLayer.Interfaces.Interspec.Model;
using RnD.Model;
using RnD.Model.EF;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using ProfitGroup.SampleService.Data.Database.Entities;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using NLog.Config;
using System.IO;

namespace ProfitGroup.SampleService.Core.Services
{
    public class KsssInfoService : IKSSSInfoService
    {
        private readonly RnDConnection _databaseContext;
        private readonly API _api;

        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();
            }
        }

        public KsssInfoService(RDnLManager manager, API api)
        {
            _api = api;
            _databaseContext = ((ISession<RnDConnection>)manager.EventAwareObjectFactory.FactorySession).Context;
        }

        public async Task<ISpecification> FindActiveSpecificationById(decimal specificationId)
        {

            var activeSpecification = await _databaseContext.RndvSp.FirstAsync(c =>
                c.SP == specificationId
                && c.ACTIVE_STR == "1"
                );

            return _api.Specification.GetSpecification(activeSpecification.SP, activeSpecification.SP_VERSION);
        }

        public Task<List<RndvSpIi>> GetInfoFieldsByKSSSCode(string ksssCode)
        {
            // ToDo: Вынести константу KD_KSSS в конфиг + Проверить различные сценарии, когда обновляется Frame, создается ли новая версия инфополя?
            return _databaseContext.RndvSpIi.Where(c =>
                    c.II_SHORT_DESC_MAIN == "KD_KSSS"
                    && c.IIVALUE_MAIN == ksssCode
            //c.ACTIVE_STR == "1"
            ).ToListAsync();
        }

        public string GetPackageNameFromSpecificationKSSS(ISpecification specification)
        {
            
            // Если 30 фрейм, Id Frame: PackagedRM RM, то мы ничего не ищем.
            if (specification.FrameId == 30)
            {
                return string.Empty;
            }
            
            var kssInfoGroup =
                specification.InfoCards.FirstOrDefault(c =>
                    c.ShortDescription == "KSSS_Data"); //ToDo: Вынести константу в конфиг

            var infoField =
                kssInfoGroup?.InfoFields.FirstOrDefault(c =>
                    c.ShortDescription == "KD_Pack"); //ToDo: Вынести константу в конфиг

            return infoField != null ? infoField.InfoFieldValue : string.Empty;
        }

        public string GetProductCodeFromSpecificationKSSS(ISpecification specification, out decimal? frameId)
        {
            var productCode = string.Empty;

            frameId = specification.FrameId;

            switch (specification.FrameId)
            {
                case 17: // Id Frame: PackagedProducts (SKU)
                    productCode = GetProductCodeFromPackagedProductFrame(specification);
                    break;
                case 30: // Id Frame: PackagedRM RM
                    productCode = GetProductCodeFromPackagedRmFrame(specification);

                    break;
                default:

                    frameId = null;
                    Logger.Error(
                        $"Спецификация {specification.ID}:{specification.Version} имеет неверный Frame, отличный от 17(PackagedProducts) и 30(PackagedRM)");
                    break;
            }

            return productCode;
        }

        private static string GetProductCodeFromPackagedRmFrame(ISpecification specification)
        {
            // Id Frame: PackagedRM
            var kssInfoGroup =
                specification.InfoCards.FirstOrDefault(c =>
                    c.ShortDescription == "KSSS_RM_SKU"); //ToDo: Вынести константу в конфиг

            var infoField =
                kssInfoGroup?.InfoFields.FirstOrDefault(c =>
                    c.ShortDescription == "KSSS_RM_ID"); //ToDo: Вынести константу в конфиг

            return infoField?.InfoFieldValue;
        }

        private static string GetProductCodeFromPackagedProductFrame(ISpecification specification)
        {
            // Id Frame: PackagedProducts

            var kssInfoGroup =
                specification.InfoCards.FirstOrDefault(c =>
                    c.ShortDescription == "KSSS_SKU"); //ToDo: Вынести константу в конфиг

            var infoField =
                kssInfoGroup?.InfoFields.FirstOrDefault(c =>
                    c.ShortDescription == "SKU_KsssProductCode"); //ToDo: Вынести константу в конфиг

            return infoField?.InfoFieldValue;
        }

        [Obsolete("Неактуально, параметры локация приходит в запросе")]
        public IEnumerable<string> GetSpecificationLocationName(ISpecification specification)
        {
            if (specification.Material is Material material && material.IBaseCustomizationObject_InternalDTO is MaterialDTO materialDto &&
                materialDto.Plants.Count() == 1)
            {
                return
                    materialDto.Plants.Select(c =>
                        c.ShortDescription);
            }

            return Enumerable.Empty<string>();
        }

        public async Task<S4InspectionTypeMap> MapOperationControl(decimal locationId, decimal? frameId, string inspectonType)
        {
            //var sql = "SELECT * FROM [ProfIT].[S4InspectionTypeMap] " +
            //               "WHERE [ProfIT].[S4InspectionTypeMap].[LO] = @location " +
            //               "AND [ProfIT].[S4InspectionTypeMap].[InspectonType] = '@inspectonType'";

            //var sqlParameters = new List<SqlParameter>
            //{
            //    new SqlParameter("@location", locationId),
            //    new SqlParameter("@inspectonType", inspectonType)
            //};

            //if (frameId != null)
            //{
            //    sql += " AND ([FR] = @frame OR [FR] IS NULL)";
            //    sqlParameters.Add(new SqlParameter("@frame", frameId));
            //}

            //var s4InspectionTypeMap = await _databaseContext.Database
            //    .SqlQuery<S4InspectionTypeMap?>(sql, sqlParameters.ToArray()).FirstOrDefaultAsync();



            //foreach (var item in sqlParameters)
            //{
            //    sql = sql.Replace(item.ParameterName, item.Value.ToString());
            //}

            //Console.WriteLine(sql);

            var sql = @"
                SELECT *
                FROM [RndSuite].[S4InspectionTypeMap]
                WHERE [RndSuite].[S4InspectionTypeMap].[LO] = @location
                    AND [RndSuite].[S4InspectionTypeMap].[InspectonType] = @inspectonType";

            var sqlParameters = new List<SqlParameter>
            {
                new SqlParameter("@location", locationId.ToString()),
                new SqlParameter("@inspectonType", inspectonType)
            };

            if (frameId != null)
            {
                sql += " AND ([FR] = @frame OR [FR] IS NULL)";
                sqlParameters.Add(new SqlParameter("@frame", frameId));
            }

            var s4InspectionTypeMap = await _databaseContext.Database
                .SqlQuery<S4InspectionTypeMap>(sql, sqlParameters.ToArray()).FirstOrDefaultAsync();

            return s4InspectionTypeMap;
        }


        public Task<SiemtControlTypes> GetControlTypeByCode(string controlTypeCode)
        {
            var s4InspectionTypeMap =
                _databaseContext.Database.SqlQuery<SiemtControlTypes>(
                    $"SELECT * FROM [RndSuite].[SiemtControlTypes] WHERE [RndSuite].[SiemtControlTypes].[CODE] = @controlTypeCode",
                    new SqlParameter("@controlTypeCode", controlTypeCode));

            return s4InspectionTypeMap.FirstOrDefaultAsync();
        }

        public RndvSt GetSampleType(string productCode, string operationControlName, string package, string stage,
            decimal plantId)
        {
            // SELECT TOP 501 st.SHORT_DESC, stAu.AU, stAu.VALUE
            //     FROM OpcenterRDnL.RndSuite.RndtSt st
            // INNER JOIN OpcenterRDnL.RndSuite.RndtStAu stAu ON stAu.ST = st.ST
            // WHERE st.ACTIVE = 1
            // AND st.SHORT_DESC LIKE N'%FP768%'
            // AND (stAu.AU = 21 OR stAu.AU = 11)
            // AND st.VERSION = stAu.VERSION 
            // AND (stAu.VALUE LIKE N'%Приёмочный контроль%' OR stAu.VALUE = 'FP768')

            // Исправить дубликат запроса
            var sampleTypes = _databaseContext.RndvSt.Join(_databaseContext.RndvStAu,
                sampleType => sampleType.ST,
                sampleTypeDetails => sampleTypeDetails.ST,
                (sampleType, sampleTypeDetails)
                    => new { SampleType = sampleType, SampleTypeAu = sampleTypeDetails }
            ).Where(c => c.SampleType.VERSION == c.SampleTypeAu.VERSION
                         && c.SampleType.ACTIVE_STR == "1"
                         && c.SampleType.SHORT_DESC_MAIN.Contains(productCode)
                         && (c.SampleTypeAu.AU == 21 || c.SampleTypeAu.AU == 11) // Type of testing
                         && (c.SampleTypeAu.VALUE_MAIN.Contains(operationControlName) || c.SampleTypeAu.VALUE_MAIN == productCode)
            ).ToList();

            if (!string.IsNullOrEmpty(package))
            {
                sampleTypes = _databaseContext.RndvSt.Join(_databaseContext.RndvStAu,
                    sampleType => sampleType.ST,
                    sampleTypeDetails => sampleTypeDetails.ST,
                    (sampleType, sampleTypeDetails)
                        => new { SampleType = sampleType, SampleTypeAu = sampleTypeDetails }
                ).Where(c => c.SampleType.VERSION == c.SampleTypeAu.VERSION
                             && c.SampleType.ACTIVE_STR == "1"
                             && c.SampleType.SHORT_DESC_MAIN.Contains(productCode)
                             && (c.SampleTypeAu.AU == 21 || c.SampleTypeAu.AU == 11 || c.SampleTypeAu.AU == 43) // Type of testing
                             && (c.SampleTypeAu.VALUE_MAIN.Contains(operationControlName) || c.SampleTypeAu.VALUE_MAIN == productCode || c.SampleTypeAu.VALUE_MAIN == package)
                ).ToList();   
            }
            
            if (!string.IsNullOrEmpty(stage))
            {
                sampleTypes = _databaseContext.RndvSt.Join(_databaseContext.RndvStAu,
                    sampleType => sampleType.ST,
                    sampleTypeDetails => sampleTypeDetails.ST,
                    (sampleType, sampleTypeDetails)
                        => new { SampleType = sampleType, SampleTypeAu = sampleTypeDetails }
                ).Where(c => c.SampleType.VERSION == c.SampleTypeAu.VERSION
                             && c.SampleType.ACTIVE_STR == "1"
                             && c.SampleType.SHORT_DESC_MAIN.Contains(productCode)
                             && (c.SampleTypeAu.AU == 21 || c.SampleTypeAu.AU == 11 || c.SampleTypeAu.AU == 38) // Type of testing
                             && (c.SampleTypeAu.VALUE_MAIN.Contains(operationControlName) || c.SampleTypeAu.VALUE_MAIN == productCode || c.SampleTypeAu.VALUE_MAIN == stage)
                ).ToList();   
            }

            // Отбираем объекты, согласно локации
            sampleTypes = sampleTypes
                .Where(c => c.SampleType.RndvStLo.FirstOrDefault(g => g.LO == plantId) != null)
                .ToList();
            
            // Проверяем, что было совпадение по двум параметрам (operationControlName и productCode)
            // Или трём, если был передан упаковка или стадия
            var paramsForSearchCount = string.IsNullOrEmpty(stage) && string.IsNullOrEmpty(package) ? 2 : 3;

            var groupingSampleTypes = 
                sampleTypes.GroupBy(c => c.SampleType.SHORT_DESC)
                    .Where(c => c.Count() == paramsForSearchCount)
                    .Select(c => c.FirstOrDefault())
                    .ToList();

            if (groupingSampleTypes.Count == 1) 
                return groupingSampleTypes.Single().SampleType;
            
            Logger.Error(
                $"В результате получения SampleType было получено несколько возможных вариантов: {string.Join(",", sampleTypes.Select(c => $"{c.SampleType.ST} / {c.SampleType.SHORT_DESC_MAIN}"))}");

            return null;
        }

        public async Task<RndvLo> GetPlantByName(string plant)
        {
            return await _databaseContext.RndvLo.FirstOrDefaultAsync(c => c.SHORT_DESC_MAIN == plant);
        }
    }
}