using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WorkFlowCheck.BL.Services.Interfaces; using WorkFlowCheck.DL; using WorkFlowCheck.DL.Entities; namespace WorkFlowCheck.BL.Services { public class NumberGeneratorService : INumberGeneratorService { private readonly DbContext _dbContext; private static readonly Dictionary _templateLocks = new(); public NumberGeneratorService(AppDbContext dbContext) { _dbContext = dbContext; } public async Task GenerateNextNumberAsync(int templateId, DateTime? baseDate = null) { var semaphore = GetOrCreateSemaphore(templateId); await semaphore.WaitAsync(); try { using var transaction = await _dbContext.Database.BeginTransactionAsync(); var template = await _dbContext.Set() .FirstOrDefaultAsync(t => t.Id == templateId); if (template == null) throw new InvalidOperationException($"Template with ID {templateId} not found."); var now = baseDate ?? DateTime.Now; var (year, month) = GetKeyDateParts(template, now); var templateDate = await _dbContext.Set() .FirstOrDefaultAsync(td => td.NumberGeneratorTemplateId == templateId && td.Year == year && td.Month == month); if (templateDate == null) { templateDate = new NumberGeneratorTemplateDate { NumberGeneratorTemplateId = template.Id, Year = year, Month = month, CurrentNumber = 0 }; _dbContext.Add(templateDate); } templateDate.CurrentNumber++; var formatted = templateDate.CurrentNumber.ToString(template.DigitFormat); var dynamicSuffix = BuildDynamicSuffix(template, now); var result = BuildFormattedNumber(template, formatted, dynamicSuffix); templateDate.LastGeneratedNumber = result; await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); return result; } catch { await _dbContext.Database.RollbackTransactionAsync(); throw; } finally { semaphore.Release(); } } public async Task GetSampleAsync(int templateId, DateTime? baseDate = null) { var template = await _dbContext.Set() .AsNoTracking() .FirstOrDefaultAsync(t => t.Id == templateId); if (template == null) throw new InvalidOperationException($"Template with ID {templateId} not found."); var now = baseDate ?? DateTime.Now; var formatted = 1.ToString(template.DigitFormat); var dynamicSuffix = BuildDynamicSuffix(template, now); return BuildFormattedNumber(template, formatted, dynamicSuffix); } private SemaphoreSlim GetOrCreateSemaphore(int templateId) { lock (_templateLocks) { if (!_templateLocks.ContainsKey(templateId)) _templateLocks[templateId] = new SemaphoreSlim(1, 1); return _templateLocks[templateId]; } } private static (int year, int? month) GetKeyDateParts(NumberGeneratorTemplate template, DateTime date) { int year = date.Year; int? month = template.GenerateType == GenerateType.YearAndMonth ? date.Month : null; return (year, month); } private static string BuildDynamicSuffix(NumberGeneratorTemplate template, DateTime date) { return template.GenerateType switch { GenerateType.OnlyYear => date.Year.ToString(), GenerateType.YearAndMonth => $"{date.Year}/{date.Month:D2}", _ => template.Suffix }; } private static string BuildFormattedNumber(NumberGeneratorTemplate template, string formattedNumber, string dynamicSuffix) { var sb = new StringBuilder(); if (!string.IsNullOrEmpty(template.Prefix)) sb.Append(template.Prefix); if (!string.IsNullOrEmpty(template.PrefixSeparator) && !string.IsNullOrEmpty(template.Prefix)) sb.Append(template.PrefixSeparator); sb.Append(formattedNumber); if (!string.IsNullOrEmpty(dynamicSuffix)) { if (!string.IsNullOrEmpty(template.SuffixSeparator)) sb.Append(template.SuffixSeparator); sb.Append(dynamicSuffix); } return sb.ToString(); } } }