using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Domain.Common.Rabbit.Messages.TemplateMatching;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.Job;
using PARR.Domain.Entities.Unit;
using PARR.Domain.Enums;
using PARR.TemplateMatcher.Services.Interfaces;

namespace PARR.TemplateMatcher.Services.Implementations;

internal class TemplateReuser : ITemplateReuser
{
    private readonly ILogger<TemplateReuser> logger;
    private readonly ITemplateRepository templateRepository;
    private readonly ITemplateNameNormalizer nameNormalizer;
    private readonly ITemplateMqPublisher mqPublisher;

    public TemplateReuser(
        ILogger<TemplateReuser> logger,
        ITemplateRepository templateRepository,
        ITemplateNameNormalizer nameNormalizer,
        ITemplateMqPublisher mqPublisher)
    {
        this.logger = logger;
        this.templateRepository = templateRepository;
        this.nameNormalizer = nameNormalizer;
        this.mqPublisher = mqPublisher;
    }

    public async Task<bool> TryReuseAsync(
        Job targetJob,
        Guid targetUnitId,
        Unit? targetUnit,
        int? index,
        List<UnitInTemplateMessage> unitsInTemplate,
        bool isActiveTemplate,
        bool isActiveSchedule,
        HistoryInitiator initiator,
        CancellationToken ct = default)
    {
        const int maxAttempts = 3;

        for (int attempt = 1; attempt <= maxAttempts; attempt++)
        {
            ct.ThrowIfCancellationRequested();

            try
            {
                // 1. Атомарный захват шаблона
                var templateId = await templateRepository.ReserveUnusedTemplateAsync(targetUnitId, initiator);
                if (templateId == null)
                {
                    logger.LogDebug("Нет доступных Unused-шаблонов (попытка {Attempt}).", attempt);
                    return false; // Шаблоны закончились
                }

                // Загружаем захваченный шаблон
                var template = await templateRepository.Get()
                    .AsNoTracking()
                    .Include(t => t.Unit)
                    .FirstOrDefaultAsync(t => t.Id == templateId, ct);

                if (template == null)
                {
                    logger.LogWarning("Зарезервированный шаблон {TemplateId} не найден.", templateId);
                    continue; // Попробовать еще раз
                }

                logger.LogInformation("Шаблон {TemplateId} захвачен для переиспользования.", templateId);

                // 2. Нормализация имени
                var templateForName = new Template
                {
                    Id = template.Id,
                    Name = template.Name,
                    JobId = targetJob.Id,
                    UnitId = targetUnitId,
                    Index = index,
                    Job = targetJob,
                    Unit = targetUnit,
                    UnitsInTemplate = unitsInTemplate.Select(m => new UnitsInTemplate { UnitId = m.UnitId, UnitFieldValueId = m.UnitFieldValueId }).ToList()
                };

                var expectedName = await nameNormalizer.GetNormalizedTemplateNameAsync(templateForName);

                // 3. Отправка команды в MQ
                var message = new TemplateUpdaterMessage
                {
                    TemplateId = template.Id,
                    JobId = targetJob.Id,
                    UnitId = targetUnitId,
                    Name = expectedName,
                    IsActiveTemplate = isActiveTemplate,
                    IsActiveSchedule = isActiveSchedule,
                    StatusTypeId = TemplateStatusTypeEnum.Used,
                    Initiator = initiator,
                    IsNew = true,
                    Index = index ?? template.Index,
                    UnitsInTemplate = unitsInTemplate
                };

                await mqPublisher.PublishUpdateAsync(message, ct);
                return true; // Успех
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Ошибка при переиспользовании (попытка {Attempt}).", attempt);
                if (attempt == maxAttempts) return false; // После 3 попыток сдаемся
                await Task.Delay(Random.Shared.Next(10, 50), ct);
            }
        }

        return false;
    }
}