// ---------------------------------------------------------------------------
// BT.Framework
// ServiceLocator.cs — 경량 서비스 레지스트리 (순수 C#, MonoBehaviour 비의존)
// ---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using BurningTimes.Core.Util.Log;

namespace BurningTimes.Core.Patterns
{
    /// <summary>
    /// 타입 키 기반 경량 서비스 레지스트리.
    /// </summary>
    /// <remarks>
    /// <para>설계 문서 §4-9 (v1.2 신설)에 따른 구현. MonoBehaviour와 무관한 순수 C# 서비스
    /// 의 중앙 레지스트리로, MonoSingleton(씬 생명주기 동반)과 EventBus(이벤트 분기)와 역할이
    /// 분리된 3축의 한 축을 담당한다.</para>
    /// <para>용도:</para>
    /// <list type="bullet">
    ///   <item><description>인터페이스 기반 느슨한 결합 — <c>ISaveProvider</c> 등으로 등록 후 구현 교체 용이</description></item>
    ///   <item><description>테스트 시 Mock 주입 — <see cref="Clear"/>로 전역 초기화, Mock <see cref="Register{T}(T)"/></description></item>
    ///   <item><description>Lazy 생성 — <see cref="Register{T}(Func{T})"/> 로 첫 <see cref="Resolve{T}"/> 시 생성</description></item>
    /// </list>
    /// <para>주의 규칙 (§4-9):</para>
    /// <list type="number">
    ///   <item><description>인터페이스 타입 등록 권장, 구체 타입 강결합 회피</description></item>
    ///   <item><description>글로벌 서비스만 등록. 씬 전환 시 스코프가 필요하면 MonoSingleton 사용</description></item>
    ///   <item><description><see cref="Resolve{T}"/> 실패 시 <see cref="ServiceNotRegisteredException"/> — silent null 금지</description></item>
    ///   <item><description>코어 자체는 ServiceLocator에 의존하지 않음(순환 방지)</description></item>
    /// </list>
    /// </remarks>
    public static class ServiceLocator
    {
        private const string LogCategory = "ServiceLocator";

        private static readonly object _gate = new object();
        private static readonly Dictionary<Type, object> _instances = new Dictionary<Type, object>();
        private static readonly Dictionary<Type, Func<object>> _factories = new Dictionary<Type, Func<object>>();

        // -----------------------------------------------------------------
        // 등록
        // -----------------------------------------------------------------

        /// <summary>
        /// 서비스 인스턴스를 등록한다. 동일 타입 재등록은 덮어쓴다(경고 로그).
        /// </summary>
        /// <exception cref="ArgumentNullException"><paramref name="service"/>가 <c>null</c>.</exception>
        public static void Register<T>(T service) where T : class
        {
            if (service == null) throw new ArgumentNullException(nameof(service));
            var type = typeof(T);
            lock (_gate)
            {
                if (_instances.ContainsKey(type) || _factories.ContainsKey(type))
                    Log.Warn(LogCategory, $"Register<{type.Name}>: 기존 바인딩을 덮어쓴다");
                _instances[type] = service;
                _factories.Remove(type);
            }
        }

        /// <summary>
        /// Lazy 팩토리를 등록한다. 첫 <see cref="Resolve{T}"/> 호출 시점에 팩토리가 실행되고,
        /// 결과 인스턴스가 캐싱된다(이후 호출은 캐시 반환).
        /// </summary>
        /// <exception cref="ArgumentNullException"><paramref name="factory"/>가 <c>null</c>.</exception>
        public static void Register<T>(Func<T> factory) where T : class
        {
            if (factory == null) throw new ArgumentNullException(nameof(factory));
            var type = typeof(T);
            lock (_gate)
            {
                if (_instances.ContainsKey(type) || _factories.ContainsKey(type))
                    Log.Warn(LogCategory, $"Register<{type.Name}>(factory): 기존 바인딩을 덮어쓴다");
                _instances.Remove(type);
                _factories[type] = () => factory();
            }
        }

        // -----------------------------------------------------------------
        // 조회
        // -----------------------------------------------------------------

        /// <summary>등록된 서비스를 조회한다. 미등록이면 예외.</summary>
        /// <exception cref="ServiceNotRegisteredException">해당 타입이 등록되지 않음.</exception>
        public static T Resolve<T>() where T : class
        {
            if (TryResolve<T>(out var service)) return service;
            throw new ServiceNotRegisteredException(typeof(T));
        }

        /// <summary>등록된 서비스를 조회한다. 미등록이면 <c>false</c>.</summary>
        public static bool TryResolve<T>(out T service) where T : class
        {
            var type = typeof(T);
            lock (_gate)
            {
                if (_instances.TryGetValue(type, out var cached))
                {
                    service = cached as T;
                    return service != null;
                }
                if (_factories.TryGetValue(type, out var factory))
                {
                    object created;
                    try { created = factory(); }
                    catch (Exception ex)
                    {
                        Log.Error(LogCategory, $"factory for {type.Name} threw", ex);
                        service = null;
                        return false;
                    }
                    if (created is T typed)
                    {
                        _instances[type] = typed;   // 캐시
                        _factories.Remove(type);
                        service = typed;
                        return true;
                    }
                    Log.Error(LogCategory,
                        $"factory for {type.Name} returned incompatible type {created?.GetType().Name ?? "<null>"}");
                    service = null;
                    return false;
                }
            }
            service = null;
            return false;
        }

        /// <summary>해당 타입이 등록되어 있는지 조회(팩토리 포함).</summary>
        public static bool IsRegistered<T>() where T : class
        {
            var type = typeof(T);
            lock (_gate) { return _instances.ContainsKey(type) || _factories.ContainsKey(type); }
        }

        // -----------------------------------------------------------------
        // 해제
        // -----------------------------------------------------------------

        /// <summary>해당 타입의 서비스 등록을 해제한다.</summary>
        public static void Unregister<T>() where T : class
        {
            var type = typeof(T);
            lock (_gate)
            {
                _instances.Remove(type);
                _factories.Remove(type);
            }
        }

        /// <summary>모든 등록을 해제한다(테스트 용도).</summary>
        public static void Clear()
        {
            lock (_gate)
            {
                _instances.Clear();
                _factories.Clear();
            }
        }
    }
}
