// ---------------------------------------------------------------------------
// BT.Framework.Tests
// EventBusTests.cs — 타입 안전 이벤트 버스 단위 테스트
// ---------------------------------------------------------------------------
using System;
using NUnit.Framework;
using BurningTimes.Core.Event;

namespace BurningTimes.Tests.Core.Event
{
    public class EventBusTests
    {
        private struct FooEvent { public int Value; }
        private struct BarEvent { public string Text; }

        [SetUp]
        public void SetUp()
        {
            EventBus.ClearAll();
        }

        [Test]
        public void Publish_InvokesRegisteredHandlers()
        {
            int received = 0;
            Action<FooEvent> handler = e => received = e.Value;
            EventBus.Subscribe(handler);

            EventBus.Publish(new FooEvent { Value = 42 });

            Assert.That(received, Is.EqualTo(42));
        }

        [Test]
        public void Publish_InvokesAllHandlersInRegistrationOrder()
        {
            var order = new System.Collections.Generic.List<int>();
            EventBus.Subscribe<FooEvent>(_ => order.Add(1));
            EventBus.Subscribe<FooEvent>(_ => order.Add(2));
            EventBus.Subscribe<FooEvent>(_ => order.Add(3));

            EventBus.Publish(new FooEvent());

            Assert.That(order, Is.EqualTo(new[] { 1, 2, 3 }));
        }

        [Test]
        public void Unsubscribe_RemovesHandler()
        {
            int count = 0;
            Action<FooEvent> handler = _ => count++;
            EventBus.Subscribe(handler);
            EventBus.Unsubscribe(handler);

            EventBus.Publish(new FooEvent());

            Assert.That(count, Is.EqualTo(0));
        }

        [Test]
        public void Unsubscribe_ReturnsFalseForUnknownHandler()
        {
            Action<FooEvent> handler = _ => { };
            Assert.IsFalse(EventBus.Unsubscribe(handler));
        }

        [Test]
        public void Publish_ContinuesAfterHandlerException()
        {
            int afterException = 0;
            EventBus.Subscribe<FooEvent>(_ => throw new InvalidOperationException("test"));
            EventBus.Subscribe<FooEvent>(_ => afterException++);

            // Log.Error 로만 기록되고 throw 되지 않아야 함
            EventBus.Publish(new FooEvent());

            Assert.That(afterException, Is.EqualTo(1));
        }

        [Test]
        public void Publish_SubscribeDuringIteration_DeferredToNextPublish()
        {
            int extra = 0;
            EventBus.Subscribe<FooEvent>(_ =>
            {
                EventBus.Subscribe<FooEvent>(__ => extra++);
            });

            EventBus.Publish(new FooEvent());
            Assert.That(extra, Is.EqualTo(0), "현재 Publish 에는 반영되지 않아야 함");

            EventBus.Publish(new FooEvent());
            // 첫 Publish 에서 등록된 핸들러는 두 번째 Publish 에서 호출, 그리고 다시 재등록...
            Assert.That(extra, Is.GreaterThanOrEqualTo(1));
        }

        [Test]
        public void DifferentEventTypes_AreIsolated()
        {
            int fooCount = 0;
            int barCount = 0;
            EventBus.Subscribe<FooEvent>(_ => fooCount++);
            EventBus.Subscribe<BarEvent>(_ => barCount++);

            EventBus.Publish(new FooEvent());

            Assert.That(fooCount, Is.EqualTo(1));
            Assert.That(barCount, Is.EqualTo(0));
        }

        [Test]
        public void Clear_RemovesAllHandlersOfType()
        {
            EventBus.Subscribe<FooEvent>(_ => { });
            EventBus.Subscribe<FooEvent>(_ => { });
            Assert.That(EventBus.SubscriberCount<FooEvent>(), Is.EqualTo(2));

            EventBus.Clear<FooEvent>();

            Assert.That(EventBus.SubscriberCount<FooEvent>(), Is.EqualTo(0));
        }

        [Test]
        public void ClearAll_RemovesAllTypes()
        {
            EventBus.Subscribe<FooEvent>(_ => { });
            EventBus.Subscribe<BarEvent>(_ => { });

            EventBus.ClearAll();

            Assert.That(EventBus.SubscriberCount<FooEvent>(), Is.EqualTo(0));
            Assert.That(EventBus.SubscriberCount<BarEvent>(), Is.EqualTo(0));
        }

        [Test]
        public void Subscribe_NullHandler_Throws()
        {
            Assert.Throws<ArgumentNullException>(() => EventBus.Subscribe<FooEvent>(null));
        }
    }
}
