using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorkFlowCheck.BL.Services.Interfaces;
using WorkFlowCheck.Common.DTO;
using WorkFlowCheck.DL;
using WorkFlowCheck.DL.Entities;

namespace WorkFlowCheck.BL.Services
{
    public class NumberGeneratorService : INumberGeneratorService
    {
        private readonly AppDbContext _dbContext;
        private static readonly Dictionary<int, SemaphoreSlim> _templateLocks = new();

        public NumberGeneratorService(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task<string> GenerateNextNumberAsync(int templateId, IDbContextTransaction transaction, DateTime? baseDate = null)
        {
            var semaphore = GetOrCreateSemaphore(templateId);
            await semaphore.WaitAsync();

            try
            {

                var template = await _dbContext.Set<NumberGeneratorTemplate>()
                    .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<NumberGeneratorTemplateDate>()
                    .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();
                return result;
            }
            catch
            {
                throw;
            }
            finally
            {
                semaphore.Release();
            }
        }
        public async Task<string> GetSampleAsync(int templateId, DateTime? baseDate = null)
        {
            var template = await _dbContext.Set<NumberGeneratorTemplate>()
                .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);
        }
        public async Task<List<NumberGeneratorTemplateDTO>> GetAllNumbergeneratorsAsync()
        {
            var retVal = new List<NumberGeneratorTemplateDTO>();
            try
            {
                var numbergenerators = await _dbContext.NumberGeneratorTemplates.ToListAsync();
                foreach (var numbergenerator in numbergenerators)
                {
                    var numbergeneratortemplateDTO = new NumberGeneratorTemplateDTO();
                    numbergeneratortemplateDTO.Id = numbergenerator.Id;
                    numbergeneratortemplateDTO.ShortName = numbergenerator.ShortName;
                    numbergeneratortemplateDTO.Sample = await this.GetSampleAsync(numbergenerator.Id, DateTime.Now);
                    retVal.Add(numbergeneratortemplateDTO);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }


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


}
