// ---------------------------------------------------------------------------
// BT.Framework.Tests
// ServiceLocatorTests.cs — ServiceLocator 단위 테스트
// ---------------------------------------------------------------------------
using System;
using NUnit.Framework;
using BurningTimes.Core.Patterns;

namespace BurningTimes.Tests.Core.Patterns
{
    public class ServiceLocatorTests
    {
        private interface IGreeter { string Hello(); }
        private sealed class EnGreeter : IGreeter { public string Hello() => "Hi"; }
        private sealed class KoGreeter : IGreeter { public string Hello() => "안녕"; }

        private interface ICounter { int Count { get; } }
        private sealed class Counter : ICounter
        {
            public int Count { get; private set; }
            public Counter() { Count = 1; }
        }

        [SetUp] public void SetUp() => ServiceLocator.Clear();
        [TearDown] public void TearDown() => ServiceLocator.Clear();

        [Test]
        public void Register_Instance_And_Resolve()
        {
            var svc = new EnGreeter();
            ServiceLocator.Register<IGreeter>(svc);

            Assert.IsTrue(ServiceLocator.IsRegistered<IGreeter>());
            Assert.AreSame(svc, ServiceLocator.Resolve<IGreeter>());
        }

        [Test]
        public void Register_Null_Throws()
        {
            Assert.Throws<ArgumentNullException>(() => ServiceLocator.Register<IGreeter>((IGreeter)null));
            Assert.Throws<ArgumentNullException>(() => ServiceLocator.Register<IGreeter>((Func<IGreeter>)null));
        }

        [Test]
        public void Resolve_Missing_Throws_ServiceNotRegistered()
        {
            var ex = Assert.Throws<ServiceNotRegisteredException>(() => ServiceLocator.Resolve<IGreeter>());
            Assert.AreEqual(typeof(IGreeter), ex.ServiceType);
        }

        [Test]
        public void TryResolve_Missing_Returns_False()
        {
            Assert.IsFalse(ServiceLocator.TryResolve<IGreeter>(out var svc));
            Assert.IsNull(svc);
        }

        [Test]
        public void Factory_Is_Invoked_Once_And_Cached()
        {
            int factoryCalls = 0;
            ServiceLocator.Register<ICounter>(() => { factoryCalls++; return new Counter(); });

            Assert.AreEqual(0, factoryCalls, "등록 시점에 factory는 호출되지 않는다");

            var a = ServiceLocator.Resolve<ICounter>();
            var b = ServiceLocator.Resolve<ICounter>();

            Assert.AreEqual(1, factoryCalls, "factory는 첫 Resolve에서만 1회 호출");
            Assert.AreSame(a, b, "이후 Resolve는 캐시된 인스턴스를 반환");
        }

        [Test]
        public void Register_Overwrites_Existing_Binding()
        {
            UnityEngine.TestTools.LogAssert.Expect(UnityEngine.LogType.Warning,
                new System.Text.RegularExpressions.Regex(@"\[ServiceLocator\] Register<IGreeter>"));

            ServiceLocator.Register<IGreeter>(new EnGreeter());
            ServiceLocator.Register<IGreeter>(new KoGreeter());

            Assert.AreEqual("안녕", ServiceLocator.Resolve<IGreeter>().Hello());
        }

        [Test]
        public void Unregister_Removes_Binding()
        {
            ServiceLocator.Register<IGreeter>(new EnGreeter());
            ServiceLocator.Unregister<IGreeter>();

            Assert.IsFalse(ServiceLocator.IsRegistered<IGreeter>());
            Assert.Throws<ServiceNotRegisteredException>(() => ServiceLocator.Resolve<IGreeter>());
        }

        [Test]
        public void Clear_Removes_All()
        {
            ServiceLocator.Register<IGreeter>(new EnGreeter());
            ServiceLocator.Register<ICounter>(() => new Counter());
            ServiceLocator.Clear();

            Assert.IsFalse(ServiceLocator.IsRegistered<IGreeter>());
            Assert.IsFalse(ServiceLocator.IsRegistered<ICounter>());
        }

        [Test]
        public void Factory_Exception_Is_Captured_As_False()
        {
            UnityEngine.TestTools.LogAssert.Expect(UnityEngine.LogType.Error,
                new System.Text.RegularExpressions.Regex(@"factory for IGreeter threw"));
            UnityEngine.TestTools.LogAssert.Expect(UnityEngine.LogType.Exception,
                new System.Text.RegularExpressions.Regex("boom"));

            ServiceLocator.Register<IGreeter>(() => throw new InvalidOperationException("boom"));

            Assert.IsFalse(ServiceLocator.TryResolve<IGreeter>(out var svc));
            Assert.IsNull(svc);
        }
    }
}
