using Indotalent.Data.Entities;
using Indotalent.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
using System.Collections.Concurrent;

namespace Indotalent.Infrastructure.AutoNumberGenerator;

public class AutoNumberGeneratorService
{
    private readonly AppDbContext _context;
    private static readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new();

    public AutoNumberGeneratorService(AppDbContext context)
    {
        _context = context;
    }

    public async Task<string> GenerateNextNumberAsync(
        string entityName,
        string prefixTemplate,
        string? suffixTemplate = null,
        int paddingLength = 4,
        bool useYear = true,
        bool useMonth = false,
        CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        int? year = useYear ? now.Year : null;
        int? month = useMonth ? now.Month : null;

        var lockKey = $"{entityName}_{year}_{month}";
        var semaphore = _locks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));

        await semaphore.WaitAsync(cancellationToken);
        try
        {
            var sequence = await _context.AutoNumberSequence
                .FirstOrDefaultAsync(ns =>
                    ns.EntityName == entityName &&
                    ns.Year == year &&
                    ns.Month == month,
                    cancellationToken);

            if (sequence == null)
            {
                sequence = new AutoNumberSequence
                {
                    EntityName = entityName,
                    Year = year,
                    Month = month,
                    CurrentSequence = 0,
                    PrefixTemplate = prefixTemplate,
                    SuffixTemplate = suffixTemplate,
                    PaddingLength = paddingLength
                };
                _context.AutoNumberSequence.Add(sequence);
            }

            sequence.CurrentSequence = (sequence.CurrentSequence ?? 0) + 1;

            var formattedNumber = BuildFormattedNumber(sequence, now);

            await _context.SaveChangesAsync(cancellationToken);

            return formattedNumber;
        }
        finally
        {
            semaphore.Release();
            if (semaphore.CurrentCount == 1) _locks.TryRemove(lockKey, out _);
        }
    }

    private string BuildFormattedNumber(AutoNumberSequence sequence, DateTime now)
    {
        string ProcessTemplate(string? template)
        {
            if (string.IsNullOrEmpty(template)) return "";
            return template
                .Replace("{EntityName}", sequence.EntityName)
                .Replace("{Year}", now.Year.ToString())
                .Replace("{Year2}", now.ToString("yy"))
                .Replace("{Month}", now.Month.ToString("D2"));
        }

        var prefix = ProcessTemplate(sequence.PrefixTemplate);
        var suffix = ProcessTemplate(sequence.SuffixTemplate);

        var padding = sequence.PaddingLength ?? 4;
        var paddedSequence = (sequence.CurrentSequence ?? 1).ToString($"D{padding}");

        return $"{prefix}{paddedSequence}{suffix}";
    }
}