using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using ProfitGroup.NotifyService.Core.Configs;
using ProfitGroup.NotifyService.Data.Core.Data.Entities;

namespace ProfitGroup.NotifyService.Services;

public class NotifyService : INotifyService
{
    private readonly RDnLDatabaseContext _context;
    private readonly ILogger<INotifyService> _logger;

    public NotifyService(RDnLDatabaseContext context, ILogger<INotifyService> logger)
    {
        _context = context;
        _logger = logger;
    }

    public Task<RndtSp?> GetSpecificationAsync(int specificationId, int specificationVersion)
    {
        var specification = _context.RndtSp.FirstOrDefaultAsync(c =>
            c.SP == specificationId && c.SP_VERSION == specificationVersion);
        return specification;
    }

    public async Task<RndtSpIc?> GetSpecificationInfoCardAsync(int specificationId, int specificationVersion,
        int locationId)
    {
        var bomHeaders = await _context.RndtBomHeader.Where(c =>
                c.SP == specificationId
                && c.SP_VERSION == specificationVersion
                && c.LO == locationId)
            .ToListAsync();

        if (bomHeaders.Count > 1)
        {
            return null;
        }

        var bomHeader = bomHeaders.FirstOrDefault();

        if (bomHeader == null)
        {
            return null;
        }

        var infoCards = _context.RndtSpIc.Where(c =>
                c.SP == specificationId
                && c.SP_VERSION == specificationVersion
                && c.ICNODE == bomHeader.ICNODE)
            .ToList();

        return infoCards.Count > 1 ? null : infoCards.FirstOrDefault();
    }

    public async Task<bool> UpdateSpecificationInfoCardAsync(RndtSpIc infoCard, Dictionary<string, string> updateParams)
    {
        if (infoCard is null)
        {
            throw new ArgumentException(nameof(infoCard));
        }

        var updateParametersObjects = new Dictionary<RndtSpIi, string>();

        foreach (var parameter in updateParams)
        {
            var param = await _context.RndtSpIi.FirstOrDefaultAsync(c =>
                c.SP == infoCard.SP
                && c.SP_VERSION == infoCard.SP_VERSION
                && c.ICNODE == infoCard.ICNODE
                && c.II_SHORT_DESC == parameter.Key);

            if (param == null)
            {
                _logger.LogError($"В инфокарте {infoCard.IC}, отсутствует инфополе \"{parameter.Key}\" ");
                return false;
            }

            updateParametersObjects.Add(param, parameter.Value);
        }

        foreach (var parameter in updateParametersObjects)
        {
            parameter.Key.IIVALUE = parameter.Value;
        }

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError($"Произошла ошибка при сохранения данных \n\n{ex.Message}\n\n{ex.StackTrace}");
            return false;
        }

