// ---------------------------------------------------------------------------
// BT.Framework.Tests
// MonoSingletonTests.cs — MonoSingleton<T> PlayMode 테스트
// ---------------------------------------------------------------------------
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using BurningTimes.Core.Patterns;

namespace BurningTimes.Tests.Core.Patterns
{
    public class MonoSingletonTests
    {
        // 각 테스트 고유의 파생 타입을 사용해 static 공유 이슈를 회피한다.

        // --- SyncCase ---
        private class SyncCase : MonoSingleton<SyncCase>
        {
            public int InitCount;
            protected override void OnInitialized() => InitCount++;
        }

        // --- NoAutoCreateCase ---
        private class NoAutoCreateCase : MonoSingleton<NoAutoCreateCase>
        {
            protected override bool AutoCreate => false;
        }

        // --- NonPersistentCase ---
        private class NonPersistentCase : MonoSingleton<NonPersistentCase>
        {
            protected override bool Persistent => false;
        }

        // --- DuplicateCase ---
        private class DuplicateCase : MonoSingleton<DuplicateCase>
        {
            public static int DestroyedCount;
            protected override void OnDuplicateDestroyed() => DestroyedCount++;
        }

        // --- ManualReadyCase ---
        private class ManualReadyCase : MonoSingleton<ManualReadyCase>
        {
            protected override InitMode InitMode => InitMode.ManualReady;
            public void Ready() => MarkReady();
        }

        [TearDown]
        public void TearDown()
        {
            MonoSingleton<SyncCase>.ResetForTests();
            MonoSingleton<NoAutoCreateCase>.ResetForTests();
            MonoSingleton<NonPersistentCase>.ResetForTests();
            MonoSingleton<DuplicateCase>.ResetForTests();
            MonoSingleton<ManualReadyCase>.ResetForTests();
            DuplicateCase.DestroyedCount = 0;
        }

        [UnityTest]
        public IEnumerator AutoCreate_Instance_On_First_Access()
        {
            Assert.IsFalse(MonoSingleton<SyncCase>.Exists);
            var inst = SyncCase.Instance;
            yield return null;

            Assert.IsNotNull(inst);
            Assert.IsTrue(MonoSingleton<SyncCase>.Exists);
            Assert.IsTrue(MonoSingleton<SyncCase>.IsReady);
            Assert.AreEqual(1, inst.InitCount, "OnInitialized는 1회 호출");
        }

        [UnityTest]
        public IEnumerator NoAutoCreate_Returns_Null_And_Warns()
        {
            LogAssert.Expect(LogType.Warning,
                new System.Text.RegularExpressions.Regex(@"AutoCreate=false"));

            var inst = NoAutoCreateCase.Instance;
            yield return null;
            Assert.IsNull(inst);
        }

        [UnityTest]
        public IEnumerator NonPersistent_Does_Not_DontDestroyOnLoad()
        {
            var inst = NonPersistentCase.Instance;
            yield return null;
            // DontDestroyOnLoad 씬의 scene.buildIndex == -1 임을 역으로 이용
            Assert.AreNotEqual(-1, inst.gameObject.scene.buildIndex,
                "Persistent=false인데 DontDestroyOnLoad 씬에 들어갔다");
        }

        [UnityTest]
        public IEnumerator Persistent_Goes_To_DontDestroyOnLoad()
        {
            var inst = SyncCase.Instance;
            yield return null;
            Assert.AreEqual("DontDestroyOnLoad", inst.gameObject.scene.name);
        }

        [UnityTest]
        public IEnumerator Duplicate_Instance_Is_Destroyed()
        {
            LogAssert.Expect(LogType.Warning,
                new System.Text.RegularExpressions.Regex(@"중복 인스턴스"));

            var first = DuplicateCase.Instance;
            yield return null;

            var extra = new GameObject("extra").AddComponent<DuplicateCase>();
            yield return null;
            // 파괴 완료 대기
            yield return null;

            Assert.IsTrue(extra == null, "중복 인스턴스는 Destroy 되어야 한다");
            Assert.AreEqual(1, DuplicateCase.DestroyedCount);
            Assert.AreSame(first, MonoSingleton<DuplicateCase>.Instance);
        }

        [UnityTest]
        public IEnumerator ManualReady_Stays_Not_Ready_Until_Marked()
        {
            var inst = ManualReadyCase.Instance;
            yield return null;
            Assert.IsNotNull(inst);
            Assert.IsFalse(MonoSingleton<ManualReadyCase>.IsReady);

            inst.Ready();
            Assert.IsTrue(MonoSingleton<ManualReadyCase>.IsReady);
        }
    }
}
