using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.GameData.Actors;
using Penumbra.Services;
using Penumbra.UI.Classes;

namespace Penumbra.UI.CollectionTab;

public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposable
{
    private readonly Configuration       _config;
    private readonly CommunicatorService _communicator;
    private readonly CollectionStorage   _storage;
    private readonly ActiveCollections   _active;
    private readonly TutorialService     _tutorial;
    private readonly IncognitoService    _incognito;

    private ModCollection? _dragging;

    public CollectionSelector(Configuration config, CommunicatorService communicator, CollectionStorage storage, ActiveCollections active,
        TutorialService tutorial, IncognitoService incognito)
        : base([], Flags.Delete | Flags.Add | Flags.Duplicate | Flags.Filter)
    {
        _config       = config;
        _communicator = communicator;
        _storage      = storage;
        _active       = active;
        _tutorial     = tutorial;
        _incognito    = incognito;

        _communicator.CollectionChange.Subscribe(OnCollectionChange, CollectionChange.Priority.CollectionSelector);
        // Set items.
        OnCollectionChange(CollectionType.Inactive, null, null, string.Empty);
        // Set selection.
        OnCollectionChange(CollectionType.Current, null, _active.Current, string.Empty);
    }

    protected override bool OnDelete(int idx)
    {
        if (idx < 0 || idx >= Items.Count)
            return false;

        // Always return false since we handle the selection update ourselves.
        _storage.RemoveCollection(Items[idx]);
        return false;
    }

    protected override bool DeleteButtonEnabled()
        => _storage.DefaultNamed != Current && _config.DeleteModModifier.IsActive();

    protected override string DeleteButtonTooltip()
        => _storage.DefaultNamed == Current
            ? $"The selected collection {Name(Current)} can not be deleted."
            : $"Delete the currently selected collection {(Current != null ? Name(Current) : string.Empty)}. Hold {_config.DeleteModModifier} to delete.";

    protected override bool OnAdd(string name)
        => _storage.AddCollection(name, null);

    protected override bool OnDuplicate(string name, int idx)
    {
        if (idx < 0 || idx >= Items.Count)
            return false;

        return _storage.AddCollection(name, Items[idx]);
    }

    protected override bool Filtered(int idx)
        => !Items[idx].Name.Contains(Filter, StringComparison.OrdinalIgnoreCase);

    private const string PayloadString = "Collection";

    protected override bool OnDraw(int idx)
    {
        using var color  = ImRaii.PushColor(ImGuiCol.Header, ColorId.SelectedCollection.Value());
        var       ret    = ImGui.Selectable(Name(Items[idx]), idx == CurrentIdx);
        using var source = ImRaii.DragDropSource();

        if (idx == CurrentIdx)
            _tutorial.OpenTutorial(BasicTutorialSteps.CurrentCollection);

        if (source)
        {
            _dragging = Items[idx];
            ImGui.SetDragDropPayload(PayloadString, nint.Zero, 0);
            ImGui.TextUnformatted($"Assigning {Name(_dragging)} to...");
        }

        if (ret)
            _active.SetCollection(Items[idx], CollectionType.Current);

        return ret;
    }

    public void DragTargetAssignment(CollectionType type, ActorIdentifier identifier)
    {
        using var target = ImRaii.DragDropTarget();
        if (!target.Success || _dragging == null || !ImGuiUtil.IsDropping(PayloadString))
            return;

        _active.SetCollection(_dragging, type, _active.Individuals.GetGroup(identifier));
        _dragging = null;
    }

    public void Dispose()
    {
        _communicator.CollectionChange.Unsubscribe(OnCollectionChange);
    }

    private string Name(ModCollection collection)
        => _incognito.IncognitoMode || collection.Name.Length == 0 ? collection.AnonymizedName : collection.Name;

    public void RestoreCollections()
    {
        Items.Clear();
        foreach (var c in _storage.OrderBy(c => c.Name))
            Items.Add(c);
        SetFilterDirty();
        SetCurrent(_active.Current);
    }

    private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? @new, string _3)
    {
        switch (type)
        {
            case CollectionType.Temporary: return;
            case CollectionType.Current:
                if (@new != null)
                    SetCurrent(@new);
                SetFilterDirty();
                return;
            case CollectionType.Inactive:
                RestoreCollections();
                SetFilterDirty();
                return;
            default:
                SetFilterDirty();
                return;
        }
    }
}
