using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin.Services;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto;
using LightlessSync.API.Dto.Group;
using LightlessSync.Services;
using LightlessSync.Services.LightFinder;
using LightlessSync.Services.Mediator;
using LightlessSync.UI.Services;
using LightlessSync.UI.Tags;
using LightlessSync.Utils;
using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using System.Numerics;

namespace LightlessSync.UI;

public class SyncshellFinderUI : WindowMediatorSubscriberBase
{
    private readonly ApiController _apiController;
    private readonly LightFinderService _broadcastService;
    private readonly UiSharedService _uiSharedService;
    private readonly LightFinderScannerService _broadcastScannerService;
    private readonly PairUiService _pairUiService;
    private readonly DalamudUtilService _dalamudUtilService;

    private Vector4 _tagBackgroundColor = new(0.18f, 0.18f, 0.18f, 0.95f);
    private Vector4 _tagBorderColor = new(0.35f, 0.35f, 0.35f, 0.4f);

    private readonly List<SeStringUtils.SeStringSegment> _seResolvedSegments = new();
    private readonly List<GroupJoinDto> _nearbySyncshells = [];
    private List<GroupFullInfoDto> _currentSyncshells = [];
    private int _selectedNearbyIndex = -1;
    private int _syncshellPageIndex = 0;
    private readonly HashSet<string> _recentlyJoined = new(StringComparer.Ordinal);

    private GroupJoinDto? _joinDto;
    private GroupJoinInfoDto? _joinInfo;
    private DefaultPermissionsDto _ownPermissions = null!;
    private bool _useTestSyncshells = false;

    private bool _compactView = false;
    private readonly LightlessProfileManager _lightlessProfileManager;

