// ---------------------------------------------------------------------------
// BT.Framework
// EventBus.cs — 타입 안전 프로세스 내 이벤트 버스 (정적)
// ---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using BurningTimes.Core.Util.Log;

namespace BurningTimes.Core.Event
{
    /// <summary>
    /// 타입을 키로 사용하는 프로세스 내 이벤트 버스.
    /// </summary>
    /// <remarks>
    /// <para>설계 문서 §3-1 (04_Tier1_3종_상호작용_설계_v1.md). 기존 <c>ObserverManager</c> 의
    /// 문자열 키·박싱 비용·프로젝트 특화 키 생성기 오염 문제를 해결하기 위해 제네릭 타입 버스로 재설계.</para>
    /// <para>계약:</para>
    /// <list type="bullet">
    ///   <item><description>메인 스레드 전용 (Unity 표준). 멀티스레드 지원은 Tier 3 네트워크 도입 시 재검토.</description></item>
    ///   <item><description><see cref="Publish{TEvent}"/> 중 핸들러가 예외를 던져도 나머지 구독자 호출 보장 (catch-and-log).</description></item>
    ///   <item><description><see cref="Publish{TEvent}"/> 진행 중 발생한 Subscribe/Unsubscribe 는 다음 Publish 에 반영(iteration snapshot).</description></item>
    ///   <item><description>이벤트 타입은 struct 권장 (박싱 회피). class 도 허용.</description></item>
    ///   <item><description>약한 참조 방식 미채택 — 명시적 <see cref="Unsubscribe{TEvent}"/> 의무 (설계 문서 §6-2 기각안 참조).</description></item>
    /// </list>
    /// <para>문자열 키 기반 특수 용도는 <see cref="BurningTimes.Core.Event.Raw.RawEventBus"/> 분리.</para>
    /// </remarks>
    public static class EventBus
    {
        /// <summary>
        /// 타입별 구독자 목록 저장소. 동일 타입에 대한 List 는 핸들러 집합.
        /// </summary>
        /// <remarks>
        /// 제네릭 정적 클래스 <see cref="Holder{TEvent}"/> 를 통해 타입별 List 를 캐싱, Dictionary lookup 비용 제거 (C11 자원 효율).
        /// </remarks>
        private static class Holder<TEvent>
        {
            public static readonly List<Action<TEvent>> Handlers = new List<Action<TEvent>>(4);
        }

        /// <summary>
        /// 전체 타입 핸들러 목록을 역으로 추적하는 메타 테이블 (<see cref="ClearAll"/> 용).
        /// </summary>
        private static readonly List<Action> ClearCallbacks = new List<Action>();

        /// <summary>타입별 초기화 1회 flag.</summary>
        private static readonly HashSet<Type> RegisteredTypes = new HashSet<Type>();

        /// <summary>
        /// 해당 이벤트 타입에 대한 핸들러를 등록한다.
        /// </summary>
        /// <typeparam name="TEvent">이벤트 타입.</typeparam>
        /// <param name="handler">호출 콜백.</param>
        /// <exception cref="ArgumentNullException"><paramref name="handler"/> 가 null 일 때.</exception>
        public static void Subscribe<TEvent>(Action<TEvent> handler)
        {
            if (handler == null) throw new ArgumentNullException(nameof(handler));
            EnsureRegistered<TEvent>();
            Holder<TEvent>.Handlers.Add(handler);
        }

        /// <summary>
        /// 등록된 핸들러를 제거한다. 동일 핸들러가 여러 번 등록됐다면 가장 먼저 발견된 1개만 제거한다.
        /// </summary>
        /// <typeparam name="TEvent">이벤트 타입.</typeparam>
        /// <param name="handler">해제할 콜백.</param>
        /// <returns>실제로 제거되었으면 true, 미등록 상태면 false.</returns>
        public static bool Unsubscribe<TEvent>(Action<TEvent> handler)
        {
            if (handler == null) return false;
            return Holder<TEvent>.Handlers.Remove(handler);
        }

        /// <summary>
        /// 해당 이벤트를 모든 구독자에게 전달한다.
        /// </summary>
        /// <typeparam name="TEvent">이벤트 타입.</typeparam>
        /// <param name="e">이벤트 인스턴스 (struct 권장).</param>
        /// <remarks>
        /// 핸들러 중 하나가 예외를 던져도 나머지 핸들러는 호출된다. 예외는 <see cref="Log"/> 로 기록.
        /// 호출 중 발생한 <see cref="Subscribe{TEvent}"/>·<see cref="Unsubscribe{TEvent}"/> 는 현재 iteration 에 영향 주지 않는다.
        /// </remarks>
        public static void Publish<TEvent>(TEvent e)
        {
            var handlers = Holder<TEvent>.Handlers;
            int count = handlers.Count;
            if (count == 0) return;

            // iteration snapshot: 현재 호출 중 추가·제거가 일어나도 본 배치는 원본 기준.
            // 배열 할당 비용은 구독자 수가 많은 이벤트에서만 의미가 있으나, 안전성 우선.
            Action<TEvent>[] snapshot = new Action<TEvent>[count];
            for (int i = 0; i < count; i++) snapshot[i] = handlers[i];

            for (int i = 0; i < count; i++)
            {
                try
                {
                    snapshot[i].Invoke(e);
                }
                catch (Exception ex)
                {
                    Log.Error("EventBus", $"핸들러 예외 ({typeof(TEvent).Name})", ex);
                }
            }
        }

        /// <summary>
        /// 특정 이벤트 타입의 모든 구독자를 제거한다. 테스트·씬 전환 시 유용.
        /// </summary>
        public static void Clear<TEvent>()
        {
            Holder<TEvent>.Handlers.Clear();
        }

        /// <summary>
        /// 지금까지 한 번이라도 Subscribe 된 모든 타입의 구독자를 일괄 제거한다.
        /// </summary>
        /// <remarks>주로 테스트 셋업·애플리케이션 종료 시 사용.</remarks>
        public static void ClearAll()
        {
            for (int i = 0; i < ClearCallbacks.Count; i++) ClearCallbacks[i].Invoke();
        }

        /// <summary>
        /// 특정 이벤트 타입의 현재 구독자 수 조회 (테스트·디버그용).
        /// </summary>
        public static int SubscriberCount<TEvent>() => Holder<TEvent>.Handlers.Count;

        private static void EnsureRegistered<TEvent>()
        {
            Type t = typeof(TEvent);
            if (RegisteredTypes.Contains(t)) return;
            RegisteredTypes.Add(t);
            ClearCallbacks.Add(() => Holder<TEvent>.Handlers.Clear());
        }
    }
}
