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();

        // Imported Steam friends (persisted by ClientsideFriendsImport), resolved to players
        // that actually exist on this private server.
        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

                var steamId = player.Auth?.FirstOrDefault(a => a.Type == PlayFabUserAuthType.Steam)?.Key ?? "";
                var displayName = string.IsNullOrWhiteSpace(player.DisplayName) ? "Prospector" : player.DisplayName;

                // 0 = offline, 1 = online, 2 = in match. Online if seen in the last 3 minutes.
                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 */ }
                }

                // The exact model the UE client binds is not documented, so expose the identity
                // under every plausible field name (camelCase + PlayFab PascalCase) and both a
                // flat and nested profile — whichever the client reads, the tile gets populated.
                friends.Add(new
                {
                    // identity
                    friendPlayFabId = player.Id,
                    FriendPlayFabId = player.Id,
                    playerId = player.Id,
                    PlayerId = player.Id,
                    playFabId = player.Id,
                    PlayFabId = player.Id,
                    // name
                    displayName,
                    DisplayName = displayName,
                    titleDisplayName = displayName,
                    TitleDisplayName = displayName,
                    name = displayName,
                    username = displayName,
                    Username = displayName,
                    // steam
                    steamId,
                    SteamId = steamId,
                    steamInfo = new { steamId, SteamId = steamId },
                    SteamInfo = new { SteamId = steamId, steamId },
                    // presence
                    onlineState,
                    OnlineState = onlineState,
                    isOnline = onlineState > 0,
                    inMatch = onlineState == 2,
                    presence = new { onlineState, state = onlineState },
                    avatarUrl = "",
                    // nested profiles
                    profile = new { playerId = player.Id, displayName, avatarUrl = "" },
                    Profile = new { PlayerId = player.Id, DisplayName = displayName, PlayerProfileModel = new { DisplayName = displayName } },
                    tags = Array.Empty<string>(),
                    Tags = Array.Empty<string>(),
                });
            }
        }

        // Return the list under both the camelCase and PlayFab-cased container keys.
        var result = new { friends, Friends = friends, count = friends.Count };
        _logger.LogInformation("GetFriendList for {User}: {Count} friend(s); payload={Json}", userId, friends.Count, JsonSerializer.Serialize(result));
        return result;
    }
}
