using CashLineERP.Application.Localization;
using CashLineERP.SharedKernel.Localization;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories;

public sealed class LocalizationResourceRepository : ILocalizationResourceRepository
{
    private static readonly SupportedCultureView[] SupportedCultures =
    [
        new(
            LocalizationCultureCode.EnglishUnitedStates,
            "English (United States)",
            "English",
            LocalizationDirectionCode.LeftToRight,
            true),
        new(
            LocalizationCultureCode.ArabicEgypt,
            "Arabic (Egypt)",
            "العربية (مصر)",
            LocalizationDirectionCode.RightToLeft,
            false)
    ];

    private readonly CashLineErpDbContext _dbContext;

    public LocalizationResourceRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IReadOnlyCollection<SupportedCultureView> GetSupportedCultures() => SupportedCultures;

    public LocalizationResourcePackView GetResourcePack(string resourceSetCode, string? cultureCode = null)
    {
        if (string.IsNullOrWhiteSpace(resourceSetCode))
        {
            throw new InvalidOperationException("Resource set code is required.");
        }

        var requestedCultureCode = LocalizationCultureCode.Normalize(cultureCode);
        var activeResources = _dbContext.LocalizationResources
            .AsNoTracking()
            .Where(resource =>
                resource.IsActive
                && resource.ResourceSetCode == resourceSetCode)
            .ToList();

        if (activeResources.Count == 0)
        {
            throw new InvalidOperationException($"Localization resource set '{resourceSetCode}' does not exist.");
        }

        var resolvedCultureCode = ResolveCultureCode(activeResources, requestedCultureCode);

        var resolvedResources = activeResources
            .Where(resource => string.Equals(resource.CultureCode, resolvedCultureCode, StringComparison.OrdinalIgnoreCase))
            .OrderBy(resource => resource.SortOrder)
            .ThenBy(resource => resource.ResourceKey, StringComparer.OrdinalIgnoreCase)
            .ToArray();

        var textDirectionCode = resolvedResources.First().TextDirectionCode;

        return new LocalizationResourcePackView(
            resourceSetCode,
            requestedCultureCode,
            resolvedCultureCode,
            textDirectionCode,
            resolvedResources.ToDictionary(
                resource => resource.ResourceKey,
                resource => resource.ResourceValue,
                StringComparer.OrdinalIgnoreCase));
    }

    private static string ResolveCultureCode(
        IReadOnlyCollection<Domain.Localization.LocalizationResource> activeResources,
        string requestedCultureCode)
    {
        var exactMatch = activeResources
            .Select(resource => resource.CultureCode)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .FirstOrDefault(cultureCode => string.Equals(cultureCode, requestedCultureCode, StringComparison.OrdinalIgnoreCase));

        if (exactMatch is not null)
        {
            return exactMatch;
        }

        var languageCode = requestedCultureCode.Split('-', StringSplitOptions.RemoveEmptyEntries)[0];
        var languageMatch = activeResources
            .Select(resource => resource.CultureCode)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .FirstOrDefault(cultureCode =>
                string.Equals(cultureCode, languageCode, StringComparison.OrdinalIgnoreCase)
                || cultureCode.StartsWith($"{languageCode}-", StringComparison.OrdinalIgnoreCase));

        if (languageMatch is not null)
        {
            return languageMatch;
        }

        var defaultMatch = activeResources
            .Select(resource => resource.CultureCode)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .FirstOrDefault(cultureCode => string.Equals(cultureCode, LocalizationCultureCode.EnglishUnitedStates, StringComparison.OrdinalIgnoreCase));

        return defaultMatch ?? activeResources.Select(resource => resource.CultureCode).First();
    }
}
