// ---------------------------------------------------------------------------
// BT.Framework
// MonoSingleton.cs — 4종 통합(Sync/Async/Inner/Ready) MonoBehaviour 싱글톤
// ---------------------------------------------------------------------------
using System.Collections;
using UnityEngine;
using BurningTimes.Core.Coroutine;
using BurningTimes.Core.Util.Log;

namespace BurningTimes.Core.Patterns
{
    /// <summary>
    /// <see cref="MonoBehaviour"/> 단일 인스턴스 베이스 클래스.
    /// </summary>
    /// <remarks>
    /// <para>기존 BurningTimesCore의 4종(<c>Singleton</c>, <c>AsyncSingleton</c>, <c>InnerSingleton</c>,
    /// <c>ReadySingleton</c>)을 통합했다(설계 문서 §4-1). 옵션은 <see cref="Attribute"/> 방식이 아닌
    /// 가상 프로퍼티로 노출하여 런타임 리플렉션 비용을 제거했다.</para>
    /// <para>동작 규칙:</para>
    /// <list type="bullet">
    ///   <item><description><see cref="Instance"/>는 씬에 있으면 찾고, 없으면 <see cref="AutoCreate"/> 판단 후 생성.</description></item>
    ///   <item><description><see cref="Persistent"/>이면 <c>DontDestroyOnLoad</c> 적용.</description></item>
    ///   <item><description><see cref="InitMode"/>에 따라 <see cref="OnInitialized"/> / <see cref="OnInitializeAsync"/>
    ///     / <see cref="MarkReady"/> 중 하나로 준비 상태가 결정된다.</description></item>
    ///   <item><description>중복 인스턴스가 발견되면 나중 것이 파괴된다(<see cref="OnDuplicateDestroyed"/> 훅).</description></item>
    ///   <item><description>애플리케이션 종료 후 <see cref="Instance"/> 접근은 <c>null</c>을 반환한다(Unity destroyed object 회피).</description></item>
    /// </list>
    /// <para>주의: Unity 메인 스레드에서만 접근한다. 다른 스레드 호출은 지원하지 않는다.</para>
    /// </remarks>
    public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
    {
        private const string LogCategory = "Singleton";

        private static T _instance;
        private static bool _applicationIsQuitting;
        private static readonly object _gate = new object();

        /// <summary>
        /// 싱글톤 인스턴스. 없으면 <see cref="AutoCreate"/> 규칙으로 자동 생성 시도.
        /// 애플리케이션 종료 중이면 <c>null</c>을 반환한다.
        /// </summary>
        public static T Instance
        {
            get
            {
                if (_applicationIsQuitting) return null;
                if (_instance != null) return _instance;

                lock (_gate)
                {
                    if (_instance != null) return _instance;

                    // 씬 검색
                    var found = FindAnyObjectByType<T>(FindObjectsInactive.Include);
                    if (found != null)
                    {
                        _instance = found;
                        _instance.ApplyPersistence();
                        return _instance;
                    }

                    // 자동 생성 판정 — 프로토타입 인스턴스로 AutoCreate 값만 읽음
                    // AutoCreate=false 이면 외부가 GameObject에 붙여야 한다(InnerSingleton 대응)
                    var probe = new GameObject(typeof(T).Name + "_probe").AddComponent<T>();
                    bool autoCreate = probe.AutoCreate;
                    if (!autoCreate)
                    {
                        Destroy(probe.gameObject);
                        Log.Warn(LogCategory,
                            $"{typeof(T).Name}: AutoCreate=false, 씬에 인스턴스가 없어 null 반환");
                        return null;
                    }

                    // probe 가 이미 Awake를 통해 _instance 에 등록되었을 것
                    probe.gameObject.name = typeof(T).Name;
                    return _instance;
                }
            }
        }

        /// <summary>싱글톤이 이미 존재하거나 조회 가능한지 확인(자동 생성 트리거 없음).</summary>
        public static bool Exists => !_applicationIsQuitting && _instance != null;

        /// <summary>초기화 완료 여부. <see cref="InitMode"/>에 따라 결정된다.</summary>
        public static bool IsReady { get; private set; }

        // -----------------------------------------------------------------
        // 옵션 (파생에서 override)
        // -----------------------------------------------------------------

        /// <summary>씬 전환에도 파괴되지 않을지 여부. 기본 <c>true</c>.</summary>
        protected virtual bool Persistent => true;

        /// <summary>인스턴스 부재 시 자동 생성 여부. 기본 <c>true</c>.</summary>
        protected virtual bool AutoCreate => true;

        /// <summary>초기화 모드. 기본 <see cref="Core.Patterns.InitMode.Sync"/>.</summary>
        protected virtual InitMode InitMode => InitMode.Sync;

        // -----------------------------------------------------------------
        // 라이프사이클 훅 (파생에서 override)
        // -----------------------------------------------------------------

        /// <summary><see cref="InitMode.Sync"/>에서 <c>Awake</c> 시 호출된다.</summary>
        protected virtual void OnInitialized() { }

        /// <summary><see cref="InitMode.Async"/>에서 코루틴으로 실행된다. 종료 시점에 <see cref="IsReady"/>가 <c>true</c>.</summary>
        protected virtual IEnumerator OnInitializeAsync() { yield break; }

        /// <summary>중복 인스턴스가 파괴될 때 마지막 호출. 리소스 정리 훅.</summary>
        protected virtual void OnDuplicateDestroyed() { }

        /// <summary><see cref="InitMode.ManualReady"/>에서 구현체가 호출하여 준비 상태로 전환.</summary>
        protected void MarkReady() { IsReady = true; }

        // -----------------------------------------------------------------
        // Unity 라이프사이클
        // -----------------------------------------------------------------

        protected virtual void Awake()
        {
            if (_instance != null && _instance != this)
            {
                Log.Warn(LogCategory,
                    $"{typeof(T).Name}: 중복 인스턴스 감지, 이번 GameObject 파괴");
                OnDuplicateDestroyed();
                Destroy(gameObject);
                return;
            }

            _instance = (T)this;
            ApplyPersistence();

            switch (InitMode)
            {
                case InitMode.Sync:
                    OnInitialized();
                    IsReady = true;
                    break;
                case InitMode.Async:
                    CoroutineRunner.Start(InitializeAsyncWrapper());
                    break;
                case InitMode.ManualReady:
                    // 구현체가 MarkReady 호출할 때까지 대기
                    break;
            }
        }

        private IEnumerator InitializeAsyncWrapper()
        {
            yield return OnInitializeAsync();
            IsReady = true;
        }

        protected virtual void OnDestroy()
        {
            if (_instance == this)
            {
                _instance = null;
                IsReady = false;
            }
        }

        protected virtual void OnApplicationQuit()
        {
            _applicationIsQuitting = true;
        }

        // -----------------------------------------------------------------
        // 공용 유틸
        // -----------------------------------------------------------------

        private void ApplyPersistence()
        {
            if (!Persistent) return;
            if (transform.parent != null) transform.SetParent(null, true); // DontDestroyOnLoad는 루트 객체에만 적용
            DontDestroyOnLoad(gameObject);
        }

        /// <summary>테스트 전용: 정적 상태 초기화.</summary>
        internal static void ResetForTests()
        {
            if (_instance != null && _instance.gameObject != null)
                Object.DestroyImmediate(_instance.gameObject);
            _instance = null;
            IsReady = false;
            _applicationIsQuitting = false;
        }
    }
}