        return true;
    }

    public async Task<RndtAu?> GetActiveSpecificationAttributeAsync(string attributeName)
    {
        return await _context.RndtAu.FirstOrDefaultAsync(c => c.SHORT_DESC == attributeName && c.ACTIVE == "1");
    }


    public async Task<bool> UpdateAttributeAsync(RndtSp specification, RndtAu actualSpecificationAttribute,
        string value)
    {
        var attribute =
            _context.RndtSpAu.FirstOrDefault(c =>
                c.SP == specification.SP && c.SP_VERSION == specification.SP_VERSION &&
                c.AU == actualSpecificationAttribute.AU);

        if (attribute == null)
        {
            attribute = new RndtSpAu
            {
                SP = specification.SP,
                SP_VERSION = specification.SP_VERSION,
                AU = actualSpecificationAttribute.AU,
                AU_VERSION = actualSpecificationAttribute.VERSION,
                AUSEQ = await _context.RndtSpAu.MaxAsync(c => c.AUSEQ) + 1,
                VALUE = value
            };

            _context.RndtSpAu.Add(attribute);
        }
        else
        {
            attribute.VALUE = value;
        }

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(
                $"Произошла ошибка при сохранения данных (Обновление атрибута AENNR у спецификации {specification.SP}, версия {specification.SP_VERSION}) \n\n{ex.Message}\n\n{ex.StackTrace}");
            return false;
        }

        return true;
    }

    public Task<RndtSc?> GetSampleAsync(int sampleId)
    {
        return _context.RndtSc.FirstOrDefaultAsync(c => c.SC == sampleId);
    }

    public Task<RndtScIc?> GetSampleInfoCardAsync(int sampleId)
    {
        var infoCard = _context.RndtScIc.FirstOrDefaultAsync(c =>
            c.SC == sampleId &&
            c.IC_SHORT_DESC == $"{Config.SampleInfoCardFieldName}");

        return infoCard;
    }

    public async Task<bool> UpdateSampleInfoCardAsync(RndtScIc infoCard, Dictionary<string, string> updateParams)
    {
        if (infoCard is null)
        {
            throw new ArgumentException("Параметр не может иметь значение null", nameof(infoCard));
        }

        var updateParametersObjects = new Dictionary<RndtScIi, string>();

        foreach (var parameter in updateParams)
        {
            var param = await _context.RndtScIi.FirstOrDefaultAsync(c =>
                c.SC == infoCard.SC &&
                c.II_SHORT_DESC == parameter.Key);

            if (param == null)
            {
                _logger.LogError($"В инфокарте {infoCard.IC}, отсутствует параметр \"{parameter.Key}\" ");
                return false;
            }

            updateParametersObjects.Add(param, parameter.Value);
        }

        foreach (var parameter in updateParametersObjects)
        {
            parameter.Key.IIVALUE = parameter.Value;
        }

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError($"Произошла ошибка при сохранениия данных \n\n{ex.Message}\n\n{ex.StackTrace}");
            return false;
        }

        return true;
    }

    public async Task<bool> SaveMethodLog(decimal methodId, decimal methodVersion, string statusCode, string statusText)
    {
        // var method =
        //     await _context.ProfITS4MtLog.FirstOrDefaultAsync(c => c.MT == methodId && c.MT_VERSION == methodVersion);

        // method.S4Status = statusCode;
        // method.S4StatusText = statusText;
        // method.S4StatusDate = DateTimeOffset.Now;
        // _context.SaveChangesAsync();
        //
        // if (method == null)
        //     return false;
        //
        // _context.Database.ExecuteSqlRaw(
        //     "UPDATE OpcenterRDnL.RndSuite.ProfITS4MtLog SET S4Status = N'@S4Status', S4StatusText = N'@S4StatusText', S4StatusDate = N'@S4StatusDate' WHERE GUID = N'@Guid'",
        //     new SqlParameter("user", statusCode),
        //     new SqlParameter("S4Status", statusCode),
        //     new SqlParameter("S4StatusText", statusText),
        //     new SqlParameter("S4StatusDate", DateTimeOffset.Now),
        //     new SqlParameter("Guid", method.GUID)
        //     );

        try
        {
            var parameters = new[]
            {
                new SqlParameter("@MethodId", methodId),
                new SqlParameter("@MethodVersion", methodVersion),
                new SqlParameter("@StatusCode", statusCode),
                new SqlParameter("@StatusText", statusText),
                new SqlParameter("@StatusDate", DateTimeOffset.Now)
            };

            await _context.Database.ExecuteSqlRawAsync(
                "UPDATE [RndSuite].[ProfITS4MtLog] " +
                "SET [S4Status] = @StatusCode, [S4StatusText] = @StatusText, [S4StatusDate] = @StatusDate " +
                "WHERE [MT] = @MethodId AND [MT_VERSION] = @MethodVersion", parameters);
            // await _context.SaveChangesAsync();
            return true;
        }
        catch (Exception exception)
        {
            _logger.LogError(0, exception,
                $"Не удалось сохранить данные метода в базу. Модель данных была обновлена, однако при сохранении возникла ошибка. {exception.Message}");
            return false;
        }

        return false;
    }

    public async Task<bool> SaveParameterLog(decimal parameterId, decimal parameterVersion, string statusCode,
        string statusText)
    {
        // var parameter =
        //     await _context.ProfITS4PrLog.FirstOrDefaultAsync(c =>
        //         c.PR == parameterId && c.PR_VERSION == parameterVersion);

        // if (parameter == null)
        //     return false;
        //
        // parameter.S4Status = statusCode;
        // parameter.S4StatusText = statusText;
        // parameter.S4StatusDate = DateTimeOffset.Now;

        try
        {
            var parameters = new[]{
                new SqlParameter("@ParameterId", parameterId),
                new SqlParameter("@ParameterVersion", parameterVersion),
                new SqlParameter("@StatusCode", statusCode),
                new SqlParameter("@StatusText", statusText),
                new SqlParameter("@StatusDate", DateTimeOffset.Now)
            };

            var affectedRows = await _context.Database.ExecuteSqlRawAsync(
                "UPDATE [RndSuite].[ProfITS4PrLog] " +
                "SET [S4Status] = @StatusCode, [S4StatusText] = @StatusText, [S4StatusDate] = @StatusDate " +
                "WHERE [PR] = @ParameterId AND [PR_VERSION] = @ParameterVersion", parameters);
            
            // await _context.SaveChangesAsync();
            return true;
        }
        catch (Exception exception)
        {
            _logger.LogError(0, exception,
                $"Не удалось сохранить данные параметра в базу. Модель данных была обновлена, однако при сохранении возникла ошибка. {exception.Message}");
            return false;
        }

        return false;
    }
}