using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
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 StandaloneProfileUi : WindowMediatorSubscriberBase
{
    private readonly LightlessProfileManager _lightlessProfileManager;
    private readonly PairUiService _pairUiService;
    private readonly ServerConfigurationManager _serverManager;
    private readonly ProfileTagService _profileTagService;
    private readonly ApiController _apiController;
    private readonly UiSharedService _uiSharedService;
    private readonly UserData? _userData;
    private readonly GroupData? _groupData;
    private readonly bool _isGroupProfile;
    private readonly bool _isLightfinderContext;
    private readonly string? _lightfinderCid;
    private byte[] _lastProfilePicture = [];
    private byte[] _lastSupporterPicture = [];
    private byte[] _lastBannerPicture = [];
    private IDalamudTextureWrap? _supporterTextureWrap;
    private IDalamudTextureWrap? _textureWrap;
    private IDalamudTextureWrap? _bannerTextureWrap;
    private bool _bannerTextureLoaded;
    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 const float MaxHeightMultiplier = 2.5f;
    private const float DescriptionMaxVisibleLines = 12f;
    private const string UserDescriptionPlaceholder = "-- User has no description set --";
    private const string GroupDescriptionPlaceholder = "-- Syncshell has no description set --";
    private const string LightfinderDisplayName = "Lightfinder User";
    private readonly string _lightfinderDisplayName = LightfinderDisplayName;
    private float _lastComputedWindowHeight = -1f;

    public StandaloneProfileUi(
        ILogger<StandaloneProfileUi> logger,
        LightlessMediator mediator,
        UiSharedService uiBuilder,
        ServerConfigurationManager serverManager,
        ProfileTagService profileTagService,
        DalamudUtilService dalamudUtilService,
        LightlessProfileManager lightlessProfileManager,
        PairUiService pairUiService,
        Pair? pair,
        UserData? userData,
        GroupData? groupData,
        bool isLightfinderContext,
        string? lightfinderCid,
        PerformanceCollectorService performanceCollector,
        ApiController apiController)
        : base(logger, mediator, BuildWindowTitle(
            userData,
            groupData,
            isLightfinderContext,
            isLightfinderContext ? ResolveLightfinderDisplayName(dalamudUtilService, lightfinderCid) : null),
            performanceCollector)
    {
        _uiSharedService = uiBuilder;
        _serverManager = serverManager;
        _profileTagService = profileTagService;
        _lightlessProfileManager = lightlessProfileManager;
        Pair = pair;
        _pairUiService = pairUiService;
        _userData = userData;
        _groupData = groupData;
        _isGroupProfile = groupData is not null;
        _isLightfinderContext = isLightfinderContext;
        _lightfinderCid = lightfinderCid;
        if (_isLightfinderContext)
            _lightfinderDisplayName = ResolveLightfinderDisplayName(dalamudUtilService, lightfinderCid);

        Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoResize;

        var fixedSize = new Vector2(840f, 525f) * ImGuiHelpers.GlobalScale;
        Size = fixedSize;
        SizeCondition = ImGuiCond.Always;
        WindowBuilder.For(this)
            .SetSizeConstraints(
                fixedSize,
                new Vector2(fixedSize.X, fixedSize.Y * MaxHeightMultiplier))
            .Apply();

        IsOpen = true;
        _apiController = apiController;
    }

    public Pair? Pair { get; }
    public bool IsGroupProfile => _isGroupProfile;
    public GroupData? ProfileGroupData => _groupData;
    public bool IsLightfinderContext => _isLightfinderContext;
    public string? LightfinderCid => _lightfinderCid;
    public UserData ProfileUserData => _userData ?? throw new InvalidOperationException("ProfileUserData is only available for user profiles.");

    public void SetTagColorTheme(Vector4? background, Vector4? border)
    {
        if (background.HasValue)
            _tagBackgroundColor = background.Value;
        if (border.HasValue)
            _tagBorderColor = border.Value;
    }

    private static Vector4 ResolveThemeColor(string colorName, Vector4 fallback)
    {
        try
        {
            return UIColors.Get(colorName);
        }
        catch (ArgumentException)
        {
            // fallback when the color key is not registered
        }

        return fallback;
    }

    private static string BuildWindowTitle(UserData? userData, GroupData? groupData, bool isLightfinderContext, string? lightfinderDisplayName)
    {
        if (groupData is not null)
        {
            var alias = groupData.AliasOrGID;
            return $"Syncshell Profile of {alias}##LightlessSyncStandaloneGroupProfileUI{groupData.GID}";
        }

        if (userData is null)
            return "Lightless Profile##LightlessSyncStandaloneProfileUI";

        var name = isLightfinderContext ? lightfinderDisplayName ?? LightfinderDisplayName : userData.AliasOrUID;
        var suffix = isLightfinderContext ? " (Lightfinder)" : string.Empty;
        return $"Lightless Profile of {name}{suffix}##LightlessSyncStandaloneProfileUI{name}";
    }

    private static string ResolveLightfinderDisplayName(DalamudUtilService dalamudUtilService, string? hashedCid)
    {
        if (string.IsNullOrEmpty(hashedCid))
            return LightfinderDisplayName;

        try
        {
            var (name, address) = dalamudUtilService.FindPlayerByNameHash(hashedCid);
            if (string.IsNullOrEmpty(name))
                return LightfinderDisplayName;

            var world = dalamudUtilService.GetWorldNameFromPlayerAddress(address);
            return string.IsNullOrEmpty(world) ? name : $"{name} ({world})";
        }
        catch
        {
            return LightfinderDisplayName;
        }
    }

    protected override void DrawInternal()
    {
        try
        {
            if (_isGroupProfile)
            {
                DrawGroupProfileWindow();
                return;
            }

            if (_userData is null)
                return;

            var userData = _userData;
            var scale = ImGuiHelpers.GlobalScale;
            var viewport = ImGui.GetMainViewport();
            var linked = !_isLightfinderContext
                         && Pair is null
                         && ProfileEditorLayoutCoordinator.IsActive(userData.UID);
            var baseSize = ProfileEditorLayoutCoordinator.GetProfileSize(scale);
            var baseWidth = baseSize.X;
            var minHeight = baseSize.Y;
            var maxAllowedHeight = minHeight * MaxHeightMultiplier;
            var targetHeight = _lastComputedWindowHeight > 0f
                ? Math.Clamp(_lastComputedWindowHeight, minHeight, maxAllowedHeight)
                : minHeight;
            var desiredSize = new Vector2(baseWidth, targetHeight);
            Size = desiredSize;
            if (linked)
            {
                ProfileEditorLayoutCoordinator.EnsureAnchor(viewport.WorkPos, scale);

                var currentPos = ImGui.GetWindowPos();
                if (IsWindowBeingDragged())
                    ProfileEditorLayoutCoordinator.UpdateAnchorFromProfile(currentPos);

                var desiredPos = ProfileEditorLayoutCoordinator.GetProfilePosition(scale);
                if (!ProfileEditorLayoutCoordinator.NearlyEquals(currentPos, desiredPos))
                    ImGui.SetWindowPos(desiredPos, ImGuiCond.Always);

                if (!ProfileEditorLayoutCoordinator.NearlyEquals(ImGui.GetWindowSize(), desiredSize))
                    ImGui.SetWindowSize(desiredSize, ImGuiCond.Always);
            }
            else
            {
                var defaultPosition = viewport.WorkPos + (new Vector2(50f, 70f) * scale);
                ImGui.SetWindowPos(defaultPosition, ImGuiCond.FirstUseEver);
                ImGui.SetWindowSize(desiredSize, ImGuiCond.Always);
            }

            var profile = _lightlessProfileManager.GetLightlessProfile(userData);
            IReadOnlyList<ProfileTagDefinition> profileTags = profile.Tags.Count > 0
                ? ProfileTagService.ResolveTags(profile.Tags)
                : [];

            if (_textureWrap == null || !profile.ImageData.Value.SequenceEqual(_lastProfilePicture))
            {
                _textureWrap?.Dispose();
                _textureWrap = null;
                _lastProfilePicture = profile.ImageData.Value;
                ResetBannerTexture();
                if (_lastProfilePicture.Length > 0)
                {
                    _textureWrap = _uiSharedService.LoadImage(_lastProfilePicture);
                }
            }

            if (_supporterTextureWrap == null || !profile.SupporterImageData.Value.SequenceEqual(_lastSupporterPicture))
            {
                _supporterTextureWrap?.Dispose();
                _supporterTextureWrap = null;
                if (!string.IsNullOrEmpty(profile.Base64SupporterPicture))
                {
                    _lastSupporterPicture = profile.SupporterImageData.Value;
                    if (_lastSupporterPicture.Length > 0)
                    {
                        _supporterTextureWrap = _uiSharedService.LoadImage(_lastSupporterPicture);
                    }
                }
            }

            var bannerBytes = profile.BannerImageData.Value;
            if (!_lastBannerPicture.SequenceEqual(bannerBytes))
            {
                ResetBannerTexture();
                _lastBannerPicture = bannerBytes;
            }
            string? noteText = null;

            var isSelfProfile = !_isLightfinderContext
                                && _userData is not null
                                && !string.IsNullOrEmpty(_apiController.UID)
                                && string.Equals(_userData.UID, _apiController.UID, StringComparison.Ordinal);

            string statusLabel = _isLightfinderContext
                ? "Exploring"
                : isSelfProfile ? "Online" : "Offline";

            string? visiblePlayerName = null;
            bool directPair = false;
            bool youPaused = false;
            bool theyPaused = false;
            List<string> syncshellLines = [];

            if (!_isLightfinderContext)
            {
                noteText = _serverManager.GetNoteForUid(_userData!.UID);
            }

            if (!_isLightfinderContext && Pair != null)
            {
                var snapshot = _pairUiService.GetSnapshot();
                noteText = _serverManager.GetNoteForUid(Pair.UserData.UID);

                statusLabel = Pair.IsVisible ? "Visible" : (Pair.IsOnline ? "Online" : "Offline");
                visiblePlayerName = Pair.IsVisible ? Pair.PlayerName : null;

                directPair = Pair.IsDirectlyPaired;

                var pairInfo = Pair.UserPair;
                if (pairInfo != null)
                {
                    if (directPair)
                    {
                        youPaused = pairInfo.OwnPermissions.IsPaused();
                        theyPaused = pairInfo.OtherPermissions.IsPaused();
                    }

                    if (pairInfo.Groups.Count != 0)
                    {
                        foreach (var gid in pairInfo.Groups)
                        {
                            var groupLabel = snapshot.GroupsByGid.TryGetValue(gid, out var groupInfo)
                                ? groupInfo.GroupAliasOrGID
                                : gid;

                            var groupNote = _serverManager.GetNoteForGid(gid);
                            syncshellLines.Add(string.IsNullOrEmpty(groupNote) ? groupLabel : $"{groupNote} ({groupLabel})");
                        }
                    }
                }

                if (isSelfProfile)
                    statusLabel = "Online";
            }

            var presenceTokens = new List<PresenceToken>
            {
                new(statusLabel, string.Equals(statusLabel, "Offline", StringComparison.OrdinalIgnoreCase))
            };

            if (!string.IsNullOrEmpty(visiblePlayerName))
                presenceTokens.Add(new PresenceToken(visiblePlayerName, false));

            if (directPair)
            {
                presenceTokens.Add(new PresenceToken("Direct Pair", true));
                if (youPaused)
                    presenceTokens.Add(new PresenceToken("You paused syncing", true));
                if (theyPaused)
                    presenceTokens.Add(new PresenceToken("They paused syncing", true));
            }

            if (profile.IsNSFW)
                presenceTokens.Add(new PresenceToken("NSFW", Emphasis: true));

            if (syncshellLines.Count > 0)
                presenceTokens.Add(new PresenceToken($"Sharing Syncshells ({syncshellLines.Count})", Emphasis: false, syncshellLines, "Shared Syncshells"));

            var drawList = ImGui.GetWindowDrawList();
            var style = ImGui.GetStyle();
            var windowPos = ImGui.GetWindowPos();
            var windowSize = ImGui.GetWindowSize();
            var bannerHeight = 260f * scale;
            var portraitSize = new Vector2(180f, 180f) * scale;
            var portraitBorder = 1.5f * scale;
            var portraitRounding = 1f * scale;
            var portraitFrameSize = portraitSize + new Vector2(portraitBorder * 2f);
            var portraitOverlap = portraitSize.Y * 0.35f;
            var portraitOffsetX = 35f * scale;
            var infoOffsetX = portraitOffsetX + portraitFrameSize.X + style.ItemSpacing.X * 2f;
            var bannerTexture = GetBannerTexture(_lastBannerPicture) ?? _textureWrap ?? _supporterTextureWrap;

            string defaultSubtitle = !_isLightfinderContext && Pair != null && !string.IsNullOrEmpty(Pair.UserData.Alias)
                ? Pair.UserData.Alias!
                : _isLightfinderContext ? "Lightfinder Session" : noteText ?? string.Empty;

            bool hasVanityAlias = !_isLightfinderContext && userData.HasVanity && !string.IsNullOrWhiteSpace(userData.Alias);
            Vector4? vanityTextColor = null;
            Vector4? vanityGlowColor = null;

            if (hasVanityAlias)
            {
                if (!string.IsNullOrWhiteSpace(userData.TextColorHex))
                    vanityTextColor = UIColors.HexToRgba(userData.TextColorHex);

                if (!string.IsNullOrWhiteSpace(userData.TextGlowColorHex))
                    vanityGlowColor = UIColors.HexToRgba(userData.TextGlowColorHex);
            }

            bool useVanityColors = vanityTextColor.HasValue || vanityGlowColor.HasValue;
            string primaryHeaderText = _isLightfinderContext
                ? _lightfinderDisplayName
                : hasVanityAlias ? userData.Alias! : userData.UID;

            List<(string Text, bool UseVanityColor, bool Disabled)> secondaryHeaderLines = new();
            if (!_isLightfinderContext && hasVanityAlias)
            {
                secondaryHeaderLines.Add((userData.UID, useVanityColors, false));

                if (!string.IsNullOrEmpty(defaultSubtitle)
                    && !string.Equals(defaultSubtitle, userData.UID, StringComparison.OrdinalIgnoreCase)
                    && !string.Equals(defaultSubtitle, userData.Alias, StringComparison.OrdinalIgnoreCase))
                {
                    secondaryHeaderLines.Add((defaultSubtitle, false, true));
                }
            }
            else if (!string.IsNullOrEmpty(defaultSubtitle)
                     && !string.Equals(defaultSubtitle, userData.UID, StringComparison.OrdinalIgnoreCase))
            {
                secondaryHeaderLines.Add((defaultSubtitle, false, true));
            }

            var bannerScrollOffset = new Vector2(ImGui.GetScrollX(), ImGui.GetScrollY());
            var bannerMin = windowPos - bannerScrollOffset;
            var bannerMax = bannerMin + new Vector2(windowSize.X, bannerHeight);

            if (bannerTexture != null)
            {
                drawList.AddImage(
                    bannerTexture.Handle,
                    bannerMin,
                    bannerMax);
            }
            else
            {
                var headerBase = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.Header));
                var topColor = ResolveThemeColor("ProfileBodyGradientTop", Vector4.Lerp(headerBase, Vector4.One, 0.25f));
                topColor.W = 1f;
                var bottomColor = ResolveThemeColor("ProfileBodyGradientBottom", Vector4.Lerp(headerBase, Vector4.Zero, 0.35f));
                bottomColor.W = 1f;

                drawList.AddRectFilledMultiColor(
                    bannerMin,
                    bannerMax,
                    ImGui.ColorConvertFloat4ToU32(topColor),
                    ImGui.ColorConvertFloat4ToU32(topColor),
                    ImGui.ColorConvertFloat4ToU32(bottomColor),
                    ImGui.ColorConvertFloat4ToU32(bottomColor));
            }

            if (_supporterTextureWrap != null)
            {
                const float iconBaseSize = 40f;
                var iconPadding = new Vector2(style.WindowPadding.X + 18f * scale, style.WindowPadding.Y + 18f * scale);
                var textureWidth = MathF.Max(1f, _supporterTextureWrap.Width);
                var textureHeight = MathF.Max(1f, _supporterTextureWrap.Height);
                var textureMaxEdge = MathF.Max(textureWidth, textureHeight);
                var iconScale = (iconBaseSize * scale) / textureMaxEdge;
                var iconSize = new Vector2(textureWidth * iconScale, textureHeight * iconScale);
                var iconMax = bannerMax - iconPadding;
                var iconMin = iconMax - iconSize;
                var backgroundPadding = 6f * scale;
                var iconBackgroundMin = iconMin - new Vector2(backgroundPadding);
                var iconBackgroundMax = iconMax + new Vector2(backgroundPadding);
                var backgroundColor = new Vector4(0f, 0f, 0f, 0.65f);
                var cornerRadius = MathF.Max(4f * scale, iconSize.Y * 0.25f);

                drawList.AddRectFilled(iconBackgroundMin, iconBackgroundMax, ImGui.GetColorU32(backgroundColor), cornerRadius);
                drawList.AddImage(_supporterTextureWrap.Handle, iconMin, iconMax);
            }

            var contentStartY = MathF.Max(style.WindowPadding.Y, bannerHeight - portraitOverlap);
            var topAreaStart = ImGui.GetCursorPos();

            var portraitBackgroundPadding = 12f * scale;
            var portraitAreaSize = portraitFrameSize + new Vector2(portraitBackgroundPadding * 2f);
            var portraitAreaPos = new Vector2(style.WindowPadding.X + portraitOffsetX - portraitBackgroundPadding, contentStartY - portraitBackgroundPadding - 24f * scale);

            ImGui.SetCursorPos(portraitAreaPos);
            var portraitAreaScreenPos = ImGui.GetCursorScreenPos();
            ImGui.Dummy(portraitAreaSize);

            var portraitAreaMin = portraitAreaScreenPos;
            var portraitAreaMax = portraitAreaMin + portraitAreaSize;
            var portraitFrameMin = portraitAreaMin + new Vector2(portraitBackgroundPadding);
            var portraitFrameMax = portraitFrameMin + portraitFrameSize;

            var portraitAreaColor = style.Colors[(int)ImGuiCol.WindowBg];
            portraitAreaColor.W = MathF.Min(1f, portraitAreaColor.W + 0.2f);
            drawList.AddRectFilled(portraitAreaMin, portraitAreaMax, ImGui.GetColorU32(portraitAreaColor), portraitRounding + portraitBorder + portraitBackgroundPadding);

            var portraitFrameBorder = style.Colors[(int)ImGuiCol.Border];

            if (_textureWrap != null)
            {
                drawList.AddImageRounded(
                    _textureWrap.Handle,
                    portraitFrameMin + new Vector2(portraitBorder, portraitBorder),
                    portraitFrameMax - new Vector2(portraitBorder, portraitBorder),
                    Vector2.Zero,
                    Vector2.One,
                    0xFFFFFFFF,
                    portraitRounding);
            }
            else
            {
                drawList.AddRect(
                    portraitFrameMin + new Vector2(portraitBorder, portraitBorder),
                    portraitFrameMax - new Vector2(portraitBorder, portraitBorder),
                    ImGui.GetColorU32(portraitFrameBorder),
                    portraitRounding);
            }

            var portraitAreaLocalMin = portraitAreaMin - windowPos;
            var portraitAreaLocalMax = portraitAreaMax - windowPos;
            var portraitFrameLocalMin = portraitFrameMin - windowPos;
            var portraitFrameLocalMax = portraitFrameMax - windowPos;
            var portraitBlockBottom = windowPos.Y + portraitAreaLocalMax.Y;

            var infoStartY = MathF.Max(contentStartY, bannerHeight + style.WindowPadding.Y);
            var aliasColumnX = infoOffsetX + 18f * scale;
            ImGui.SetCursorPos(new Vector2(aliasColumnX, infoStartY));

            ImGui.BeginGroup();
            using (_uiSharedService.UidFont.Push())
            {
                if (useVanityColors)
                {
                    var seString = SeStringUtils.BuildFormattedPlayerName(primaryHeaderText, vanityTextColor, vanityGlowColor);
                    SeStringUtils.RenderSeStringWithHitbox(seString, ImGui.GetCursorScreenPos(), ImGui.GetFont());
                }
                else
                {
                    ImGui.TextUnformatted(primaryHeaderText);
                }
            }

            foreach (var (text, useColor, disabled) in secondaryHeaderLines)
            {
                if (useColor && useVanityColors)
                {
                    var seString = SeStringUtils.BuildFormattedPlayerName(text, vanityTextColor, vanityGlowColor);
                    SeStringUtils.RenderSeStringWithHitbox(seString, ImGui.GetCursorScreenPos(), ImGui.GetFont());
                }
                else
                {
                    if (disabled)
                        ImGui.TextDisabled(text);
                    else
                        ImGui.TextUnformatted(text);
                }
            }
            ImGui.EndGroup();
            var namesEnd = ImGui.GetCursorPos();

            var namesBlockBottom = windowPos.Y + namesEnd.Y;
            var aliasGroupRectMin = ImGui.GetItemRectMin();
            var aliasGroupRectMax = ImGui.GetItemRectMax();
            var aliasGroupLocalMin = aliasGroupRectMin - windowPos;
            var aliasGroupLocalMax = aliasGroupRectMax - windowPos;

            var tagsStartLocal = new Vector2(aliasGroupLocalMax.X + style.ItemSpacing.X + 25f * scale, aliasGroupLocalMin.Y + style.FramePadding.Y + 2f * scale);
            ImGui.SetCursorPos(tagsStartLocal);
            RenderProfileTags(profileTags, scale);
            var tagsEndLocal = ImGui.GetCursorPos();
            var tagsBlockBottom = windowPos.Y + tagsEndLocal.Y;
            var aliasBlockBottom = windowPos.Y + aliasGroupLocalMax.Y;
            var aliasAndTagsBottomLocal = MathF.Max(aliasGroupLocalMax.Y, tagsEndLocal.Y);
            var aliasAndTagsBlockBottom = MathF.Max(aliasBlockBottom, tagsBlockBottom);

            var descriptionPreSpacing = style.ItemSpacing.Y * 1.35f;
            var descriptionStartLocal = new Vector2(aliasColumnX, aliasAndTagsBottomLocal + descriptionPreSpacing);
            var horizontalInset = style.ItemSpacing.X * 0.5f;
            var descriptionSeparatorSpacing = style.ItemSpacing.Y * 0.5f;
            var descriptionSeparatorThickness = MathF.Max(1f, scale);
            var descriptionSeparatorStart = windowPos + new Vector2(aliasColumnX - horizontalInset, descriptionStartLocal.Y);
            var descriptionSeparatorEnd = new Vector2(windowPos.X + windowSize.X - style.WindowPadding.X + horizontalInset, descriptionSeparatorStart.Y);
            drawList.AddLine(descriptionSeparatorStart, descriptionSeparatorEnd, ImGui.GetColorU32(portraitFrameBorder), descriptionSeparatorThickness);

            var descriptionContentStartLocal = descriptionStartLocal + new Vector2(0f, descriptionSeparatorThickness + descriptionSeparatorSpacing + style.FramePadding.Y * 0.75f);
            ImGui.SetCursorPos(descriptionContentStartLocal);
            ImGui.TextDisabled("Description");
            ImGui.SetCursorPosX(aliasColumnX);
            var descriptionRegionWidth = ImGui.GetContentRegionAvail().X;
            if (descriptionRegionWidth <= 0f)
                descriptionRegionWidth = 1f;
            var measurementWrapWidth = MathF.Max(1f, descriptionRegionWidth - style.WindowPadding.X * 2f);
            var hasDescription = !string.IsNullOrWhiteSpace(profile.Description);
            float descriptionContentHeight;
            float lineHeightWithSpacing;
            using (_uiSharedService.GameFont.Push())
            {
                lineHeightWithSpacing = ImGui.GetTextLineHeightWithSpacing();
                var measurementText = hasDescription
                    ? NormalizeDescriptionForMeasurement(profile.Description!)
                    : UserDescriptionPlaceholder;
                if (string.IsNullOrWhiteSpace(measurementText))
                    measurementText = UserDescriptionPlaceholder;

                descriptionContentHeight = ImGui.CalcTextSize(measurementText, wrapWidth: measurementWrapWidth).Y;
                if (descriptionContentHeight <= 0f)
                    descriptionContentHeight = lineHeightWithSpacing;
            }

            var maxDescriptionHeight = lineHeightWithSpacing * DescriptionMaxVisibleLines;
            var descriptionChildHeight = Math.Clamp(descriptionContentHeight, lineHeightWithSpacing, maxDescriptionHeight);

            RenderDescriptionChild(
                "##StandaloneProfileDescription",
                new Vector2(descriptionRegionWidth, descriptionChildHeight),
                hasDescription ? profile.Description : null,
                UserDescriptionPlaceholder);

            var descriptionEndLocal = ImGui.GetCursorPos();
            var descriptionBlockBottom = windowPos.Y + descriptionEndLocal.Y;
            aliasAndTagsBottomLocal = MathF.Max(aliasAndTagsBottomLocal, descriptionEndLocal.Y);
            aliasAndTagsBlockBottom = MathF.Max(aliasAndTagsBlockBottom, descriptionBlockBottom);

            var presenceLabelSpacing = style.ItemSpacing.Y * 0.35f;
            var presenceAnchorY = MathF.Max(portraitFrameLocalMax.Y, aliasGroupLocalMax.Y);
            var presenceStartLocal = new Vector2(
                portraitFrameLocalMin.X,
                presenceAnchorY + presenceLabelSpacing);
            ImGui.SetCursorPos(presenceStartLocal);
            ImGui.TextDisabled("Presence");
            ImGui.SetCursorPosX(portraitFrameLocalMin.X);
            if (presenceTokens.Count > 0)
            {
                var presenceColumnWidth = MathF.Max(1f, aliasColumnX - portraitFrameLocalMin.X - style.ItemSpacing.X);
                RenderPresenceTokens(presenceTokens, scale, presenceColumnWidth);
            }
            else
            {
                ImGui.SetCursorPosX(portraitFrameLocalMin.X);
                ImGui.TextDisabled("-- No presence information --");
                ImGui.SetCursorPosX(portraitFrameLocalMin.X);
                ImGui.Dummy(new Vector2(0f, style.ItemSpacing.Y * 0.25f));
            }

            var presenceContentEnd = ImGui.GetCursorPos();
            var separatorSpacing = style.ItemSpacing.Y * 0.2f;
            var separatorThickness = MathF.Max(1f, scale);
            var separatorStartLocal = new Vector2(portraitFrameLocalMin.X, presenceContentEnd.Y + separatorSpacing);
            var separatorStart = windowPos + separatorStartLocal;
            var separatorEnd = new Vector2(portraitFrameMax.X, separatorStart.Y);
            drawList.AddLine(separatorStart, separatorEnd, ImGui.GetColorU32(portraitFrameBorder), separatorThickness);
            var afterSeparatorLocal = separatorStartLocal + new Vector2(0f, separatorThickness + separatorSpacing * 0.75f);

            var columnStartLocalY = afterSeparatorLocal.Y;
            var leftColumnX = portraitFrameLocalMin.X;
            var leftWrapPos = windowPos.X + aliasColumnX - style.ItemSpacing.X;

            ImGui.SetCursorPos(new Vector2(leftColumnX, columnStartLocalY));
            float leftColumnEndY = columnStartLocalY;

            if (!string.IsNullOrEmpty(noteText))
            {
                ImGui.TextDisabled("Notes");
                ImGui.SetCursorPosX(leftColumnX);
                ImGui.PushTextWrapPos(leftWrapPos);
                ImGui.TextUnformatted(noteText);
                ImGui.PopTextWrapPos();
                ImGui.SetCursorPos(new Vector2(leftColumnX, ImGui.GetCursorPosY() + style.ItemSpacing.Y * 0.5f));
                leftColumnEndY = ImGui.GetCursorPosY();
            }

            leftColumnEndY = MathF.Max(leftColumnEndY, ImGui.GetCursorPosY());

            var columnsBottomLocal = leftColumnEndY;
            var columnsBottom = windowPos.Y + columnsBottomLocal;
            var topAreaBase = windowPos.Y + topAreaStart.Y;
            var contentBlockBottom = MathF.Max(columnsBottom, aliasAndTagsBlockBottom);
            var leftBlockBottom = MathF.Max(portraitBlockBottom, contentBlockBottom);
            var topAreaHeight = leftBlockBottom - topAreaBase;
            if (topAreaHeight < 0f)
                topAreaHeight = 0f;

            ImGui.SetCursorPos(new Vector2(leftColumnX, topAreaStart.Y + topAreaHeight + style.ItemSpacing.Y));

            var finalCursorY = ImGui.GetCursorPosY();
            var paddingY = ImGui.GetStyle().WindowPadding.Y;
            var computedHeight = finalCursorY + paddingY;
            var adjustedHeight = Math.Clamp(computedHeight, minHeight, maxAllowedHeight);
            _lastComputedWindowHeight = adjustedHeight;

            var finalSize = new Vector2(baseWidth, adjustedHeight);
            Size = finalSize;
            ImGui.SetWindowSize(finalSize, ImGuiCond.Always);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Error during standalone profile draw");
        }
    }

    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 RenderDescriptionChild(
        string childId,
        Vector2 childSize,
        string? description,
        string placeholderText)
    {
        ImGui.PushStyleVar(ImGuiStyleVar.ChildBorderSize, 0f);
        if (ImGui.BeginChild(childId, childSize, false))
        {
            using (_uiSharedService.GameFont.Push())
            {
                ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + ImGui.GetContentRegionAvail().X);
                if (string.IsNullOrWhiteSpace(description))
                {
                    ImGui.TextUnformatted(placeholderText);
                }
                else if (!SeStringUtils.TryRenderSeStringMarkupAtCursor(description))
                {
                    ImGui.TextUnformatted(description);
                }
                ImGui.PopTextWrapPos();
            }
        }
        ImGui.EndChild();
        ImGui.PopStyleVar();
    }

    private void RenderProfileTags(IReadOnlyList<ProfileTagDefinition> tags, float scale)
    {
        if (tags.Count == 0)
        {
            ImGui.TextDisabled("-- No tags set --");
            return;
        }

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

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

        float cursorX = startScreen.X;
        float cursorY = startScreen.Y;
        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);
            var tagWidth = tagSize.X;
            var tagHeight = tagSize.Y;

            if (cursorX > startScreen.X && cursorX + tagWidth > startScreen.X + availableWidth)
            {
                cursorX = startScreen.X;
                cursorY += rowHeight + style.ItemSpacing.Y;
                rowHeight = 0f;
            }

            var tagPos = new Vector2(cursorX, cursorY);
            ImGui.SetCursorScreenPos(tagPos);
            ImGui.InvisibleButton($"##profileTag_{i}", tagSize);
            ProfileTagRenderer.RenderTag(tag, tagPos, scale, drawList, style, _tagBackgroundColor, _tagBorderColor, defaultTextColorU32, _seResolvedSegments, GetIconWrap, _logger);

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

        var totalHeight = (cursorY + rowHeight) - startScreen.Y;
        if (totalHeight < 0f)
            totalHeight = 0f;

        ImGui.SetCursorPos(new Vector2(startLocal.X, startLocal.Y + totalHeight));
    }

    private void DrawGroupProfileWindow()
    {
        if (_groupData is null)
            return;

        var scale = ImGuiHelpers.GlobalScale;
        var viewport = ImGui.GetMainViewport();
        var linked = ProfileEditorLayoutCoordinator.IsActive(_groupData.GID);
        var baseSize = ProfileEditorLayoutCoordinator.GetProfileSize(scale);
        var baseWidth = baseSize.X;
        var minHeight = baseSize.Y;
        var maxAllowedHeight = minHeight * MaxHeightMultiplier;
        var targetHeight = _lastComputedWindowHeight > 0f
            ? Math.Clamp(_lastComputedWindowHeight, minHeight, maxAllowedHeight)
            : minHeight;
        var desiredSize = new Vector2(baseWidth, targetHeight);
        Size = desiredSize;

        if (linked)
        {
            ProfileEditorLayoutCoordinator.EnsureAnchor(viewport.WorkPos, scale);

            var currentPos = ImGui.GetWindowPos();
            if (IsWindowBeingDragged())
                ProfileEditorLayoutCoordinator.UpdateAnchorFromProfile(currentPos);

            var desiredPos = ProfileEditorLayoutCoordinator.GetProfilePosition(scale);
            if (!ProfileEditorLayoutCoordinator.NearlyEquals(currentPos, desiredPos))
                ImGui.SetWindowPos(desiredPos, ImGuiCond.Always);

            if (!ProfileEditorLayoutCoordinator.NearlyEquals(ImGui.GetWindowSize(), desiredSize))
                ImGui.SetWindowSize(desiredSize, ImGuiCond.Always);
        }
        else
        {
            var defaultPosition = viewport.WorkPos + (new Vector2(50f, 70f) * scale);
            ImGui.SetWindowPos(defaultPosition, ImGuiCond.FirstUseEver);
            ImGui.SetWindowSize(desiredSize, ImGuiCond.Always);
        }

        var profile = _lightlessProfileManager.GetLightlessGroupProfile(_groupData);
        IReadOnlyList<ProfileTagDefinition> profileTags = profile.Tags.Count > 0
            ? ProfileTagService.ResolveTags(profile.Tags)
            : Array.Empty<ProfileTagDefinition>();

        if (_textureWrap == null || !profile.ProfileImageData.Value.SequenceEqual(_lastProfilePicture))
        {
            _textureWrap?.Dispose();
            _textureWrap = null;
            _lastProfilePicture = profile.ProfileImageData.Value;
            ResetBannerTexture();
            if (_lastProfilePicture.Length > 0)
            {
                _textureWrap = _uiSharedService.LoadImage(_lastProfilePicture);
            }
        }

        if (_supporterTextureWrap != null)
        {
            _supporterTextureWrap.Dispose();
            _supporterTextureWrap = null;
        }
        _lastSupporterPicture = Array.Empty<byte>();

        var bannerBytes = profile.BannerImageData.Value;
        if (!_lastBannerPicture.SequenceEqual(bannerBytes))
        {
            ResetBannerTexture();
            _lastBannerPicture = bannerBytes;
        }

        var noteText = _serverManager.GetNoteForGid(_groupData.GID);

        var presenceTokens = new List<PresenceToken>
        {
            new(profile.IsDisabled ? "Disabled" : "Active", profile.IsDisabled)
        };

        if (profile.IsNsfw)
            presenceTokens.Add(new PresenceToken("NSFW", Emphasis: true));

        int memberCount = 0;
        List<Pair>? groupMembers = null;
        var snapshot = _pairUiService.GetSnapshot();
        GroupFullInfoDto? groupInfo = null;
        if (_groupData is not null && snapshot.GroupsByGid.TryGetValue(_groupData.GID, out var refreshedGroupInfo))
        {
            groupInfo = refreshedGroupInfo;
        }
        if (groupInfo is not null && snapshot.GroupPairs.TryGetValue(groupInfo, out var pairsForGroup))
        {
            groupMembers = pairsForGroup.ToList();
            memberCount = groupMembers.Count;
        }
        else if (groupInfo?.GroupPairUserInfos is { Count: > 0 })
        {
            memberCount = groupInfo.GroupPairUserInfos.Count;
        }

        string memberLabel = memberCount == 1 ? "1 Member" : $"{memberCount} Members";
        presenceTokens.Add(new PresenceToken(memberLabel, false));

        if (groupInfo?.GroupPermissions.IsDisableInvites() ?? false)
        {
            presenceTokens.Add(new PresenceToken(
                "Invites Locked",
                true,
                new[]
                {
                    "New members cannot join while this lock is active."
                },
                "Syncshell Status"));
        }

        var drawList = ImGui.GetWindowDrawList();
        var style = ImGui.GetStyle();
        var windowPos = ImGui.GetWindowPos();
        var windowSize = ImGui.GetWindowSize();
        var bannerHeight = 260f * scale;
        var portraitSize = new Vector2(180f, 180f) * scale;
        var portraitBorder = 1.5f * scale;
        var portraitRounding = 1f * scale;
        var portraitFrameSize = portraitSize + new Vector2(portraitBorder * 2f);
        var portraitOverlap = portraitSize.Y * 0.35f;
        var portraitOffsetX = 35f * scale;
        var infoOffsetX = portraitOffsetX + portraitFrameSize.X + style.ItemSpacing.X * 2f;
        var bannerTexture = GetBannerTexture(_lastBannerPicture) ?? _textureWrap;

        var bannerScrollOffset = new Vector2(ImGui.GetScrollX(), ImGui.GetScrollY());
        var bannerMin = windowPos - bannerScrollOffset;
        var bannerMax = bannerMin + new Vector2(windowSize.X, bannerHeight);

        if (bannerTexture != null)
        {
            drawList.AddImage(
                bannerTexture.Handle,
                bannerMin,
                bannerMax);
        }
        else
        {
            var headerBase = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.Header));
            var topColor = ResolveThemeColor("ProfileBodyGradientTop", Vector4.Lerp(headerBase, Vector4.One, 0.25f));
            topColor.W = 1f;
            var bottomColor = ResolveThemeColor("ProfileBodyGradientBottom", Vector4.Lerp(headerBase, Vector4.Zero, 0.35f));
            bottomColor.W = 1f;

            drawList.AddRectFilledMultiColor(
                bannerMin,
                bannerMax,
                ImGui.ColorConvertFloat4ToU32(topColor),
                ImGui.ColorConvertFloat4ToU32(topColor),
                ImGui.ColorConvertFloat4ToU32(bottomColor),
                ImGui.ColorConvertFloat4ToU32(bottomColor));
        }

        var contentStartY = MathF.Max(style.WindowPadding.Y, bannerHeight - portraitOverlap);
        var topAreaStart = ImGui.GetCursorPos();

        var portraitBackgroundPadding = 12f * scale;
        var portraitAreaSize = portraitFrameSize + new Vector2(portraitBackgroundPadding * 2f);
        var portraitAreaPos = new Vector2(style.WindowPadding.X + portraitOffsetX - portraitBackgroundPadding, contentStartY - portraitBackgroundPadding - 24f * scale);

        ImGui.SetCursorPos(portraitAreaPos);
        var portraitAreaScreenPos = ImGui.GetCursorScreenPos();
        ImGui.Dummy(portraitAreaSize);
        var contentStart = ImGui.GetCursorPos();

        var portraitAreaMin = portraitAreaScreenPos;
        var portraitAreaMax = portraitAreaMin + portraitAreaSize;
        var portraitFrameMin = portraitAreaMin + new Vector2(portraitBackgroundPadding);
        var portraitFrameMax = portraitFrameMin + portraitFrameSize;

        var portraitAreaColor = style.Colors[(int)ImGuiCol.WindowBg];
        portraitAreaColor.W = MathF.Min(1f, portraitAreaColor.W + 0.2f);
        drawList.AddRectFilled(portraitAreaMin, portraitAreaMax, ImGui.GetColorU32(portraitAreaColor), portraitRounding + portraitBorder + portraitBackgroundPadding);
        var portraitFrameBorder = style.Colors[(int)ImGuiCol.Border];

        if (_textureWrap != null)
        {
            drawList.AddImageRounded(
                _textureWrap.Handle,
                portraitFrameMin + new Vector2(portraitBorder, portraitBorder),
                portraitFrameMax - new Vector2(portraitBorder, portraitBorder),
                Vector2.Zero,
                Vector2.One,
                0xFFFFFFFF,
                portraitRounding);
        }
        else
        {
            drawList.AddRect(
                portraitFrameMin + new Vector2(portraitBorder, portraitBorder),
                portraitFrameMax - new Vector2(portraitBorder, portraitBorder),
                ImGui.GetColorU32(portraitFrameBorder),
                portraitRounding);
        }

        drawList.AddRect(portraitFrameMin, portraitFrameMax, ImGui.GetColorU32(portraitFrameBorder), portraitRounding);
        var portraitAreaLocalMax = portraitAreaMax - windowPos;
        var portraitFrameLocalMin = portraitFrameMin - windowPos;
        var portraitFrameLocalMax = portraitFrameMax - windowPos;
        var portraitBlockBottom = windowPos.Y + portraitAreaLocalMax.Y;

        ImGui.SetCursorPos(contentStart);

        bool useVanityColors = false;
        Vector4? vanityTextColor = null;
        Vector4? vanityGlowColor = null;
        string primaryHeaderText = _groupData.AliasOrGID;

        List<(string Text, bool UseVanityColor, bool Disabled)> secondaryHeaderLines =
        [
        (_groupData.GID, false, true)
        ];

        if (groupInfo is not null)
            secondaryHeaderLines.Add(($"Owner: {groupInfo.Owner.AliasOrUID}", false, true));
        else
            secondaryHeaderLines.Add(($"Unknown Owner", false, true));

        var infoStartY = MathF.Max(contentStartY, bannerHeight + style.WindowPadding.Y);
        var aliasColumnX = infoOffsetX + 18f * scale;
        ImGui.SetCursorPos(new Vector2(aliasColumnX, infoStartY));

        ImGui.BeginGroup();
        using (_uiSharedService.UidFont.Push())
        {
            ImGui.TextUnformatted(primaryHeaderText);
        }

        foreach (var (text, useColor, disabled) in secondaryHeaderLines)
        {
            if (useColor && useVanityColors)
            {
                var seString = SeStringUtils.BuildFormattedPlayerName(text, vanityTextColor, vanityGlowColor);
                SeStringUtils.RenderSeStringWithHitbox(seString, ImGui.GetCursorScreenPos(), ImGui.GetFont());
            }
            else
            {
                if (disabled)
                    ImGui.TextDisabled(text);
                else
                    ImGui.TextUnformatted(text);
            }
        }
        ImGui.EndGroup();
        var namesEnd = ImGui.GetCursorPos();

        var aliasGroupRectMin = ImGui.GetItemRectMin();
        var aliasGroupRectMax = ImGui.GetItemRectMax();
        var aliasGroupLocalMin = aliasGroupRectMin - windowPos;
        var aliasGroupLocalMax = aliasGroupRectMax - windowPos;

        var tagsStartLocal = new Vector2(aliasGroupLocalMax.X + style.ItemSpacing.X + 25f * scale, aliasGroupLocalMin.Y + style.FramePadding.Y + 2f * scale);
        ImGui.SetCursorPos(tagsStartLocal);
        if (profileTags.Count > 0)
            RenderProfileTags(profileTags, scale);
        else
            ImGui.TextDisabled("-- No tags set --");
        var tagsEndLocal = ImGui.GetCursorPos();
        var tagsBlockBottom = windowPos.Y + tagsEndLocal.Y;
        var aliasBlockBottom = windowPos.Y + aliasGroupLocalMax.Y;
        var aliasAndTagsBottomLocal = MathF.Max(aliasGroupLocalMax.Y, tagsEndLocal.Y);
        var aliasAndTagsBlockBottom = MathF.Max(aliasBlockBottom, tagsBlockBottom);

        var descriptionSeparatorSpacing = style.ItemSpacing.Y * 0.35f;
        var descriptionSeparatorThickness = MathF.Max(1f, scale);
        var descriptionExtraOffset = 0f;
        if (groupInfo?.Owner is not null)
            descriptionExtraOffset = style.ItemSpacing.Y * 0.6f;
        var descriptionStartLocal = new Vector2(aliasColumnX, aliasAndTagsBottomLocal + descriptionSeparatorSpacing + descriptionExtraOffset);
        var horizontalInset = style.ItemSpacing.X * 0.5f;
        var descriptionSeparatorStart = windowPos + new Vector2(aliasColumnX - horizontalInset, descriptionStartLocal.Y);
        var descriptionSeparatorEnd = new Vector2(windowPos.X + windowSize.X - style.WindowPadding.X + horizontalInset, descriptionSeparatorStart.Y);
        drawList.AddLine(descriptionSeparatorStart, descriptionSeparatorEnd, ImGui.GetColorU32(portraitFrameBorder), descriptionSeparatorThickness);

        var descriptionContentStartLocal = new Vector2(aliasColumnX, descriptionStartLocal.Y + descriptionSeparatorThickness + descriptionSeparatorSpacing + style.FramePadding.Y * 0.75f);
        ImGui.SetCursorPos(descriptionContentStartLocal);
        ImGui.TextDisabled("Description");
        ImGui.SetCursorPosX(aliasColumnX);
        var descriptionRegionWidth = ImGui.GetContentRegionAvail().X;
        if (descriptionRegionWidth <= 0f)
            descriptionRegionWidth = 1f;
        var measurementWrapWidth = MathF.Max(1f, descriptionRegionWidth - style.WindowPadding.X * 2f);
        var hasDescription = !string.IsNullOrWhiteSpace(profile.Description);
        float descriptionContentHeight;
        float lineHeightWithSpacing;
        using (_uiSharedService.GameFont.Push())
        {
            lineHeightWithSpacing = ImGui.GetTextLineHeightWithSpacing();
            var measurementText = hasDescription
                ? NormalizeDescriptionForMeasurement(profile.Description!)
                : GroupDescriptionPlaceholder;
            if (string.IsNullOrWhiteSpace(measurementText))
                measurementText = GroupDescriptionPlaceholder;

            descriptionContentHeight = ImGui.CalcTextSize(measurementText, wrapWidth: measurementWrapWidth).Y;
            if (descriptionContentHeight <= 0f)
                descriptionContentHeight = lineHeightWithSpacing;
        }

        var maxDescriptionHeight = lineHeightWithSpacing * DescriptionMaxVisibleLines;
        var descriptionChildHeight = Math.Clamp(descriptionContentHeight, lineHeightWithSpacing, maxDescriptionHeight);

        RenderDescriptionChild(
            "##StandaloneGroupDescription",
            new Vector2(descriptionRegionWidth, descriptionChildHeight),
            hasDescription ? profile.Description : null,
            GroupDescriptionPlaceholder);
        var descriptionEndLocal = ImGui.GetCursorPos();
        var descriptionBlockBottom = windowPos.Y + descriptionEndLocal.Y;
        aliasAndTagsBottomLocal = MathF.Max(aliasAndTagsBottomLocal, descriptionEndLocal.Y);
        aliasAndTagsBlockBottom = MathF.Max(aliasAndTagsBlockBottom, descriptionBlockBottom);

        var presenceLabelSpacing = style.ItemSpacing.Y * 0.35f;
        var presenceAnchorY = MathF.Max(portraitFrameLocalMax.Y, aliasGroupLocalMax.Y);
        var presenceStartLocal = new Vector2(portraitFrameLocalMin.X, presenceAnchorY + presenceLabelSpacing);
        ImGui.SetCursorPos(presenceStartLocal);
        ImGui.TextDisabled("Presence");
        ImGui.SetCursorPosX(portraitFrameLocalMin.X);
        if (presenceTokens.Count > 0)
        {
            var presenceColumnWidth = MathF.Max(1f, aliasColumnX - portraitFrameLocalMin.X - style.ItemSpacing.X);
            RenderPresenceTokens(presenceTokens, scale, presenceColumnWidth);
        }
        else
        {
            ImGui.TextDisabled("-- No status flags --");
            ImGui.Dummy(new Vector2(0f, style.ItemSpacing.Y * 0.25f));
        }

        var presenceContentEnd = ImGui.GetCursorPos();
        var separatorSpacing = style.ItemSpacing.Y * 0.2f;
        var separatorThickness = MathF.Max(1f, scale);
        var separatorStartLocal = new Vector2(portraitFrameLocalMin.X, presenceContentEnd.Y + separatorSpacing);
        var separatorStart = windowPos + separatorStartLocal;
        var separatorEnd = new Vector2(portraitFrameMax.X, separatorStart.Y);
        drawList.AddLine(separatorStart, separatorEnd, ImGui.GetColorU32(portraitFrameBorder), separatorThickness);
        var afterSeparatorLocal = separatorStartLocal + new Vector2(0f, separatorThickness + separatorSpacing * 0.75f);

        var columnStartLocalY = afterSeparatorLocal.Y;
        var leftColumnX = portraitFrameLocalMin.X;
        ImGui.SetCursorPos(new Vector2(leftColumnX, columnStartLocalY));
        float leftColumnEndY = columnStartLocalY;

        if (!string.IsNullOrEmpty(noteText))
        {
            ImGui.TextDisabled("Notes");
            ImGui.SetCursorPosX(leftColumnX);
            ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + ImGui.GetContentRegionAvail().X);
            ImGui.TextUnformatted(noteText);
            ImGui.PopTextWrapPos();
            ImGui.SetCursorPos(new Vector2(leftColumnX, ImGui.GetCursorPosY() + style.ItemSpacing.Y * 0.5f));
            leftColumnEndY = ImGui.GetCursorPosY();
        }

        leftColumnEndY = MathF.Max(leftColumnEndY, ImGui.GetCursorPosY());

        var columnsBottomLocal = leftColumnEndY;
        var columnsBottom = windowPos.Y + columnsBottomLocal;
        var topAreaBase = windowPos.Y + topAreaStart.Y;
        var contentBlockBottom = MathF.Max(columnsBottom, aliasAndTagsBlockBottom);
        var leftBlockBottom = MathF.Max(portraitBlockBottom, contentBlockBottom);
        var topAreaHeight = leftBlockBottom - topAreaBase;
        if (topAreaHeight < 0f)
            topAreaHeight = 0f;

        ImGui.SetCursorPos(new Vector2(leftColumnX, topAreaStart.Y + topAreaHeight + style.ItemSpacing.Y));

        var finalCursorY = ImGui.GetCursorPosY();
        var paddingY = ImGui.GetStyle().WindowPadding.Y;
        var computedHeight = finalCursorY + paddingY;
        var adjustedHeight = Math.Clamp(computedHeight, minHeight, maxAllowedHeight);
        _lastComputedWindowHeight = adjustedHeight;

        var finalSize = new Vector2(baseWidth, adjustedHeight);
        Size = finalSize;
        ImGui.SetWindowSize(finalSize, ImGuiCond.Always);
    }

    private IDalamudTextureWrap? GetBannerTexture(byte[] bannerBytes)
    {
        if (_bannerTextureLoaded)
            return _bannerTextureWrap;

        _bannerTextureLoaded = true;

        if (bannerBytes.Length == 0)
            return null;

        _bannerTextureWrap = _uiSharedService.LoadImage(bannerBytes);
        return _bannerTextureWrap;
    }

    private void ResetBannerTexture()
    {
        _bannerTextureWrap?.Dispose();
        _bannerTextureWrap = null;
        _lastBannerPicture = [];
        _bannerTextureLoaded = false;
    }

    private static bool IsWindowBeingDragged()
    {
        return ImGui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows) && ImGui.GetIO().MouseDown[0];
    }

    private static string NormalizeDescriptionForMeasurement(string description)
    {
        if (string.IsNullOrWhiteSpace(description))
            return string.Empty;

        var normalized = description.ReplaceLineEndings("\n");
        normalized = normalized
            .Replace("<br>", "\n", StringComparison.OrdinalIgnoreCase)
            .Replace("<br/>", "\n", StringComparison.OrdinalIgnoreCase)
            .Replace("<br />", "\n", StringComparison.OrdinalIgnoreCase)
            .Replace("</br>", "\n", StringComparison.OrdinalIgnoreCase);

        return SeStringUtils.StripMarkup(normalized);
    }
    private static void RenderPresenceTokens(IReadOnlyList<PresenceToken> tokens, float scale, float? maxWidth = null)
    {
        if (tokens.Count == 0)
            return;

        var drawList = ImGui.GetWindowDrawList();
        var style = ImGui.GetStyle();

        var startPos = ImGui.GetCursorPos();
        float startX = startPos.X;
        float cursorX = startX;
        float cursorY = startPos.Y;
        float availWidth = maxWidth ?? ImGui.GetContentRegionAvail().X;
        if (availWidth <= 0f)
            availWidth = ImGui.GetContentRegionAvail().X;
        if (availWidth <= 0f)
            availWidth = 1f;
        float spacingX = style.ItemSpacing.X;
        float spacingY = style.ItemSpacing.Y;
        float rounding = style.FrameRounding > 0f ? style.FrameRounding : 6f * scale;

        var padding = new Vector2(8f * scale, 4f * scale);
        var baseColor = new Vector4(0.16f, 0.16f, 0.16f, 0.95f);
        var baseBorder = style.Colors[(int)ImGuiCol.Border];
        baseBorder.W *= 0.35f;
        var alertColor = new Vector4(0.32f, 0.2f, 0.2f, 0.95f);
        var alertBorder = Vector4.Lerp(baseBorder, new Vector4(0.9f, 0.5f, 0.5f, baseBorder.W), 0.6f);
        var textColor = style.Colors[(int)ImGuiCol.Text];

        float rowHeight = 0f;

        for (int i = 0; i < tokens.Count; i++)
        {
            var token = tokens[i];
            var textSize = ImGui.CalcTextSize(token.Text);
            var tagSize = textSize + padding * 2f;

            if (cursorX > startX && cursorX + tagSize.X > startX + availWidth)
            {
                cursorX = startX;
                cursorY += rowHeight + spacingY;
                rowHeight = 0f;
            }

            ImGui.SetCursorPos(new Vector2(cursorX, cursorY));
            ImGui.InvisibleButton($"##presenceTag_{i}", tagSize);

            var tagMin = ImGui.GetItemRectMin();
            var tagMax = ImGui.GetItemRectMax();

            var fillColor = token.Emphasis ? alertColor : baseColor;
            var borderColor = token.Emphasis ? alertBorder : baseBorder;

            drawList.AddRectFilled(tagMin, tagMax, ImGui.GetColorU32(fillColor), rounding);
            drawList.AddRect(tagMin, tagMax, ImGui.GetColorU32(borderColor), rounding);
            drawList.AddText(tagMin + padding, ImGui.GetColorU32(textColor), token.Text);

            if (token.Tooltip is { Count: > 0 })
            {
                if (ImGui.IsItemHovered())
                {
                    ImGui.BeginTooltip();
                    if (!string.IsNullOrEmpty(token.TooltipTitle))
                    {
                        ImGui.TextUnformatted(token.TooltipTitle);
                        ImGui.Separator();
                    }

                    foreach (var line in token.Tooltip)
                        ImGui.TextUnformatted(line);
                    ImGui.EndTooltip();
                }
            }

            cursorX += tagSize.X + spacingX;
            rowHeight = MathF.Max(rowHeight, tagSize.Y);
        }

        ImGui.SetCursorPos(new Vector2(startX, cursorY + rowHeight));
        ImGui.Dummy(new Vector2(0f, spacingY * 0.25f));
    }

    public override void OnClose()
    {
        if (!_isGroupProfile
            && !_isLightfinderContext
            && Pair is null
            && _userData is not null
            && ProfileEditorLayoutCoordinator.IsActive(_userData.UID))
        {
            ProfileEditorLayoutCoordinator.Disable(_userData.UID);
        }
        else if (_isGroupProfile
                 && _groupData is not null
                 && ProfileEditorLayoutCoordinator.IsActive(_groupData.GID))
        {
            ProfileEditorLayoutCoordinator.Disable(_groupData.GID);
        }
        Mediator.Publish(new RemoveWindowMessage(this));
    }

    private readonly record struct PresenceToken(
        string Text,
        bool Emphasis,
        IReadOnlyList<string>? Tooltip = null,
        string? TooltipTitle = null);
}
