using MealPlan.Api.Data;
using Microsoft.EntityFrameworkCore;

namespace MealPlan.Api.Services.Generators;

public interface ICatalogMatchingService
{
    Task<int?> ResolveCatalogItemIdAsync(string nameOrCatalogName, CancellationToken ct = default);
}

public class CatalogMatchingService : ICatalogMatchingService
{
    private readonly AppDbContext _db;
    private List<CatalogEntry>? _cache;

    public CatalogMatchingService(AppDbContext db) => _db = db;

    public async Task<int?> ResolveCatalogItemIdAsync(string nameOrCatalogName, CancellationToken ct = default)
    {
        if (string.IsNullOrWhiteSpace(nameOrCatalogName)) return null;

        var cache = await GetCacheAsync(ct);
        var key = Normalize(nameOrCatalogName);

        var exact = cache.FirstOrDefault(c =>
            c.NameKey == key || c.NamePlKey == key || c.CatalogNameKey == key);
        if (exact is not null) return exact.Id;

        return cache
            .Where(c => key.Contains(c.NameKey, StringComparison.Ordinal) ||
                        c.NameKey.Contains(key, StringComparison.Ordinal) ||
                        (!string.IsNullOrEmpty(c.NamePlKey) &&
                         (key.Contains(c.NamePlKey, StringComparison.Ordinal) ||
                          c.NamePlKey.Contains(key, StringComparison.Ordinal))))
            .OrderBy(c => Math.Abs(c.NameKey.Length - key.Length))
            .FirstOrDefault()
            ?.Id;
    }

    private async Task<List<CatalogEntry>> GetCacheAsync(CancellationToken ct)
    {
        if (_cache is not null) return _cache;

        _cache = await _db.IngredientNutritionCatalog.AsNoTracking()
            .Where(i => i.IsActive)
            .Select(i => new CatalogEntry(i.Id, i.Name, i.NamePl))
            .ToListAsync(ct);

        return _cache;
    }

    private static string Normalize(string value) => value.Trim().ToLowerInvariant();

    private sealed record CatalogEntry(int Id, string Name, string? NamePl)
    {
        public string NameKey { get; } = Name.Trim().ToLowerInvariant();
        public string NamePlKey { get; } = NamePl?.Trim().ToLowerInvariant() ?? string.Empty;
        public string CatalogNameKey { get; } = Name.Trim().ToLowerInvariant();
    }
}