    public SyncshellFinderUI(
        ILogger<SyncshellFinderUI> logger,
        LightlessMediator mediator,
        PerformanceCollectorService performanceCollectorService,
        LightFinderService broadcastService,
        UiSharedService uiShared,
        ApiController apiController,
        LightFinderScannerService broadcastScannerService,
        PairUiService pairUiService,
        DalamudUtilService dalamudUtilService,
        LightlessProfileManager lightlessProfileManager) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService)
    {
        _broadcastService = broadcastService;
        _uiSharedService = uiShared;
        _apiController = apiController;
        _broadcastScannerService = broadcastScannerService;
        _pairUiService = pairUiService;
        _dalamudUtilService = dalamudUtilService;
        _lightlessProfileManager = lightlessProfileManager;

        IsOpen = false;
        WindowBuilder.For(this)
                   .SetSizeConstraints(new Vector2(600, 400), new Vector2(600, 550))
                   .Apply();

        Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
        Mediator.Subscribe<BroadcastStatusChangedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
        Mediator.Subscribe<UserLeftSyncshell>(this, async _ => await RefreshSyncshellsAsync(_.gid).ConfigureAwait(false));
        Mediator.Subscribe<UserJoinedSyncshell>(this, async _ => await RefreshSyncshellsAsync(_.gid).ConfigureAwait(false));
    }

    public override async void OnOpen()
    {
        _ownPermissions = _apiController.DefaultPermissions.DeepClone()!;
        await RefreshSyncshellsAsync().ConfigureAwait(false);
    }

    protected override void DrawInternal()
    {
        ImGui.BeginGroup();
        _uiSharedService.MediumText("Nearby Syncshells", UIColors.Get("LightlessPurple"));

#if DEBUG
        if (ImGui.SmallButton("Show test syncshells"))
        {
            _useTestSyncshells = !_useTestSyncshells;
            _ = Task.Run(async () => await RefreshSyncshellsAsync().ConfigureAwait(false));
        }
        ImGui.SameLine();
#endif

        string checkboxLabel = "Compact view";
        float availWidth = ImGui.GetContentRegionAvail().X;
        float checkboxWidth = ImGui.CalcTextSize(checkboxLabel).X + ImGui.GetFrameHeight();

        float rightX = ImGui.GetCursorPosX() + availWidth - checkboxWidth - 4.0f;
        ImGui.SetCursorPosX(rightX);
        ImGui.Checkbox(checkboxLabel, ref _compactView);
        ImGui.EndGroup();

        UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));
        ImGui.Dummy(new Vector2(0, 2 * ImGuiHelpers.GlobalScale));
        if (_nearbySyncshells.Count == 0)
        {
            ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells are being broadcasted.");

            if (!_broadcastService.IsBroadcasting)
            {

                UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"));

                ImGui.TextColored(UIColors.Get("LightlessYellow"), "Lightfinder is currently disabled, to locate nearby syncshells, Lightfinder must be active.");
                ImGuiHelpers.ScaledDummy(0.5f);

                ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 10.0f);
                ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessPurple"));

                if (ImGui.Button("Open Lightfinder", new Vector2(200 * ImGuiHelpers.GlobalScale, 0)))
                {
                    Mediator.Publish(new UiToggleMessage(typeof(LightFinderUI)));
                }

                ImGui.PopStyleColor();
                ImGui.PopStyleVar();

                return;
            }

            return;
        }

        var broadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts().ToList() ?? [];
        _broadcastScannerService.TryGetLocalHashedCid(out var localHashedCid);

        var cardData = new List<(GroupJoinDto Shell, string BroadcasterName, bool IsSelfBroadcast)>();

        foreach (var shell in _nearbySyncshells)
        {
            string broadcasterName;

            if (shell?.Group == null || string.IsNullOrEmpty(shell.Group.GID))
                continue;

            if (_useTestSyncshells)
            {
                var displayName = !string.IsNullOrEmpty(shell.Group.Alias)
                    ? shell.Group.Alias
                    : shell.Group.GID;

                broadcasterName = $"{displayName} (Tester of TestWorld)";
            }
            else
            {
                var broadcast = broadcasts
                    .FirstOrDefault(b => string.Equals(b.GID, shell.Group.GID, StringComparison.Ordinal));

                if (broadcast == null)
                    continue;

                var (name, address) = _dalamudUtilService.FindPlayerByNameHash(broadcast.HashedCID);
                if (string.IsNullOrEmpty(name))
                    continue;

                var worldName = _dalamudUtilService.GetWorldNameFromPlayerAddress(address);
                broadcasterName = !string.IsNullOrEmpty(worldName)
                    ? $"{name} ({worldName})"
                    : name;

                var isSelfBroadcast = !string.IsNullOrEmpty(localHashedCid)
                    && string.Equals(broadcast.HashedCID, localHashedCid, StringComparison.Ordinal);

                cardData.Add((shell, broadcasterName, isSelfBroadcast));
                continue;
            }

            cardData.Add((shell, broadcasterName, false));
        }

        if (cardData.Count == 0)
        {
            ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells are being broadcasted.");
            return;
        }

        if (_compactView)
        {
            DrawSyncshellGrid(cardData);
        }
        else
        {
            DrawSyncshellList(cardData);
        }


        if (_joinDto != null && _joinInfo != null && _joinInfo.Success)
            DrawConfirmation();
    }

    private void DrawSyncshellList(List<(GroupJoinDto Shell, string BroadcasterName, bool IsSelfBroadcast)> listData)
    {
        const int shellsPerPage = 3;
        var totalPages = (int)Math.Ceiling(listData.Count / (float)shellsPerPage);
        if (totalPages <= 0)
            totalPages = 1;

        _syncshellPageIndex = Math.Clamp(_syncshellPageIndex, 0, totalPages - 1);

        var firstIndex = _syncshellPageIndex * shellsPerPage;
        var lastExclusive = Math.Min(firstIndex + shellsPerPage, listData.Count);

        ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 8.0f);
        ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1.0f);

        for (int index = firstIndex; index < lastExclusive; index++)
        {
            var (shell, broadcasterName, isSelfBroadcast) = listData[index];
            var broadcasterLabel = string.IsNullOrEmpty(broadcasterName)
                ? (isSelfBroadcast ? "You" : string.Empty)
                : (isSelfBroadcast ? $"{broadcasterName} (You)" : broadcasterName);

            ImGui.PushID(shell.Group.GID);
            float rowHeight = 74f * ImGuiHelpers.GlobalScale;

            ImGui.BeginChild($"ShellRow##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true);

            var displayName = !string.IsNullOrEmpty(shell.Group.Alias) ? shell.Group.Alias : shell.Group.GID;

            var style = ImGui.GetStyle();
            float startX = ImGui.GetCursorPosX();
            float regionW = ImGui.GetContentRegionAvail().X;
            float rightTxtW = ImGui.CalcTextSize(broadcasterLabel).X;

            _uiSharedService.MediumText(displayName, UIColors.Get("LightlessPurple"));
            if (ImGui.IsItemHovered())
                ImGui.SetTooltip("Click to open profile.");
            if (ImGui.IsItemClicked())
            {
                Mediator.Publish(new GroupProfileOpenStandaloneMessage(shell.Group));
            }

            float rightX = startX + regionW - rightTxtW - style.ItemSpacing.X;
            ImGui.SameLine();
            ImGui.SetCursorPosX(rightX);
            ImGui.TextUnformatted(broadcasterLabel);
            if (ImGui.IsItemHovered())
                ImGui.SetTooltip("Broadcaster of the syncshell.");

            UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));

            var groupProfile = _lightlessProfileManager.GetLightlessGroupProfile(shell.Group);

            IReadOnlyList<ProfileTagDefinition> groupTags =
                groupProfile != null && groupProfile.Tags.Count > 0
                    ? ProfileTagService.ResolveTags(groupProfile.Tags)
                    : [];

            var limitedTags = groupTags.Count > 3
                ? [.. groupTags.Take(3)]
                : groupTags;

            float tagScale = ImGuiHelpers.GlobalScale * 0.9f;

            Vector2 rowStartLocal = ImGui.GetCursorPos();

            float tagsWidth = 0f;
            float tagsHeight = 0f;

            if (limitedTags.Count > 0)
            {
                (tagsWidth, tagsHeight) = RenderProfileTagsSingleRow(limitedTags, tagScale);
            }
            else
            {
                ImGui.SetCursorPosX(startX);
                ImGui.TextDisabled("-- No tags set --");
                ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
            }

            float btnBaselineY = rowStartLocal.Y;
            float joinX = rowStartLocal.X + (tagsWidth > 0 ? tagsWidth + style.ItemSpacing.X : 0f);

            ImGui.SetCursorPos(new Vector2(joinX, btnBaselineY));
            DrawJoinButton(shell, isSelfBroadcast);

            float btnHeight = ImGui.GetFrameHeightWithSpacing();
            float rowHeightUsed = MathF.Max(tagsHeight, btnHeight);

            ImGui.SetCursorPos(new Vector2(
                rowStartLocal.X,
                rowStartLocal.Y + rowHeightUsed));

            ImGui.EndChild();
            ImGui.PopID();

            ImGui.Dummy(new Vector2(0, 2 * ImGuiHelpers.GlobalScale));
        }

        ImGui.PopStyleVar(2);

        DrawPagination(totalPages);
    }

    private void DrawSyncshellGrid(List<(GroupJoinDto Shell, string BroadcasterName, bool IsSelfBroadcast)> cardData)
    {
        const int shellsPerPage = 4;
        var totalPages = (int)Math.Ceiling(cardData.Count / (float)shellsPerPage);
        if (totalPages <= 0)
            totalPages = 1;

        _syncshellPageIndex = Math.Clamp(_syncshellPageIndex, 0, totalPages - 1);

        var firstIndex = _syncshellPageIndex * shellsPerPage;
        var lastExclusive = Math.Min(firstIndex + shellsPerPage, cardData.Count);

        var avail = ImGui.GetContentRegionAvail();
        var spacing = ImGui.GetStyle().ItemSpacing;

        var cardWidth = (avail.X - spacing.X) / 2.0f;
        var cardHeight = (avail.Y - spacing.Y - (ImGui.GetFrameHeightWithSpacing() * 2.0f)) / 2.0f;
        cardHeight = MathF.Max(110f * ImGuiHelpers.GlobalScale, cardHeight);

        ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 8.0f);
        ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1.0f);

        for (int index = firstIndex; index < lastExclusive; index++)
        {
            var localIndex = index - firstIndex;
            var (shell, broadcasterName, isSelfBroadcast) = cardData[index];
            var broadcasterLabel = string.IsNullOrEmpty(broadcasterName)
                ? (isSelfBroadcast ? "You" : string.Empty)
                : (isSelfBroadcast ? $"{broadcasterName} (You)" : broadcasterName);

            if (localIndex % 2 != 0)
                ImGui.SameLine();

            ImGui.PushID(shell.Group.GID);

            ImGui.BeginGroup();
            _ = ImGui.BeginChild("ShellCard##" + shell.Group.GID, new Vector2(cardWidth, cardHeight), border: true);

            var displayName = !string.IsNullOrEmpty(shell.Group.Alias)
                ? shell.Group.Alias
                : shell.Group.GID;

            var style = ImGui.GetStyle();
            float startX = ImGui.GetCursorPosX();
            float availW = ImGui.GetContentRegionAvail().X;

            ImGui.BeginGroup();

            _uiSharedService.MediumText(displayName, UIColors.Get("LightlessPurple"));
            if (ImGui.IsItemHovered())
                ImGui.SetTooltip("Click to open profile.");
            if (ImGui.IsItemClicked())
            {
                Mediator.Publish(new GroupProfileOpenStandaloneMessage(shell.Group));
            }

            float nameRightX = ImGui.GetItemRectMax().X;

            var regionMinScreen = ImGui.GetCursorScreenPos();
            float regionRightX = regionMinScreen.X + availW;

            float minBroadcasterX = nameRightX + style.ItemSpacing.X;

            float maxBroadcasterWidth = regionRightX - minBroadcasterX;

            string broadcasterToShow = broadcasterLabel;

            if (!string.IsNullOrEmpty(broadcasterLabel) && maxBroadcasterWidth > 0f)
            {
                float bcFullWidth = ImGui.CalcTextSize(broadcasterLabel).X;
                string toolTip;

                if (bcFullWidth > maxBroadcasterWidth)
                {
                    broadcasterToShow = TruncateTextToWidth(broadcasterLabel, maxBroadcasterWidth);
                    toolTip = broadcasterLabel + Environment.NewLine + Environment.NewLine + "Broadcaster of the syncshell.";
                } 
                else
                {
                    toolTip = "Broadcaster of the syncshell.";
                }

                float bcWidth = ImGui.CalcTextSize(broadcasterToShow).X;

                float broadX = regionRightX - bcWidth;

                broadX = MathF.Max(broadX, minBroadcasterX);

                ImGui.SameLine();
                var curPos = ImGui.GetCursorPos();
                ImGui.SetCursorPos(new Vector2(broadX - regionMinScreen.X + startX, curPos.Y + 3f * ImGuiHelpers.GlobalScale));
                ImGui.TextUnformatted(broadcasterToShow);
                if (ImGui.IsItemHovered())
                    ImGui.SetTooltip(toolTip);
            }

            ImGui.EndGroup();

            UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));

            ImGui.Dummy(new Vector2(0, 6 * ImGuiHelpers.GlobalScale));

            var groupProfile = _lightlessProfileManager.GetLightlessGroupProfile(shell.Group);

            IReadOnlyList<ProfileTagDefinition> groupTags =
                groupProfile != null && groupProfile.Tags.Count > 0
                    ? ProfileTagService.ResolveTags(groupProfile.Tags)
                    : [];

            float tagScale = ImGuiHelpers.GlobalScale * 0.9f;

            if (groupTags.Count > 0)
            {
                var limitedTags = groupTags.Count > 2
                    ? [.. groupTags.Take(2)]
                    : groupTags;

                ImGui.SetCursorPosX(startX);

                var (_, tagsHeight) = RenderProfileTagsSingleRow(limitedTags, tagScale);

                ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
            }
            else
            {
                ImGui.SetCursorPosX(startX);
                ImGui.TextDisabled("-- No tags set --");
                ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
            }

            var buttonHeight = ImGui.GetFrameHeightWithSpacing();
            var remainingY = ImGui.GetContentRegionAvail().Y - buttonHeight;
            if (remainingY > 0)
                ImGui.Dummy(new Vector2(0, remainingY));

            DrawJoinButton(shell, isSelfBroadcast);

            ImGui.EndChild();
            ImGui.EndGroup();

            ImGui.PopID();
        }

        ImGui.Dummy(new Vector2(0, 2 * ImGuiHelpers.GlobalScale));
        ImGui.PopStyleVar(2);

        DrawPagination(totalPages);
    }

    private void DrawPagination(int totalPages)
    {
        if (totalPages > 1)
        {
            UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));

            var style = ImGui.GetStyle();
            string pageLabel = $"Page {_syncshellPageIndex + 1}/{totalPages}";

            float prevWidth = ImGui.CalcTextSize("<").X + style.FramePadding.X * 2;
            float nextWidth = ImGui.CalcTextSize(">").X + style.FramePadding.X * 2;
            float textWidth = ImGui.CalcTextSize(pageLabel).X;

            float totalWidth = prevWidth + textWidth + nextWidth + style.ItemSpacing.X * 2;

            float availWidth = ImGui.GetContentRegionAvail().X;
            float offsetX = (availWidth - totalWidth) * 0.5f;

            ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offsetX);

            if (ImGui.Button("<##PrevSyncshellPage") && _syncshellPageIndex > 0)
                _syncshellPageIndex--;

            ImGui.SameLine();
            ImGui.Text(pageLabel);

            ImGui.SameLine();
            if (ImGui.Button(">##NextSyncshellPage") && _syncshellPageIndex < totalPages - 1)
                _syncshellPageIndex++;
        }
    }

    private void DrawJoinButton(GroupJoinDto shell, bool isSelfBroadcast)
    {
        const string visibleLabel = "Join";
        var label = $"{visibleLabel}##{shell.Group.GID}";

        var isAlreadyMember = _currentSyncshells.Exists(g => string.Equals(g.GID, shell.GID, StringComparison.Ordinal));
        var isRecentlyJoined = _recentlyJoined.Contains(shell.GID);

        Vector2 buttonSize;

        if (!_compactView)
        {
            var style = ImGui.GetStyle();
            var textSize = ImGui.CalcTextSize(visibleLabel);

            var width = textSize.X + style.FramePadding.X * 20f;
            buttonSize = new Vector2(width, 30f);

            float availX = ImGui.GetContentRegionAvail().X;
            float curX = ImGui.GetCursorPosX();
            float newX = curX + (availX - buttonSize.X);
            ImGui.SetCursorPosX(newX);
        }
        else
        {
            buttonSize = new Vector2(-1, 0);
        }

        if (!isAlreadyMember && !isRecentlyJoined && !isSelfBroadcast)
        {
            ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessGreen"));
            ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f));
            ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f));
            if (ImGui.Button(label, buttonSize))
            {
                _logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})");

                _ = Task.Run(async () =>
                {
                    try
                    {
                        var info = await _apiController.GroupJoinHashed(new GroupJoinHashedDto(
                            shell.Group,
                            shell.Password,
                            shell.GroupUserPreferredPermissions
                        )).ConfigureAwait(false);

                        if (info != null && info.Success)
                        {
                            _joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions);
                            _joinInfo = info;
                            _ownPermissions = _apiController.DefaultPermissions.DeepClone()!;

                            _logger.LogInformation($"Fetched join info for {shell.Group.GID}");
                        }
                        else
                        {
                            _logger.LogWarning($"Failed to join {shell.Group.GID}: info was null or unsuccessful");
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"Join failed for {shell.Group.GID}");
                    }
                });
            }
        }
        else
        {
            ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("DimRed"));
            ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("DimRed").WithAlpha(0.85f));
            ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("DimRed").WithAlpha(0.75f));

            using (ImRaii.Disabled())
            {
                ImGui.Button(label, buttonSize);
            }

            UiSharedService.AttachToolTip(isSelfBroadcast
                ? "This is your own Syncshell."
                : "Already a member or owner of this Syncshell.");
        }

        ImGui.PopStyleColor(3);
    }

    private (float widthUsed, float rowHeight) RenderProfileTagsSingleRow(IReadOnlyList<ProfileTagDefinition> tags, float scale)
    {
        if (tags == null || tags.Count == 0)
            return (0f, 0f);

        var drawList = ImGui.GetWindowDrawList();
        var style = ImGui.GetStyle();
        var defaultTextColorU32 = ImGui.GetColorU32(ImGuiCol.Text);

        var baseLocal = ImGui.GetCursorPos();
        var baseScreen = ImGui.GetCursorScreenPos();
        float availableWidth = ImGui.GetContentRegionAvail().X;
        if (availableWidth <= 0f)
            availableWidth = 1f;

        float cursorLocalX = baseLocal.X;
        float cursorScreenX = baseScreen.X;
        float rowHeight = 0f;

        for (int i = 0; i < tags.Count; i++)
        {
            var tag = tags[i];
            if (!tag.HasContent)
                continue;

            var tagSize = ProfileTagRenderer.MeasureTag(tag, scale, style, _tagBackgroundColor, _tagBorderColor, defaultTextColorU32, _seResolvedSegments, GetIconWrap, _logger);

            float tagWidth = tagSize.X;
            float tagHeight = tagSize.Y;

            if (cursorLocalX > baseLocal.X && cursorLocalX + tagWidth > baseLocal.X + availableWidth)
                break;

            var tagScreenPos = new Vector2(cursorScreenX, baseScreen.Y);
            ImGui.SetCursorScreenPos(tagScreenPos);
            ImGui.InvisibleButton($"##profileTagInline_{i}", tagSize);

            ProfileTagRenderer.RenderTag(tag, tagScreenPos, scale, drawList, style, _tagBackgroundColor, _tagBorderColor, defaultTextColorU32, _seResolvedSegments, GetIconWrap, _logger);

            cursorLocalX += tagWidth + style.ItemSpacing.X;
            cursorScreenX += tagWidth + style.ItemSpacing.X;
            rowHeight = MathF.Max(rowHeight, tagHeight);
        }

        ImGui.SetCursorPos(new Vector2(baseLocal.X, baseLocal.Y + rowHeight));

        float widthUsed = cursorLocalX - baseLocal.X;
        return (widthUsed, rowHeight);
    }
    private static string TruncateTextToWidth(string text, float maxWidth)
    {
        if (string.IsNullOrEmpty(text))
            return text;

        const string ellipsis = "...";
        float ellipsisWidth = ImGui.CalcTextSize(ellipsis).X;

        if (maxWidth <= ellipsisWidth)
            return ellipsis;

        int low = 0;
        int high = text.Length;
        string best = ellipsis;

        while (low <= high)
        {
            int mid = (low + high) / 2;
            string candidate = string.Concat(text.AsSpan(0, mid), ellipsis);
            float width = ImGui.CalcTextSize(candidate).X;

            if (width <= maxWidth)
            {
                best = candidate;
                low = mid + 1;
            }
            else
            {
                high = mid - 1;
            }
        }

        return best;
    }

    private IDalamudTextureWrap? GetIconWrap(uint iconId)
    {
        try
        {
            if (_uiSharedService.TryGetIcon(iconId, out var wrap) && wrap != null)
                return wrap;
        }
        catch (Exception ex)
        {
            _logger.LogDebug(ex, "Failed to resolve icon {IconId} for profile tags", iconId);
        }

        return null;
    }

    private void DrawConfirmation()
    {
        if (_joinDto != null && _joinInfo != null)
        {
            ImGui.Separator();
            ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
            ImGuiHelpers.ScaledDummy(2f);
            ImGui.TextUnformatted("Suggested Syncshell Permissions:");

            DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v);
            DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v);
            DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v);

            ImGui.NewLine();
            ImGui.NewLine();

            if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}"))
            {
                var finalPermissions = GroupUserPreferredPermissions.NoneSet;
                finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds);
                finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations);
                finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);

                _ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions));

                _recentlyJoined.Add(_joinDto.Group.GID);

                _joinDto = null;
                _joinInfo = null;
            }
        }
    }

    private void DrawPermissionRow(string label, bool suggested, bool current, Action<bool> apply)
    {
        ImGui.AlignTextToFramePadding();
        ImGui.TextUnformatted($"- {label}");

        ImGui.SameLine(150 * ImGuiHelpers.GlobalScale);
        ImGui.TextUnformatted("Current:");
        ImGui.SameLine();
        _uiSharedService.BooleanToColoredIcon(!current);

        ImGui.SameLine(300 * ImGuiHelpers.GlobalScale);
        ImGui.TextUnformatted("Suggested:");
        ImGui.SameLine();
        _uiSharedService.BooleanToColoredIcon(!suggested);

        ImGui.SameLine(450 * ImGuiHelpers.GlobalScale);
        using var id = ImRaii.PushId(label);
        if (current != suggested)
        {
            if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply"))
                apply(suggested);
        }

        ImGui.NewLine();
    }

    private async Task RefreshSyncshellsAsync(string? gid = null)
    {
        var syncshellBroadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts();
        var snapshot = _pairUiService.GetSnapshot();
        _currentSyncshells = [.. snapshot.GroupPairs.Keys];

        _recentlyJoined.RemoveWhere(gid =>
            _currentSyncshells.Exists(s => string.Equals(s.GID, gid, StringComparison.Ordinal)));

        List<GroupJoinDto>? updatedList = [];

        if (_useTestSyncshells)
        {
            updatedList = BuildTestSyncshells();
        }
        else
        {
            if (syncshellBroadcasts.Count == 0)
            {
                ClearSyncshells();
                return;
            }

            try
            {
                var groups = await _apiController.GetBroadcastedGroups(syncshellBroadcasts)
                    .ConfigureAwait(false);
                updatedList = groups?.DistinctBy(g => g.Group.GID).ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to refresh broadcasted syncshells.");
                return;
            }
        }

        if (updatedList == null || updatedList.Count == 0)
        {
            ClearSyncshells();
            return;
        }

        if (gid != null && _recentlyJoined.Contains(gid))
        {
            _recentlyJoined.Clear();
        }

        var previousGid = GetSelectedGid();

        _nearbySyncshells.Clear();
        _nearbySyncshells.AddRange(updatedList);

        if (previousGid != null)
        {
            var newIndex = _nearbySyncshells.FindIndex(s =>
                string.Equals(s.Group.GID, previousGid, StringComparison.Ordinal));

            if (newIndex >= 0)
            {
                _selectedNearbyIndex = newIndex;
                return;
            }
        }

        ClearSelection();
    }

    private static List<GroupJoinDto> BuildTestSyncshells()
    {
        var testGroup1 = new GroupData("TEST-ALPHA", "Alpha Shell");
        var testGroup2 = new GroupData("TEST-BETA", "Beta Shell");
        var testGroup3 = new GroupData("TEST-GAMMA", "Gamma Shell");
        var testGroup4 = new GroupData("TEST-DELTA", "Delta Shell");
        var testGroup5 = new GroupData("TEST-CHARLIE", "Charlie Shell");
        var testGroup6 = new GroupData("TEST-OMEGA", "Omega Shell");
        var testGroup7 = new GroupData("TEST-POINT", "Point Shell");
        var testGroup8 = new GroupData("TEST-HOTEL", "Hotel Shell");

        return
        [
            new(testGroup1, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup2, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup3, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup4, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup5, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup6, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup7, "", GroupUserPreferredPermissions.NoneSet),
            new(testGroup8, "", GroupUserPreferredPermissions.NoneSet),
        ];
    }

    private void ClearSyncshells()
    {
        if (_nearbySyncshells.Count == 0)
            return;

        _nearbySyncshells.Clear();
        ClearSelection();
    }

    private void ClearSelection()
    {
        _selectedNearbyIndex = -1;
        _syncshellPageIndex = 0;
        _joinDto = null;
        _joinInfo = null;
    }

    private string? GetSelectedGid()
    {
        if (_selectedNearbyIndex < 0 || _selectedNearbyIndex >= _nearbySyncshells.Count)
            return null;

        return _nearbySyncshells[_selectedNearbyIndex].Group.GID;
    }

}
