using System.Text.Json;
using Prospect.Server.Api.Services.Auth.Extensions;
using Prospect.Server.Api.Services.CloudScript.Models;
using Prospect.Server.Api.Services.Database;
using Prospect.Server.Api.Services.Database.Models;
using Prospect.Server.Api.Services.UserData;

namespace Prospect.Server.Api.Services.CloudScript.Functions;

[CloudScriptFunction("GetFriendList")]
public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
{
    private readonly ILogger<GetFriendList> _logger;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly UserDataService _userDataService;
    private readonly DbUserService _userService;

    public GetFriendList(ILogger<GetFriendList> logger, IHttpContextAccessor httpContextAccessor, UserDataService userDataService, DbUserService userService)
    {
        _logger = logger;
        _httpContextAccessor = httpContextAccessor;
        _userDataService = userDataService;
        _userService = userService;
    }

    public async Task<object?> ExecuteAsync(FYBaseSocialRequest request)
    {
        var context = _httpContextAccessor.HttpContext;
        if (context == null)
        {
            throw new CloudScriptException("CloudScript was not called within a http request");
        }
        var userId = context.User.FindAuthUserId();

        // Resolve the imported Steam friends (persisted by ClientsideFriendsImport) to the
        // players that actually exist on this server. It's a private server, so only friends
        // who have logged in here will show up.
        var steamIds = new List<string>();
        var userData = await _userDataService.FindAsync(userId, userId, new List<string> { "ImportedSteamFriends" });
        if (userData.TryGetValue("ImportedSteamFriends", out var rec) && !string.IsNullOrWhiteSpace(rec.Value))
        {
            try { steamIds = JsonSerializer.Deserialize<List<string>>(rec.Value) ?? new List<string>(); }
            catch { /* ignore malformed */ }
        }

        var friends = new List<object>();
        if (steamIds.Count > 0)
        {
            var players = await _userService.FindManyAsync(PlayFabUserAuthType.Steam, steamIds);
            var nowUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            foreach (var player in players)
            {
                if (player.Id == userId) continue; // never list yourself

                // Presence persisted by UpdatePlayerPresenceState: online if seen in the last
                // 3 minutes. EYUserState best-effort: 0 = offline, 1 = online, 2 = in match.
                var onlineState = 0;
                var presenceData = await _userDataService.FindAsync(player.Id, player.Id, new List<string> { "PresenceState" });
                if (presenceData.TryGetValue("PresenceState", out var presenceRec) && !string.IsNullOrWhiteSpace(presenceRec.Value))
                {
                    try
                    {
                        var presence = JsonSerializer.Deserialize<PlayerPresenceState>(presenceRec.Value);
                        if (presence != null && nowUtc - presence.LastSeenUtc <= 180)
                        {
                            onlineState = presence.InMatch ? 2 : 1;
                        }
                    }
                    catch { /* ignore malformed presence */ }
                }

                friends.Add(new
                {
                    profile = new
                    {
                        playerId = player.Id,
                        displayName = player.DisplayName,
                        avatarUrl = "",
                    },
                    onlineState,
                });
            }
        }

        // NOTE: the exact response shape the client expects is not yet confirmed; this is a
        // best-effort structure. The log lets us verify resolution while we validate in game.
        _logger.LogInformation("GetFriendList for {User}: {Count} friend(s) resolved on server", userId, friends.Count);
        return new { friends };
    }
}
