using System.Security.Claims;

namespace Fast.Shared.Logic;

public class UserSynchronizationService
{
    private readonly MyDbContext _dbContext;
    private readonly ILogger<UserSynchronizationService> _logger;

    public UserSynchronizationService(MyDbContext dbContext, ILogger<UserSynchronizationService> logger)
    {
        _dbContext = dbContext;
        _logger = logger;
    }

    public async Task<int?> GetAppUserIdByEntraEmailAsync(ClaimsPrincipal principal)
    {
        if (principal == null)
        {
            _logger.LogError("GetAppUserIdByEntraEmailAsync: ClaimsPrincipal is null.");
            return null;
        }

        IEnumerable<string> entraEmails = GetEntraEmails(principal);

        string? entraObjectId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
        if (string.IsNullOrEmpty(entraObjectId))
        {
            _logger.LogWarning("Entra ID Object ID claim not found for authenticated user {Emails}.", string.Join(",", entraEmails));
        }

        if (!entraEmails.Any())
        {
            _logger.LogWarning("GetAppUserIdByEntraEmailAsync: Entra ID user ({EntraObjectId}) does not have an email claim. Cannot find matching user.", entraObjectId);
            return null;
        }

        var appUser = await FindAppUserByEntraEmailsAsync(principal);

        if (appUser != null)
        {
            _logger.LogInformation("Entra ID user '{EntraEmails}' (EntraObjectId: {EntraObjectId}) matched to existing app_user ID: {AppUserId}.", string.Join(",", entraEmails), entraObjectId, appUser.Id);
            return appUser.Id;
        }
        else
        {
            _logger.LogWarning("Entra ID user '{EntraEmails}' (EntraObjectId: {EntraObjectId}) authenticated but no matching app_user found by email in database. Access denied to application features.", string.Join(",", entraEmails), entraObjectId);
            return null;
        }
    }

    public static async Task<AppUser?> FindAppUserByEntraEmailsAsync(ClaimsPrincipal? principal)
    {
        if (principal == null)
            return null;

        var entraEmails = GetEntraEmails(principal);

        var normalizedEmails = entraEmails
            .Where(e => !string.IsNullOrWhiteSpace(e))
            .Select(e => e.ToUpperInvariant())
            .Distinct()
            .ToList();

        if (!normalizedEmails.Any())
            return null;

        var db = Main.CreateContext();
        var appUser = await db.AppUsers
            .AsNoTracking()
            .FirstOrDefaultAsync(u => normalizedEmails.Contains(u.NormalizedEmail ?? string.Empty));

        return appUser;
    }

    public static IEnumerable<string> GetEntraEmails(ClaimsPrincipal? principal)
    {
        if (principal == null)
        {
            return Enumerable.Empty<string>();
        }

        var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        void AddIfEmail(string? value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return;

            var v = value.Trim();
            if (v.Contains('@'))
                set.Add(v);
        }

        AddIfEmail(principal.FindFirst(ClaimTypes.Email)?.Value);
        AddIfEmail(principal.FindFirst("preferred_username")?.Value);
        AddIfEmail(principal.FindFirst(ClaimTypes.Upn)?.Value);
        AddIfEmail(principal.FindFirst("email")?.Value);

        foreach (var claim in principal.Claims)
        {
            AddIfEmail(claim.Value);
        }

        return set;
    }
}
