using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Prospect.Server.Api.Config;
using Prospect.Server.Api.Services.Database.Models;

namespace Prospect.Server.Api.Services.Database;

public class DbUserService : BaseDbService<PlayFabUser>
{
    public DbUserService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabUser))
    {
    }

    public async Task<PlayFabUser> FindAsync(PlayFabUserAuthType type, string key)
    {
        return await Collection.Find(user => user.Auth.Any(auth =>
            auth.Type == type &&
            auth.Key == key)).FirstOrDefaultAsync();
    }

    // Resolve a player by its PlayFab id (used to display squad member names).
    public async Task<PlayFabUser> FindByIdAsync(string id)
    {
        return await Collection.Find(user => user.Id == id).FirstOrDefaultAsync();
    }

    // Admin back-office: list every player.
    public async Task<List<PlayFabUser>> GetAllAsync()
    {
        return await Collection.Find(_ => true).ToListAsync();
    }

    // Persist a display-name change (UpdateUserTitleDisplayName). Without this the rename
    // is echoed back to the client but lost on next login.
    public async Task UpdateDisplayNameAsync(string id, string displayName)
    {
        var update = Builders<PlayFabUser>.Update.Set(user => user.DisplayName, displayName);
        await Collection.UpdateOneAsync(user => user.Id == id, update);
    }

    private async Task<PlayFabUser> CreateAsync(PlayFabUserAuthType type, string key)
    {
        var user = new PlayFabUser
        {
            DisplayName = "Unknown",
            Auth = new List<PlayFabUserAuth>
            {
                new PlayFabUserAuth
                {
                    Type = type,
                    Key = key
                }
            }
        };
            
        await Collection.InsertOneAsync(user);

        return user;
    }

    public async Task<PlayFabUser> FindOrCreateAsync(PlayFabUserAuthType type, string key)
    {
        return await FindAsync(type, key) ??
               await CreateAsync(type, key);
    }

    // Resolve several auth keys (e.g. a batch of Steam IDs) to the players that exist on
    // this server in a single query. Used by the friends list to keep only the imported
    // Steam friends who have actually played here.
    public async Task<List<PlayFabUser>> FindManyAsync(PlayFabUserAuthType type, IReadOnlyCollection<string> keys)
    {
        if (keys == null || keys.Count == 0)
        {
            return new List<PlayFabUser>();
        }
        var wanted = new HashSet<string>(keys);
        return await Collection.Find(user => user.Auth.Any(auth =>
            auth.Type == type && wanted.Contains(auth.Key))).ToListAsync();
    }
}