using System.Collections.Concurrent;

namespace Prospect.Server.Api.Services.Squad;

// In-memory squad/lobby state. Squads are keyed by the squadId the client provides on the
// SquadMember* calls (the client forms the party and shares a squadId; each member's client
// then calls the backend with it). A member is (lazily) registered into that squad on any
// squad call, and the current squad for a user is tracked so TryGetCompleteSquadInfo — which
// carries no squadId — can still return it. State is process-local (fine for a small server).
public class SquadMemberInfo
{
    public string UserId { get; set; } = "";
    public string DisplayName { get; set; } = "";
    public int OnlineState { get; set; }
    public bool IsReady { get; set; }
    public string SelectedMap { get; set; } = "";
    public bool IsLeader { get; set; }
}

public class SquadInfo
{
    public string SquadId { get; set; } = "";
    public ConcurrentDictionary<string, SquadMemberInfo> Members { get; } = new();
}

public class SquadService
{
    private readonly ConcurrentDictionary<string, SquadInfo> _squads = new();
    private readonly ConcurrentDictionary<string, string> _userToSquad = new();

    public SquadInfo JoinOrCreate(string squadId, string userId, string displayName)
    {
        var squad = _squads.GetOrAdd(squadId, id => new SquadInfo { SquadId = id });
        var isFirst = squad.Members.IsEmpty;
        var member = squad.Members.GetOrAdd(userId, uid => new SquadMemberInfo { UserId = uid, IsLeader = isFirst });
        if (!string.IsNullOrEmpty(displayName)) member.DisplayName = displayName;
        _userToSquad[userId] = squadId;
        return squad;
    }

    public SquadInfo? GetByUser(string userId)
        => _userToSquad.TryGetValue(userId, out var sid) && _squads.TryGetValue(sid, out var sq) ? sq : null;

    public SquadInfo? Get(string squadId)
        => _squads.TryGetValue(squadId, out var sq) ? sq : null;

    public void SetReady(string squadId, string userId, bool ready, string? selectedMap)
    {
        if (_squads.TryGetValue(squadId, out var sq) && sq.Members.TryGetValue(userId, out var m))
        {
            m.IsReady = ready;
            if (!string.IsNullOrEmpty(selectedMap)) m.SelectedMap = selectedMap;
        }
    }

    public void SetMap(string squadId, string userId, string map)
    {
        if (_squads.TryGetValue(squadId, out var sq) && sq.Members.TryGetValue(userId, out var m))
            m.SelectedMap = map;
    }

    public bool IsSquadReady(string squadId)
        => _squads.TryGetValue(squadId, out var sq) && !sq.Members.IsEmpty && sq.Members.Values.All(m => m.IsReady);

    public void Leave(string userId)
    {
        if (_userToSquad.TryRemove(userId, out var sid) && _squads.TryGetValue(sid, out var sq))
        {
            sq.Members.TryRemove(userId, out _);
            if (sq.Members.IsEmpty) _squads.TryRemove(sid, out _);
        }
    }

    // Build the client-facing squad object (models live in the global namespace).
    public FYPlayFabSquad ToClientSquad(SquadInfo squad)
    {
        return new FYPlayFabSquad
        {
            SquadID = squad.SquadId,
            Members = squad.Members.Values.Select(m => new FYPlayFabSquadMember
            {
                Profile = new FYPlayFabPlayerProfile
                {
                    PlayerId = m.UserId,
                    DisplayName = m.DisplayName,
                    AvatarUrl = "",
                },
                onlineState = m.OnlineState,
                matchmakingSettings = new FYUserMatchmakingSettings
                {
                    isReadyForMatch = m.IsReady,
                    selectedMapName = m.SelectedMap,
                    isSecretLeader = m.IsLeader,
                },
                mapRowNamesUnlocked = Array.Empty<string>(),
            }).ToArray(),
        };
    }
}
