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 LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.Profiles;
using LightlessSync.UI.Services;
using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using SharpDX.DirectWrite;
using SixLabors.ImageSharp;
using System.Globalization;
using System.Numerics;

namespace LightlessSync.UI;

public class SyncshellAdminUI : WindowMediatorSubscriberBase
{
    private readonly ApiController _apiController;
    private readonly bool _isModerator = false;
    private readonly bool _isOwner = false;
    private readonly List<string> _oneTimeInvites = [];
    private readonly LightlessProfileManager _lightlessProfileManager;
    private readonly UiSharedService _uiSharedService;
    private readonly PairUiService _pairUiService;
    private List<BannedGroupUserDto> _bannedUsers = [];
    private LightlessGroupProfileData? _profileData = null;
    private string _userSearchFilter = string.Empty;
    private IDalamudTextureWrap? _pfpTextureWrap;
    private string _profileDescription = string.Empty;
    private int _multiInvites;
    private string _newPassword;
    private bool _pwChangeSuccess;
    private Task<int>? _pruneTestTask;
    private Task<int>? _pruneTask;
    private int _pruneDays = 14;

    private Task<GroupPruneSettingsDto>? _pruneSettingsTask;
    private bool _pruneSettingsLoaded;
    private bool _autoPruneEnabled;
    private int _autoPruneDays = 14;

    public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, LightlessMediator mediator, ApiController apiController,
        UiSharedService uiSharedService, PairUiService pairUiService, GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService, LightlessProfileManager lightlessProfileManager)
        : base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
    {
        GroupFullInfo = groupFullInfo;
        _apiController = apiController;
        _uiSharedService = uiSharedService;
        _lightlessProfileManager = lightlessProfileManager;
        _pairUiService = pairUiService;
        _isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
        _isModerator = GroupFullInfo.GroupUserInfo.IsModerator();
        _newPassword = string.Empty;
        _multiInvites = 30;
        _pwChangeSuccess = true;
        IsOpen = true;
        Mediator.Subscribe<ClearProfileGroupDataMessage>(this, (msg) =>
        {
            if (msg.GroupData == null || string.Equals(msg.GroupData.AliasOrGID, GroupFullInfo.Group.AliasOrGID, StringComparison.Ordinal))
            {
                _pfpTextureWrap?.Dispose();
                _pfpTextureWrap = null;
            }
        });
        SizeConstraints = new WindowSizeConstraints()
        {
            MinimumSize = new(700, 500),
            MaximumSize = new(700, 2000),
        };
        _pairUiService = pairUiService;
    }

    public GroupFullInfoDto GroupFullInfo { get; private set; }

    protected override void DrawInternal()
    {
        if (!_isModerator && !_isOwner) return;

        _logger.LogTrace("Drawing Syncshell Admin UI for {group}", GroupFullInfo.GroupAliasOrGID);
        var snapshot = _pairUiService.GetSnapshot();
        if (snapshot.GroupsByGid.TryGetValue(GroupFullInfo.Group.GID, out var updatedInfo))
        {
            GroupFullInfo = updatedInfo;
        }

        _profileData = _lightlessProfileManager.GetLightlessGroupProfile(GroupFullInfo.Group);
        using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);

        DrawAdminHeader();

        ImGui.Separator();
        var perm = GroupFullInfo.GroupPermissions;

        DrawAdminTopBar(perm);
    }

    private void DrawAdminHeader()
    {
        float scale = ImGuiHelpers.GlobalScale;
        var style = ImGui.GetStyle();

        var cursorLocal = ImGui.GetCursorPos();
        var pMin = ImGui.GetCursorScreenPos();
        float width = ImGui.GetContentRegionAvail().X;
        float height = 64f * scale;

        var pMax = new Vector2(pMin.X + width, pMin.Y + height);
        var drawList = ImGui.GetWindowDrawList();

        var purple = UIColors.Get("LightlessPurple");
        var gradLeft = purple.WithAlpha(0.0f);
        var gradRight = purple.WithAlpha(0.85f);

        uint colTopLeft = ImGui.ColorConvertFloat4ToU32(gradLeft);
        uint colTopRight = ImGui.ColorConvertFloat4ToU32(gradRight);
        uint colBottomRight = ImGui.ColorConvertFloat4ToU32(gradRight);
        uint colBottomLeft = ImGui.ColorConvertFloat4ToU32(gradLeft);

        drawList.AddRectFilledMultiColor(
            pMin,
            pMax,
            colTopLeft,
            colTopRight,
            colBottomRight,
            colBottomLeft);

        float accentHeight = 3f * scale;
        var accentMin = new Vector2(pMin.X, pMax.Y - accentHeight);
        var accentMax = new Vector2(pMax.X, pMax.Y);
        var accentColor = UIColors.Get("LightlessBlue");
        uint accentU32 = ImGui.ColorConvertFloat4ToU32(accentColor);
        drawList.AddRectFilled(accentMin, accentMax, accentU32);

        ImGui.InvisibleButton("##adminHeaderHitbox", new Vector2(width, height));

        if (ImGui.IsItemHovered())
        {
            ImGui.BeginTooltip();
            ImGui.Text($"{GroupFullInfo.GroupAliasOrGID} is created at:");
            ImGui.Separator();
            ImGui.Text(GroupFullInfo.Group.CreatedAt?.ToString("yyyy-MM-dd HH:mm:ss 'UTC'") ?? "Unknown");
            ImGui.EndTooltip();
        }

        var titlePos = new Vector2(pMin.X + 12f * scale, pMin.Y + 8f * scale);
        ImGui.SetCursorScreenPos(titlePos);

        float titleHeight;
        using (_uiSharedService.UidFont.Push())
        {
            ImGui.TextColored(UIColors.Get("LightlessBlue"), GroupFullInfo.GroupAliasOrGID);
            titleHeight = ImGui.GetTextLineHeightWithSpacing();
        }

        var subtitlePos = new Vector2(
            pMin.X + 12f * scale,
            titlePos.Y + titleHeight - 2f * scale);

        ImGui.SetCursorScreenPos(subtitlePos);
        ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
        ImGui.TextUnformatted("Administrative Panel");
        ImGui.PopStyleColor();

        string roleLabel = _isOwner ? "Owner" : (_isModerator ? "Moderator" : string.Empty);
        if (!string.IsNullOrEmpty(roleLabel))
        {
            float roleTextW = ImGui.CalcTextSize(roleLabel).X;
            float pillPaddingX = 8f * scale;
            float pillPaddingY = -1f * scale;

            float pillWidth = roleTextW + pillPaddingX * 2f;
            float pillHeight = ImGui.GetTextLineHeight() + pillPaddingY * 2f;

            var pillMin = new Vector2(
                pMax.X - pillWidth - style.WindowPadding.X,
                subtitlePos.Y - pillPaddingY);
            var pillMax = new Vector2(pillMin.X + pillWidth, pillMin.Y + pillHeight);

            var pillBg = _isOwner ? UIColors.Get("LightlessYellow") : UIColors.Get("LightlessOrange");
            uint pillBgU = ImGui.ColorConvertFloat4ToU32(pillBg.WithAlpha(0.9f));

            drawList.AddRectFilled(pillMin, pillMax, pillBgU, 8f * scale);

            ImGui.SetCursorScreenPos(new Vector2(pillMin.X + pillPaddingX, pillMin.Y + pillPaddingY));
            ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("FullBlack"));
            ImGui.TextUnformatted(roleLabel);
            ImGui.PopStyleColor();
        }

        ImGui.SetCursorPos(new Vector2(cursorLocal.X, cursorLocal.Y + height + 6f * scale));
    }

    private void DrawAdminTopBar(GroupPermissions perm)
    {
        var style = ImGui.GetStyle();

        ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(12f, 6f) * ImGuiHelpers.GlobalScale);
        ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(10f, style.ItemSpacing.Y));
        ImGui.PushStyleVar(ImGuiStyleVar.TabRounding, 6f * ImGuiHelpers.GlobalScale);

        var baseTab = UIColors.Get("FullBlack").WithAlpha(0.0f);
        var baseTabDim = UIColors.Get("FullBlack").WithAlpha(0.1f);
        var accent = UIColors.Get("LightlessPurple");
        var accentHover = accent.WithAlpha(0.90f);
        var accentActive = accent;

        using (ImRaii.PushColor(ImGuiCol.Tab, baseTab))
        using (ImRaii.PushColor(ImGuiCol.TabHovered, accentHover))
        using (ImRaii.PushColor(ImGuiCol.TabActive, accentActive))
        using (ImRaii.PushColor(ImGuiCol.TabUnfocused, baseTabDim))
        using (ImRaii.PushColor(ImGuiCol.TabUnfocusedActive, accentActive.WithAlpha(0.80f)))
        {
            using (var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID))
            {
                if (tabbar)
                {
                    DrawInvites(perm);
                    DrawManagement();
                    DrawPermission(perm);
                    DrawProfile();
                }
            }
        }

        ImGui.PopStyleVar(3);

        ImGuiHelpers.ScaledDummy(2f);
        UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
        ImGuiHelpers.ScaledDummy(2f);
    }

    private void DrawPermission(GroupPermissions perm)
    {
        var permissionTab = ImRaii.TabItem("Permissions");
        if (permissionTab)
        {
            bool isDisableAnimations = perm.IsPreferDisableAnimations();
            bool isDisableSounds = perm.IsPreferDisableSounds();
            bool isDisableVfx = perm.IsPreferDisableVFX();

            ImGui.AlignTextToFramePadding();
            ImGui.Text("Suggest Sound Sync");
            _uiSharedService.BooleanToColoredIcon(!isDisableSounds);
            ImGui.SameLine(230);
            using (ImRaii.PushColor(ImGuiCol.Text, isDisableSounds ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
            {
                if (_uiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute,
                isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync"))
                {
                    perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds());
                    _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
                }
            }

            ImGui.AlignTextToFramePadding();
            ImGui.Text("Suggest Animation Sync");
            _uiSharedService.BooleanToColoredIcon(!isDisableAnimations);
            ImGui.SameLine(230);
            using (ImRaii.PushColor(ImGuiCol.Text, isDisableAnimations ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
            {
                if (_uiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop,
                isDisableAnimations ? "Suggest to enable animation sync" : "Suggest to disable animation sync"))
                {
                    perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations());
                    _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
                }
            }

            ImGui.AlignTextToFramePadding();
            ImGui.Text("Suggest VFX Sync");
            _uiSharedService.BooleanToColoredIcon(!isDisableVfx);
            ImGui.SameLine(230);
            using (ImRaii.PushColor(ImGuiCol.Text, isDisableVfx ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
            {
                if (_uiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle,
                isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync"))
                {
                    perm.SetPreferDisableVFX(!perm.IsPreferDisableVFX());
                    _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
                }
            }

            UiSharedService.TextWrapped("Note: those suggested permissions will be shown to users on joining the Syncshell.");
        }
        permissionTab.Dispose();

        if (_isOwner)
        {
            var ownerTab = ImRaii.TabItem("Owner Settings");
            if (ownerTab)
            {
                bool isChatDisabled = perm.IsDisableChat();
                ImGui.AlignTextToFramePadding();
                ImGui.TextUnformatted("Syncshell Chat");
                _uiSharedService.BooleanToColoredIcon(!isChatDisabled);
                ImGui.SameLine(230);
                using (ImRaii.PushColor(ImGuiCol.Text, isChatDisabled ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
                {
                    if (_uiSharedService.IconTextButton(
                        isChatDisabled ? FontAwesomeIcon.Comment : FontAwesomeIcon.Ban,
                        isChatDisabled ? "Enable syncshell chat" : "Disable syncshell chat"))
                    {
                        perm.SetDisableChat(!isChatDisabled);
                        _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
                    }
                }
                UiSharedService.AttachToolTip("Disables syncshell chat for all members.");

                ImGuiHelpers.ScaledDummy(6f);

                ImGui.AlignTextToFramePadding();
                ImGui.TextUnformatted("New Password");
                var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
                var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password");
                var textSize = ImGui.CalcTextSize("New Password").X;
                var spacing = ImGui.GetStyle().ItemSpacing.X;

                ImGui.SameLine();
                ImGui.SetNextItemWidth(availableWidth - buttonSize - textSize - spacing * 2);
                ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50);
                ImGui.SameLine();
                using (ImRaii.Disabled(_newPassword.Length < 10))
                {
                    if (_uiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password"))
                    {
                        _pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result;
                        _newPassword = string.Empty;
                    }
                }
                UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible.");

                if (!_pwChangeSuccess)
                {
                    UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow);
                }

                if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
                {
                    IsOpen = false;
                    _ = _apiController.GroupDelete(new(GroupFullInfo.Group));
                }
                UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
            }
            ownerTab.Dispose();
        }
    }

    private void DrawAutoPruneSettings()
    {
        ImGuiHelpers.ScaledDummy(2f);
        UiSharedService.TextWrapped("Automatic prune (server-side scheduled cleanup of inactive users).");

        _pruneSettingsTask ??= _apiController.GroupGetPruneSettings(new GroupDto(GroupFullInfo.Group));

        if (!_pruneSettingsLoaded)
        {
            if (!_pruneSettingsTask!.IsCompleted)
            {
                UiSharedService.ColorTextWrapped("Loading prune settings from server...", ImGuiColors.DalamudGrey);
                return;
            }

            if (_pruneSettingsTask.IsFaulted || _pruneSettingsTask.IsCanceled)
            {
                UiSharedService.ColorTextWrapped("Failed to load auto-prune settings.", ImGuiColors.DalamudRed);
                _pruneSettingsTask = null;
                _pruneSettingsLoaded = false;
                return;
            }

            var dto = _pruneSettingsTask.GetAwaiter().GetResult();

            _autoPruneEnabled = dto.AutoPruneEnabled && dto.AutoPruneDays > 0;
            _autoPruneDays = dto.AutoPruneDays > 0 ? dto.AutoPruneDays : 14;

            _pruneSettingsLoaded = true;
        }

        bool enabled = _autoPruneEnabled;
        if (ImGui.Checkbox("Enable automatic pruning", ref enabled))
        {
            _autoPruneEnabled = enabled;
            SavePruneSettings();
        }
        UiSharedService.AttachToolTip("When enabled, inactive non-pinned, non-moderator users will be pruned automatically on the server.");


        if (!_autoPruneEnabled)
        {
            ImGui.BeginDisabled();
        }
        ImGui.SameLine();
        ImGui.SetNextItemWidth(150);
        _uiSharedService.DrawCombo(
            "Day(s) of inactivity (gets checked hourly)",
            [0, 1, 3, 7, 14, 30, 90],
            (count) => count == 0 ? "2 hours(s)" : count + " day(s)",
            selected =>
            {
                _autoPruneDays = selected;
                SavePruneSettings();
            },
            _autoPruneDays);

        if (!_autoPruneEnabled)
        {
            ImGui.EndDisabled();
            UiSharedService.ColorTextWrapped(
                "Automatic prune is currently disabled. Enable it and choose an inactivity threshold to let the server clean up inactive users automatically.",
                ImGuiColors.DalamudGrey);
        }
    }

    private void DrawProfile()
    {
        var profileTab = ImRaii.TabItem("Profile");
        if (!profileTab)
            return;

        if (_profileData != null)
        {
            if (!string.Equals(_profileDescription, _profileData.Description, StringComparison.Ordinal))
            {
                _profileDescription = _profileData.Description;
            }

            UiSharedService.TextWrapped("Preview the Syncshell profile in a standalone window.");

            if (_uiSharedService.IconTextButton(FontAwesomeIcon.AddressCard, "Open Syncshell Profile"))
            {
                Mediator.Publish(new GroupProfileOpenStandaloneMessage(GroupFullInfo.Group));
            }
            UiSharedService.AttachToolTip("Opens the standalone Syncshell profile window for this group.");

            ImGuiHelpers.ScaledDummy(2f);
            ImGui.TextDisabled("Profile Flags");
            ImGui.BulletText(_profileData.IsNsfw ? "Marked as NSFW" : "Marked as SFW");
            ImGui.BulletText(_profileData.IsDisabled ? "Profile disabled for viewers" : "Profile active");

            ImGuiHelpers.ScaledDummy(2f);
            UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
            ImGuiHelpers.ScaledDummy(2f);

            UiSharedService.TextWrapped("Open the syncshell profile editor to update images, description, tags, and visibility settings.");
            ImGuiHelpers.ScaledDummy(2f);

            if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserEdit, "Open Syncshell Profile Editor"))
            {
                Mediator.Publish(new OpenGroupProfileEditorMessage(GroupFullInfo));
            }
            UiSharedService.AttachToolTip("Launches the editor window and associated live preview for this syncshell.");
        }
        else
        {
            UiSharedService.TextWrapped("Profile information is loading...");
        }

        profileTab.Dispose();
    }

    private void DrawManagement()
    {
        var mgmtTab = ImRaii.TabItem("User Management");
        if (!mgmtTab)
            return;

        ImGuiHelpers.ScaledDummy(3f);

        var style = ImGui.GetStyle();

        var baseTab = UIColors.Get("FullBlack").WithAlpha(0.0f);
        var baseTabDim = UIColors.Get("FullBlack").WithAlpha(0.1f);
        var accent = UIColors.Get("LightlessPurple");
        var accentHover = accent.WithAlpha(0.90f);
        var accentActive = accent;

        //Pushing style vars for inner tab bar
        ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(10f, 5f) * ImGuiHelpers.GlobalScale);
        ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(8f, style.ItemSpacing.Y));
        ImGui.PushStyleVar(ImGuiStyleVar.TabRounding, 5f * ImGuiHelpers.GlobalScale);

        try
        {
            //Pushing color stack for inner tab bar
            using (ImRaii.PushColor(ImGuiCol.Tab, baseTab))
            using (ImRaii.PushColor(ImGuiCol.TabHovered, accentHover))
            using (ImRaii.PushColor(ImGuiCol.TabActive, accentActive))
            using (ImRaii.PushColor(ImGuiCol.TabUnfocused, baseTabDim))
            using (ImRaii.PushColor(ImGuiCol.TabUnfocusedActive, accentActive.WithAlpha(0.80f)))
            {
                using (var innerTabBar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID))
                {
                    if (innerTabBar)
                    {
                        // Users tab
                        var usersTab = ImRaii.TabItem("Users");
                        if (usersTab)
                        {
                            DrawUserListSection();
                        }
                        usersTab.Dispose();

                        // Cleanup tab
                        var cleanupTab = ImRaii.TabItem("Cleanup");
                        if (cleanupTab)
                        {
                            DrawMassCleanupSection();
                        }
                        cleanupTab.Dispose();

                        // Bans tab
                        var bansTab = ImRaii.TabItem("Bans");
                        if (bansTab)
                        {
                            DrawUserBansSection();
                        }
                        bansTab.Dispose();
                    }
                }
            }
            mgmtTab.Dispose();
        }
        finally
        {
            // Popping style vars (3) for inner tab bar
            ImGui.PopStyleVar(3);
        }
    }

    private void DrawUserListSection()
    {
        var snapshot = _pairUiService.GetSnapshot();
        if (!snapshot.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
        {
            UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
            return;
        }

        _uiSharedService.MediumText("User List & Administration", UIColors.Get("LightlessPurple"));
        ImGuiHelpers.ScaledDummy(2f);
        DrawUserListCustom(pairs, GroupFullInfo);
    }

    private void DrawMassCleanupSection()
    {
        _uiSharedService.MediumText("Mass Cleanup", UIColors.Get("DimRed"));
        UiSharedService.TextWrapped("Tools to bulk-clean inactive or unwanted users from this Syncshell.");
        ImGuiHelpers.ScaledDummy(3f);

        using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
        {
            if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
            {
                _ = _apiController.GroupClear(new(GroupFullInfo.Group));
            }
        }
        UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell."
            + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");

        ImGui.SameLine();

        using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
        {
            if (_uiSharedService.IconTextButton(FontAwesomeIcon.Brush, "Clear Lightfinder Users"))
            {
                _ = _apiController.GroupClearFinder(new(GroupFullInfo.Group));
            }
        }
        UiSharedService.AttachToolTip("This will remove all users that joined through Lightfinder from the Syncshell."
            + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");

        ImGuiHelpers.ScaledDummy(2f);
        UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.0f);
        ImGuiHelpers.ScaledDummy(2f);

        if (_uiSharedService.IconTextButton(FontAwesomeIcon.Unlink, "Check for Inactive Users"))
        {
            _pruneTestTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: false);
            _pruneTask = null;
        }
        UiSharedService.AttachToolTip($"This will start the prune process for this Syncshell of inactive Lightless users that have not logged in in the past {_pruneDays} day(s)."
            + Environment.NewLine + "You will be able to review the amount of inactive users before executing the prune."
            + UiSharedService.TooltipSeparator + "Note: this check excludes pinned users and moderators of this Syncshell.");

        ImGui.SameLine();
        ImGui.SetNextItemWidth(150);
        _uiSharedService.DrawCombo(
            "Day(s) of inactivity",
            [0, 1, 3, 7, 14, 30, 90],
            (count) => count == 0 ? "2 hours(s)" : count + " day(s)",
            (selected) =>
            {
                _pruneDays = selected;
                _pruneTestTask = null;
                _pruneTask = null;
            },
            _pruneDays);

        if (_pruneTestTask != null)
        {
            if (!_pruneTestTask.IsCompleted)
            {
                UiSharedService.ColorTextWrapped("Calculating inactive users...", ImGuiColors.DalamudYellow);
            }
            else
            {
                ImGui.AlignTextToFramePadding();
                UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} day(s).");
                if (_pruneTestTask.Result > 0)
                {
                    using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
                    {
                        if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Prune Inactive Users"))
                        {
                            _pruneTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: true);
                            _pruneTestTask = null;
                        }
                    }
                    UiSharedService.AttachToolTip($"Pruning will remove {_pruneTestTask?.Result ?? 0} inactive user(s)."
                        + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
                }
            }
        }

        if (_pruneTask != null)
        {
            if (!_pruneTask.IsCompleted)
            {
                UiSharedService.ColorTextWrapped("Pruning Syncshell...", ImGuiColors.DalamudYellow);
            }
            else
            {
                UiSharedService.TextWrapped($"Syncshell was pruned and {_pruneTask.Result} inactive user(s) have been removed.");
            }
        }

        ImGuiHelpers.ScaledDummy(4f);
        UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.0f);
        ImGuiHelpers.ScaledDummy(2f);

        DrawAutoPruneSettings();
    }

    private void DrawUserBansSection()
    {
        _uiSharedService.MediumText("User Bans", UIColors.Get("LightlessYellow"));
        ImGuiHelpers.ScaledDummy(3f);

        if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
        {
            _bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
        }
        ImGuiHelpers.ScaledDummy(2f);

        ImGui.BeginChild("bannedListScroll#" + GroupFullInfo.GID, new Vector2(0, 0), true);

        var style = ImGui.GetStyle();
        float fullW = ImGui.GetContentRegionAvail().X;

        float colIdentity = fullW * 0.45f;
        float colMeta = fullW * 0.35f;
        float colActions = fullW - colIdentity - colMeta - style.ItemSpacing.X * 2.0f;

        // Header
        DrawBannedListHeader(colIdentity, colMeta);

        int rowIndex = 0;
        foreach (var bannedUser in _bannedUsers.ToList())
        {
            // Each row
            DrawBannedRow(bannedUser, rowIndex++, colIdentity, colMeta, colActions);
        }

        ImGui.EndChild();
    }

    private void DrawInvites(GroupPermissions perm)
    {
        var inviteTab = ImRaii.TabItem("Invites");
        if (inviteTab)
        {
            bool isInvitesDisabled = perm.IsDisableInvites();

            if (_uiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock,
                isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell"))
            {
                perm.SetDisableInvites(!isInvitesDisabled);
                _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
            }

            ImGuiHelpers.ScaledDummy(2f);

            UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password.");
            if (_uiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite"))
            {
                ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty);
            }
            UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard.");
            ImGui.InputInt("##amountofinvites", ref _multiInvites);
            ImGui.SameLine();
            using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100))
            {
                if (_uiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites"))
                {
                    _oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), _multiInvites).Result);
                }
            }

            if (_oneTimeInvites.Any())
            {
                var invites = string.Join(Environment.NewLine, _oneTimeInvites);
                ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly);
                if (_uiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard"))
                {
                    ImGui.SetClipboardText(invites);
                }
            }
        }
        inviteTab.Dispose();
    }

    private void DrawUserListCustom(IReadOnlyList<Pair> pairs, GroupFullInfoDto GroupFullInfo)
    {
        // Search bar
        ImGui.PushItemWidth(0);
        _uiSharedService.IconText(FontAwesomeIcon.Search, UIColors.Get("LightlessPurple"));
        ImGui.SameLine();

        ImGui.InputTextWithHint(
            "##UserSearchFilter",
            "Search UID/alias or note...",
            ref _userSearchFilter,
            64);

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

        var groupedPairs = new Dictionary<Pair, GroupPairUserInfo?>(
            pairs.Select(p => new KeyValuePair<Pair, GroupPairUserInfo?>(
                p,
                GroupFullInfo.GroupPairUserInfos.TryGetValue(p.UserData.UID, out var value) ? value : null
            ))
        );

        var filter = _userSearchFilter?.Trim();
        bool hasFilter = !string.IsNullOrEmpty(filter);
        if (hasFilter)
            filter = filter!.ToLowerInvariant();

        var orderedPairs = groupedPairs
            .Where(p => !hasFilter || MatchesUserFilter(p.Key, filter!))
            .OrderBy(p =>
            {
                if (p.Value == null) return 10;
                if (string.Equals(p.Key.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal)) return 0;
                if (p.Value.Value.IsModerator()) return 1;
                if (p.Value.Value.IsPinned()) return 2;
                return 10;
            })
            .ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
            .ToList();

        DrawUserListHeader();

        ImGui.BeginChild("userListScroll#" + GroupFullInfo.Group.AliasOrGID, new Vector2(0, 0), true);

        int rowIndex = 0;
        foreach (var kv in orderedPairs)
        {
            var pair = kv.Key;
            var userInfoOpt = kv.Value;
            DrawUserRowCustom(pair, userInfoOpt, GroupFullInfo, rowIndex++);
        }

        ImGui.EndChild();
    }

    private static void DrawUserListHeader()
    {
        var style = ImGui.GetStyle();
        float x0 = ImGui.GetCursorPosX();
        float fullW = ImGui.GetContentRegionAvail().X;

        ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessPurple"));

        ImGui.SetCursorPosX(x0);
        ImGui.TextUnformatted("User");

        const string actionsLabel = "Actions";
        float labelWidth = ImGui.CalcTextSize(actionsLabel).X;

        ImGui.SameLine();
        ImGui.SetCursorPosX(x0 + fullW - labelWidth);
        ImGui.TextUnformatted(actionsLabel);

        ImGui.PopStyleColor();

        UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
    }

    private void DrawUserRowCustom(Pair pair, GroupPairUserInfo? userInfoOpt, GroupFullInfoDto GroupFullInfo, int rowIndex)
    {
        using var id = ImRaii.PushId("userRow_" + pair.UserData.UID);

        var style = ImGui.GetStyle();
        Vector2 rowStart = ImGui.GetCursorPos();
        Vector2 rowStartScr = ImGui.GetCursorScreenPos();
        float fullW = ImGui.GetContentRegionAvail().X;

        float frameH = ImGui.GetFrameHeight();
        float textH = ImGui.GetTextLineHeight();
        float rowHeight = frameH;

        if (rowIndex % 2 == 0)
        {
            var drawList = ImGui.GetWindowDrawList();
            var pMin = rowStartScr;
            var pMax = new Vector2(pMin.X + fullW, pMin.Y + rowHeight);

            var bgColor = UIColors.Get("FullBlack").WithAlpha(0.05f);
            drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(bgColor));
        }

        var isUserOwner = string.Equals(pair.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal);
        var userInfo = userInfoOpt ?? GroupPairUserInfo.None;

        float baselineY = rowStart.Y + (rowHeight - textH) / 2f;

        ImGui.SetCursorPos(new Vector2(rowStart.X, baselineY));

        bool hasFlag = false;
        if (userInfoOpt != null && (userInfo.IsModerator() || userInfo.IsPinned() || isUserOwner))
        {
            if (userInfo.IsModerator())
            {
                _uiSharedService.IconText(FontAwesomeIcon.UserShield, UIColors.Get("LightlessPurple"));
                UiSharedService.AttachToolTip("Moderator");
                hasFlag = true;
            }

            if (userInfo.IsPinned() && !isUserOwner)
            {
                if (hasFlag) ImGui.SameLine(0f, style.ItemSpacing.X);
                _uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
                UiSharedService.AttachToolTip("Pinned");
                hasFlag = true;
            }

            if (isUserOwner)
            {
                if (hasFlag) ImGui.SameLine(0f, style.ItemSpacing.X);
                _uiSharedService.IconText(FontAwesomeIcon.Crown, UIColors.Get("LightlessYellow"));
                UiSharedService.AttachToolTip("Owner");
                hasFlag = true;
            }
        }

        if (hasFlag)
            ImGui.SameLine(0f, style.ItemSpacing.X);

        ImGui.SetCursorPosY(baselineY);

        var note = pair.GetNote();
        var text = note == null
            ? pair.UserData.AliasOrUID
            : $"{note} ({pair.UserData.AliasOrUID})";

        var boolcolor = UiSharedService.GetBoolColor(pair.IsOnline);
        UiSharedService.ColorText(text, boolcolor);

        if (ImGui.IsItemClicked())
            ImGui.SetClipboardText(pair.UserData.AliasOrUID);

        if (!string.IsNullOrEmpty(pair.PlayerName))
            UiSharedService.AttachToolTip(pair.PlayerName + $"{Environment.NewLine}Click to copy UID or Alias");

        DrawUserActions(pair, GroupFullInfo, userInfo, isUserOwner, baselineY);

        // Move cursor to next row
        ImGui.SetCursorPos(new Vector2(rowStart.X, rowStart.Y + rowHeight + style.ItemSpacing.Y));
    }

    private void DrawUserActions(Pair pair, GroupFullInfoDto GroupFullInfo, GroupPairUserInfo userInfo, bool isUserOwner, float baselineY)
    {
        var style = ImGui.GetStyle();
        float frameH = ImGui.GetFrameHeight();

        int buttonCount = 0;
        if (_isOwner)
            buttonCount += 2; // Crown + Mod
        if (userInfo == GroupPairUserInfo.None || (!userInfo.IsModerator() && !isUserOwner))
            buttonCount += 3; // Pin + Trash + Ban

        if (buttonCount == 0)
            return;

        float totalWidth = buttonCount * frameH + (buttonCount - 1) * style.ItemSpacing.X;

        float curX = ImGui.GetCursorPosX();
        float avail = ImGui.GetContentRegionAvail().X;

        float startX = curX + MathF.Max(0, avail - (totalWidth + 30f));

        ImGui.SetCursorPos(new Vector2(startX, baselineY));

        bool first = true;

        if (_isOwner)
        {
            // Transfer ownership
            using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow")))
            using (ImRaii.Disabled(!UiSharedService.ShiftPressed()))
            {
                if (_uiSharedService.IconButton(FontAwesomeIcon.Crown))
                {
                    _ = _apiController.GroupChangeOwnership(new(GroupFullInfo.Group, pair.UserData));
                    IsOpen = false;
                }
            }

            UiSharedService.AttachToolTip("Hold SHIFT and click to transfer ownership of this Syncshell to "
                + pair.UserData.AliasOrUID + Environment.NewLine
                + "WARNING: This action is irreversible and will close screen.");

            first = false;

            // Mod / Demod
            using (ImRaii.PushColor(ImGuiCol.Text,
                       userInfo.IsModerator() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
            {
                if (!first) ImGui.SameLine(0f, style.ItemSpacing.X);

                if (_uiSharedService.IconButton(FontAwesomeIcon.UserShield))
                {
                    userInfo.SetModerator(!userInfo.IsModerator());
                    _ = _apiController.GroupSetUserInfo(
                        new GroupPairUserInfoDto(GroupFullInfo.Group, pair.UserData, userInfo));
                }
            }

            UiSharedService.AttachToolTip(
                userInfo.IsModerator() ? $"Demod {pair.UserData.AliasOrUID}" : $"Mod {pair.UserData.AliasOrUID}");
            first = false;
        }

        if (userInfo == GroupPairUserInfo.None || (!userInfo.IsModerator() && !isUserOwner))
        {
            // Pin
            using (ImRaii.PushColor(ImGuiCol.Text,
                       userInfo.IsPinned() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
            {
                if (!first) ImGui.SameLine(0f, style.ItemSpacing.X);

                if (_uiSharedService.IconButton(FontAwesomeIcon.Thumbtack))
                {
                    userInfo.SetPinned(!userInfo.IsPinned());

                    _ = _apiController.GroupSetUserInfo(
                        new GroupPairUserInfoDto(GroupFullInfo.Group, pair.UserData, userInfo));
                }
            }

            UiSharedService.AttachToolTip(
                userInfo.IsPinned() ? $"Unpin {pair.UserData.AliasOrUID}" : $"Pin {pair.UserData.AliasOrUID}");
            first = false;

            // Trash
            using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
            using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
            {
                if (!first) ImGui.SameLine(0f, style.ItemSpacing.X);

                if (_uiSharedService.IconButton(FontAwesomeIcon.Trash))
                {
                    _ = _apiController.GroupRemoveUser(new GroupPairDto(GroupFullInfo.Group, pair.UserData));
                }
            }

            UiSharedService.AttachToolTip($"Remove {pair.UserData.AliasOrUID} from Syncshell"
                + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
            first = false;

            // Ban
            using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
            using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
            {
                if (!first) ImGui.SameLine(0f, style.ItemSpacing.X);

                if (_uiSharedService.IconButton(FontAwesomeIcon.Ban))
                {
                    Mediator.Publish(new OpenBanUserPopupMessage(pair, GroupFullInfo));
                }
            }

            UiSharedService.AttachToolTip($"Ban {pair.UserData.AliasOrUID} from Syncshell"
                + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
        }
    }

    private static void DrawBannedListHeader(float colIdentity, float colMeta)
    {
        var style = ImGui.GetStyle();
        float x0 = ImGui.GetCursorPosX();

        ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));

        // User and reason
        ImGui.SetCursorPosX(x0);
        ImGui.TextUnformatted("User / Reason");

        // Moderator and Date
        ImGui.SameLine();
        ImGui.SetCursorPosX(x0 + colIdentity + style.ItemSpacing.X);
        ImGui.TextUnformatted("Moderator / Date");

        // Actions
        ImGui.SameLine();
        ImGui.SetCursorPosX(x0 + colIdentity + colMeta + style.ItemSpacing.X * 2.0f);
        ImGui.TextUnformatted("Actions");

        ImGui.PopStyleColor();

        UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.0f);
    }

    private void DrawBannedRow(BannedGroupUserDto bannedUser, int rowIndex, float colIdentity, float colMeta, float colActions)
    {
        using var id = ImRaii.PushId("banRow_" + bannedUser.UID);

        var style = ImGui.GetStyle();
        float x0 = ImGui.GetCursorPosX();

        if (rowIndex % 2 == 0)
        {
            var drawList = ImGui.GetWindowDrawList();
            var pMin = ImGui.GetCursorScreenPos();
            var rowHeight = ImGui.GetTextLineHeightWithSpacing() * 2.6f;
            var pMax = new Vector2(
                pMin.X + colIdentity + colMeta + colActions + style.ItemSpacing.X * 2.0f,
                pMin.Y + rowHeight);

            var bgColor = UIColors.Get("FullBlack").WithAlpha(0.10f);
            drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(bgColor));
        }

        ImGui.SetCursorPosX(x0);
        ImGui.AlignTextToFramePadding();

        string alias = bannedUser.UserAlias ?? string.Empty;
        string line1 = string.IsNullOrEmpty(alias)
            ? bannedUser.UID
            : $"{alias} ({bannedUser.UID})";

        ImGui.TextUnformatted(line1);

        var reason = bannedUser.Reason ?? string.Empty;
        if (!string.IsNullOrWhiteSpace(reason))
        {
            var reasonPos = new Vector2(x0, ImGui.GetCursorPosY());
            ImGui.SetCursorPos(reasonPos);
            ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
            UiSharedService.TextWrapped(reason);
            ImGui.PopStyleColor();
        }

        ImGui.SameLine();
        ImGui.SetCursorPosX(x0 + colIdentity + style.ItemSpacing.X);

        ImGui.AlignTextToFramePadding();
        ImGui.TextUnformatted($"By: {bannedUser.BannedBy}");

        var dateText = bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture);
        ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
        ImGui.TextUnformatted(dateText);
        ImGui.PopStyleColor();

        ImGui.SameLine();
        ImGui.SetCursorPosX(x0 + colIdentity + colMeta + style.ItemSpacing.X * 2.0f);

        if (_uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban"))
        {
            _apiController.GroupUnbanUser(bannedUser);
            _bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
        }

        UiSharedService.AttachToolTip($"Unban {alias} ({bannedUser.UID}) from this Syncshell");

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

    private void SavePruneSettings()
    {
        if (_autoPruneDays <= 0)
        {
            _autoPruneEnabled = false;
        }

        var dto = new GroupPruneSettingsDto(Group: GroupFullInfo.Group, AutoPruneEnabled: _autoPruneEnabled, AutoPruneDays: _autoPruneDays);

        try
        {
            _apiController.GroupSetPruneSettings(dto).GetAwaiter().GetResult();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to save auto prune settings for group {GID}", GroupFullInfo.Group.GID);
            UiSharedService.ColorTextWrapped("Failed to save auto-prune settings.", ImGuiColors.DalamudRed);
        }
    }

    private static bool MatchesUserFilter(Pair pair, string filterLower)
    {
        var note = pair.GetNote() ?? string.Empty;
        var uid = pair.UserData.UID ?? string.Empty;
        var alias = pair.UserData.AliasOrUID ?? string.Empty;

        return note.Contains(filterLower, StringComparison.OrdinalIgnoreCase)
               || uid.Contains(filterLower, StringComparison.OrdinalIgnoreCase)
               || alias.Contains(filterLower, StringComparison.OrdinalIgnoreCase);
    }

    public override void OnClose()
    {
        Mediator.Publish(new RemoveWindowMessage(this));
        _pfpTextureWrap?.Dispose();
    }
}