using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UVC.Locale;
using UVC.Log;
using UVC.UI.Commands;

namespace UVC.UI.Menu
{
    /// <summary>
    /// 상단 메뉴의 로직을 관리하는 컨트롤러 클래스입니다.
    /// Model(데이터: <see cref="TopMenuModel"/>)과 View(표시: <see cref="TopMenuView"/>) 사이의 중재자 역할을 하며,
    /// 메뉴 아이템 초기화, 사용자 입력 처리, 언어 변경 감지 등의 기능을 수행합니다.
    /// 이 클래스는 MonoBehaviour를 상속받아 Unity 게임 오브젝트에 컴포넌트로 추가될 수 있습니다.
    /// Inspector에서 <see cref="TopMenuController"/>와 <see cref="TopMenuView"/> 추가해서 사용합니다.
    /// </summary>
    /// <example>
    /// 다음은 TopMenuController를 상속받아 특정 기능을 확장하는 예제입니다.
    /// <code>
    /// public class CustomTopMenuController : TopMenuController
    /// {
    ///     protected override void InitializeMenuItems()
    ///     {
    ///         // 기본 메뉴 아이템 초기화 로직 호출
    ///         base.InitializeMenuItems();
    ///
    ///         // 기존 모델에 새로운 메뉴 아이템 추가 또는 수정
    ///         // 예: '도움말' 메뉴 추가
    ///         model.MenuItems.Add(new MenuItemData("help", "menu_help", subMenuItems: new List&lt;MenuItemData&gt;
    ///         {
    ///             new MenuItemData("help_about", "menu_help_about", new DebugLogCommand("도움말 > 정보 선택됨"))
    ///         }));
    ///
    ///         // 변경된 모델을 기반으로 뷰를 다시 생성하거나 업데이트해야 할 수 있습니다.
    ///         // (이미 Start 메서드에서 CreateMenuItems가 호출되므로, InitializeMenuItems 시점에서는 모델만 수정)
    ///         ULog.Debug("CustomTopMenuController: '도움말' 메뉴가 추가되었습니다.");
    ///     }
    ///
    ///     protected override void HandleMenuItemClicked(MenuItemData clickedItemData)
    ///     {
    ///         // 기본 클릭 처리 로직 호출
    ///         base.HandleMenuItemClicked(clickedItemData);
    ///
    ///         // 특정 메뉴 아이템에 대한 추가적인 커스텀 로직 수행
    ///         if (clickedItemData.ItemId == "file_exit")
    ///         {
    ///             ULog.Debug("CustomTopMenuController: 애플리케이션 종료 메뉴가 선택되었습니다. 추가 확인 로직을 여기에 넣을 수 있습니다.");
    ///             // 예: 사용자에게 정말 종료할 것인지 확인하는 팝업 표시 등
    ///         }
    ///     }
    ///
    ///     // 새로운 기능을 추가할 수도 있습니다.
    ///     public void AddCustomMenuOption(string parentItemId, MenuItemData newItem)
    ///     {
    ///         if (model == null || model.MenuItems == null) return;
    ///
    ///         MenuItemData parentItem = FindMenuItemRecursive(model.MenuItems, parentItemId);
    ///         if (parentItem != null)
    ///         {
    ///             if (parentItem.IsSeparator)
    ///             {
    ///                 ULog.Warning($"구분선('{parentItemId}')에는 하위 메뉴를 추가할 수 없습니다.");
    ///                 return;
    ///             }
    ///             parentItem.AddSubMenuItem(newItem);
    ///             ULog.Debug($"'{parentItemId}'에 새로운 하위 메뉴 '{newItem.ItemId}'가 추가되었습니다.");
    ///
    ///             // 중요: 모델 변경 후에는 뷰를 업데이트해야 합니다.
    ///             // 예를 들어, 메뉴를 전부 다시 그리거나 특정 부분만 업데이트하는 메서드를 호출합니다.
    ///             // view.ClearMenuItems();
    ///             // view.CreateMenuItems(model.MenuItems, view.MenuContainer);
    ///             // 또는 더 정교한 뷰 업데이트 메서드가 필요할 수 있습니다.
    ///         }
    ///         else
    ///         {
    ///             ULog.Warning($"ID가 '{parentItemId}'인 부모 메뉴 아이템을 찾을 수 없습니다.");
    ///         }
    ///     }
    /// }
    /// </code>
    /// </example>
    public class TopMenuController : MonoBehaviour
    {
        /// <summary>
        /// 메뉴의 시각적 표현을 담당하는 <see cref="TopMenuView"/> 컴포넌트에 대한 참조입니다.
        /// Inspector에서 할당하거나 <see cref="Awake"/> 메서드에서 자동으로 찾습니다.
        /// </summary>
        protected TopMenuView view;

        /// <summary>
        /// 메뉴 아이템들의 데이터 구조를 관리하는 <see cref="TopMenuModel"/> 인스턴스입니다.
        /// 메뉴의 내용(예: 아이템 이름, 명령, 하위 메뉴)을 저장합니다.
        /// </summary>
        protected TopMenuModel model;

        /// <summary>
        /// 다국어 처리를 위한 <see cref="LocalizationManager"/>의 인스턴스입니다.
        /// 메뉴 아이템의 텍스트를 현재 설정된 언어에 맞게 표시하는 데 사용됩니다.
        /// </summary>
        protected LocalizationManager _locManager;

        /// <summary>
        /// MonoBehaviour의 Awake 메시지입니다. 스크립트 인스턴스가 로드될 때 호출됩니다.
        /// 주로 <see cref="view"/> 컴포넌트를 찾는 데 사용됩니다.
        /// 상속 클래스에서 오버라이드하여 추가적인 초기화 로직을 구현할 수 있습니다.
        /// </summary>
        protected virtual void Awake()
        {
            // 1. 이 GameObject에 연결된 TopMenuView 컴포넌트를 찾습니다.
            view = GetComponent<TopMenuView>();

            // 2. 만약 현재 GameObject에 없다면, 자식 GameObject들 중에서 TopMenuView 컴포넌트를 찾습니다.
            if (view == null)
            {
                view = GetComponentInChildren<TopMenuView>();
            }

            // view가 여전히 null이라면, Start 메서드에서 오류를 기록할 것입니다.
        }

        /// <summary>
        /// MonoBehaviour의 Start 메시지입니다. 첫 번째 프레임 업데이트 전에 호출됩니다.
        /// 모델 초기화, <see cref="LocalizationManager"/> 인스턴스 가져오기, 메뉴 아이템 데이터 설정,
        /// View에 메뉴 생성 요청, 이벤트 핸들러 등록 등의 주요 초기화 작업을 수행합니다.
        /// </summary>
        protected virtual void Start()
        {
            // 메뉴 데이터 모델 인스턴스 생성
            model = new TopMenuModel();
            // 다국어 관리자 인스턴스 가져오기
            _locManager = LocalizationManager.Instance;

            // View 컴포넌트가 할당되었는지 확인
            if (view == null)
            {
                ULog.Error("TopMenuView가 Inspector에서 할당되지 않았거나 찾을 수 없습니다. TopMenuController가 정상적으로 작동하지 않습니다.");
                return; // View가 없으면 더 이상 진행할 수 없음
            }

            // LocalizationManager 인스턴스 확인
            if (_locManager == null)
            {
                ULog.Error("LocalizationManager 인스턴스를 찾을 수 없습니다. 메뉴 텍스트가 올바르게 표시되지 않거나, 언어 변경 기능이 작동하지 않을 수 있습니다.");
                // _locManager가 null이어도 메뉴 구조 자체는 생성될 수 있도록 계속 진행합니다.
                // TopMenuView와 이 클래스의 다른 부분에서 _locManager null 체크를 통해 안전하게 처리합니다.
            }

            // 메뉴 아이템 데이터 초기화 (모델 채우기)
            InitializeMenuItems();

            // View에 기존 메뉴 아이템들을 지우도록 요청
            view.ClearMenuItems();
            // View에 현재 모델 데이터를 기반으로 메뉴 UI를 생성하도록 요청
            // view.MenuContainer는 TopMenuView에서 메뉴 UI 요소들이 배치될 부모 Transform을 가리킵니다.
            view.CreateMenuItems(model.MenuItems, view.MenuContainer);

            // View에서 발생하는 메뉴 아이템 클릭 이벤트에 대한 핸들러 등록
            view.OnMenuItemClicked += HandleMenuItemClicked;

            // LocalizationManager가 존재하고, 언어 변경 이벤트를 지원한다면 핸들러 등록
            if (_locManager != null)
            {
                _locManager.OnLanguageChanged += HandleLanguageChanged;
            }
        }

        /// <summary>
        /// MonoBehaviour의 OnDestroy 메시지입니다. GameObject가 파괴될 때 호출됩니다.
        /// 등록된 이벤트 핸들러들을 해제하여 메모리 누수 및 잠재적 오류를 방지합니다.
        /// </summary>
        protected virtual void OnDestroy()
        {
            // View의 메뉴 아이템 클릭 이벤트 핸들러 해제
            if (view != null)
            {
                view.OnMenuItemClicked -= HandleMenuItemClicked;
            }
            // LocalizationManager의 언어 변경 이벤트 핸들러 해제
            if (_locManager != null)
            {
                _locManager.OnLanguageChanged -= HandleLanguageChanged;
            }
        }

        /// <summary>
        /// 메뉴 아이템 데이터를 초기화하고 <see cref="model"/>에 추가합니다.
        /// 이 메서드에서 메뉴의 전체 구조(항목, 하위 항목, 구분선, 연결된 명령 등)를 정의합니다.
        /// 상속 클래스에서 이 메서드를 오버라이드하여 메뉴 구성을 변경하거나 확장할 수 있습니다.
        /// </summary>
        protected virtual void InitializeMenuItems()
        {
            // 기존 메뉴 아이템 목록을 비웁니다.
            model.MenuItems.Clear();

            // "파일" 메뉴 및 하위 메뉴들 정의
            model.MenuItems.Add(new MenuItemData("file", "menu_file", subMenuItems: new List<MenuItemData>
            {
                new MenuItemData("file_new", "menu_file_new", subMenuItems: new List<MenuItemData>
                {
                    new MenuItemData("file_new_project", "menu_file_new_project", new DebugLogCommand("새 프로젝트 선택됨 (Command 실행)")),
                    new MenuItemData("file_new_file", "menu_file_new_file",
                        new ActionCommand(() => Debug.Log("[SampleProject] 새 파일 선택됨")))
                }),
                new MenuItemData("file_open", "menu_file_open",
                        new ActionCommand<string>((path) => Debug.Log($"[SampleProject] 파일 열기 선택됨: {path}"), "sample.txt"),
                        commandParameter: "another_sample.txt", // 이 파라미터가 HandleMenuItemClicked에서 사용됨
                        isEnabled: false), // "파일 열기"는 비활성화 상태로 시작
                MenuItemData.CreateSeparator("file_sep1"), // 구분선 추가
                new MenuItemData("file_save", "menu_file_save", command: new DebugLogCommand("저장 선택됨 (Command 실행)") , subMenuItems: new List<MenuItemData>
                {
                    new MenuItemData("file_save_as", "menu_file_save_as", new DebugLogCommand("다른 이름으로 저장 선택됨 (Command 실행)"))
                }),
                MenuItemData.CreateSeparator("file_sep2"), // 또 다른 구분선 추가
                new MenuItemData("file_exit", "menu_file_exit", new QuitApplicationCommand()) // 애플리케이션 종료 명령 연결
            }));

            // "편집" 메뉴 및 하위 메뉴들 정의
            model.MenuItems.Add(new MenuItemData("edit", "menu_edit", subMenuItems: new List<MenuItemData>
            {
                new MenuItemData("edit_undo", "menu_edit_undo", new DebugLogCommand("실행 취소 선택됨 (Command 실행)")),
                new MenuItemData("edit_redo", "menu_edit_redo", new DebugLogCommand("다시 실행 선택됨 (Command 실행)")),
                MenuItemData.CreateSeparator("edit_sep1"), // 구분선
                new MenuItemData("preferences", "menu_preferences", new DebugLogCommand("환경설정 선택됨 (Command 실행)"))
            }));

            model.MenuItems.Add(new MenuItemData("language", "menu_language", subMenuItems: new List<MenuItemData>
            {
                // 각 언어 메뉴 아이템에 ChangeLanguageCommand를 연결하여 언어 변경 기능 수행
                new MenuItemData("lang_ko", "menu_lang_korean", new ChangeLanguageCommand("ko-KR"), commandParameter: "ko-KR"),
                new MenuItemData("lang_en", "menu_lang_english", new ChangeLanguageCommand("en-US"))
                // 필요에 따라 다른 언어들도 추가 가능
            }));
        }

        /// <summary>
        /// <see cref="TopMenuView.OnMenuItemClicked"/> 이벤트가 발생했을 때 호출되는 핸들러입니다.
        /// 클릭된 메뉴 아이템(<paramref name="clickedItemData"/>)의 유효성을 검사하고,
        /// 연결된 <see cref="ICommand"/>를 실행합니다.
        /// </summary>
        /// <param name="clickedItemData">사용자가 클릭한 메뉴 아이템의 데이터입니다.</param>
        protected virtual void HandleMenuItemClicked(MenuItemData clickedItemData)
        {
            // 클릭된 아이템이 구분선이거나 비활성화 상태인지 확인
            if (clickedItemData.IsSeparator)
            {
                if (!clickedItemData.IsEnabled)
                {
                    Debug.Log($"비활성화된 메뉴 아이템 클릭 시도: {clickedItemData.ItemId} (표시 키: {clickedItemData.DisplayNameKey})");
                }
                return;
            }

            if (!clickedItemData.IsEnabled)
            {
                // 비활성화된 아이템 클릭 시 로그 (디버깅 목적)
                ULog.Debug($"비활성화된 메뉴 아이템 클릭 시도: {clickedItemData.ItemId} (표시 키: {clickedItemData.DisplayNameKey})");
                return; // 비활성화된 아이템은 명령을 실행하지 않음
            }

            // 클릭된 메뉴 아이템 정보 로그 (디버깅 목적)
            ULog.Debug($"메뉴 아이템 클릭됨: {clickedItemData.ItemId} (표시 키: {clickedItemData.DisplayNameKey})");

            // 메뉴 아이템에 연결된 Command가 있다면 실행
            // Command가 null일 수 있으므로 null 조건부 연산자(?.) 사용
            clickedItemData.Command?.Execute(clickedItemData.CommandParameter);
        }

        /// <summary>
        /// <see cref="LocalizationManager.OnLanguageChanged"/> 이벤트가 발생했을 때 호출되는 핸들러입니다.
        /// 새로운 언어 코드(<paramref name="newLanguageCode"/>)가 적용되었음을 감지하고,
        /// <see cref="view"/>에 모든 메뉴 텍스트를 업데이트하도록 요청합니다.
        /// </summary>
        /// <param name="newLanguageCode">새롭게 설정된 언어 코드입니다 (예: "ko-KR", "en-US").</param>
        protected virtual void HandleLanguageChanged(string newLanguageCode)
        {
            ULog.Debug($"언어 변경 감지됨: {newLanguageCode}. 메뉴 텍스트 업데이트를 시도합니다...");
            // View와 Model이 모두 유효한 경우에만 텍스트 업데이트 진행
            if (view != null && model != null)
            {
                view.UpdateAllMenuTexts(model.MenuItems);
                ULog.Debug("메뉴 텍스트 업데이트 완료.");
            }
            else
            {
                ULog.Warning("View 또는 Model이 null이므로 메뉴 텍스트를 업데이트할 수 없습니다.");
            }
        }

        /// <summary>
        /// 특정 ID를 가진 메뉴 아이템의 활성화 상태를 동적으로 변경합니다.
        /// 이 메서드는 모델(<see cref="TopMenuModel"/>)의 데이터를 변경하고,
        /// 변경 사항을 뷰(<see cref="TopMenuView"/>)에 반영하여 UI의 버튼 상호작용 상태를 업데이트합니다.
        /// </summary>
        /// <param name="itemId">상태를 변경할 메뉴 아이템의 고유 ID입니다.</param>
        /// <param name="isEnabled">새로운 활성화 상태입니다 (true: 활성, false: 비활성).</param>
        public virtual void SetMenuItemEnabled(string itemId, bool isEnabled)
        {
            if (model == null || model.MenuItems == null)
            {
                ULog.Warning("모델이 초기화되지 않아 메뉴 아이템 활성화 상태를 변경할 수 없습니다.");
                return;
            }

            // 모델에서 해당 ID를 가진 메뉴 아이템을 재귀적으로 검색
            MenuItemData targetItem = FindMenuItemRecursive(model.MenuItems, itemId);

            if (targetItem != null)
            {
                // 구분선의 활성화 상태는 변경할 수 없음
                if (targetItem.IsSeparator)
                {
                    ULog.Warning($"구분선('{itemId}')의 활성화 상태는 변경할 수 없습니다. 작업이 무시됩니다.");
                    return;
                }

                // 이미 요청된 상태와 동일하다면 변경하지 않음 (불필요한 업데이트 방지)
                if (targetItem.IsEnabled == isEnabled)
                {
                    ULog.Debug($"메뉴 아이템 '{itemId}'은(는) 이미 요청된 활성화 상태({isEnabled})입니다.");
                    return;
                }

                // 1. 모델 데이터의 활성화 상태 변경
                targetItem.IsEnabled = isEnabled;
                ULog.Debug($"모델에서 메뉴 아이템 '{itemId}'의 활성화 상태가 '{isEnabled}'(으)로 변경되었습니다.");

                // 2. View 업데이트: 해당 메뉴 아이템 GameObject를 찾아 Button 컴포넌트의 interactable 속성 갱신
                if (view != null && view.isActiveAndEnabled) // View가 유효하고 활성화된 상태일 때만 시도
                {
                    if (view.TryGetMenuItemGameObject(itemId, out GameObject menuItemGO))
                    {
                        Button button = menuItemGO.GetComponent<Button>();
                        if (button != null)
                        {
                            button.interactable = isEnabled;
                            ULog.Debug($"View에서 메뉴 아이템 '{itemId}'의 버튼 상호작용 상태가 '{isEnabled}'(으)로 업데이트되었습니다.");
                            // 필요에 따라, 텍스트 색상 등 다른 시각적 요소도 여기서 업데이트 할 수 있습니다.
                            // 예: TextMeshProUGUI textComponent = menuItemGO.GetComponentInChildren<TextMeshProUGUI>();
                            //     if (textComponent != null) textComponent.color = isEnabled ? Color.black : Color.gray;
                        }
                        else
                        {
                            ULog.Warning($"메뉴 아이템 GameObject ('{itemId}')에서 Button 컴포넌트를 찾을 수 없어 View를 업데이트할 수 없습니다.");
                        }
                    }
                    else
                    {
                        ULog.Warning($"View에서 ID가 '{itemId}'인 메뉴 아이템 GameObject를 찾을 수 없어 View를 업데이트할 수 없습니다.");
                    }
                }
                else if (view == null || !view.isActiveAndEnabled)
                {
                    ULog.Warning("View가 유효하지 않거나 비활성화 상태이므로, 메뉴 아이템 버튼의 상호작용 상태를 업데이트할 수 없습니다. 모델 데이터만 변경되었습니다.");
                }
            }
            else
            {
                ULog.Warning($"ID가 '{itemId}'인 메뉴 아이템을 모델에서 찾을 수 없어 활성화 상태를 변경할 수 없습니다.");
            }
        }

        /// <summary>
        /// 제공된 메뉴 아이템 리스트(<paramref name="items"/>)와 그 하위 메뉴들을 재귀적으로 탐색하여
        /// 지정된 ID(<paramref name="itemId"/>)를 가진 <see cref="MenuItemData"/>를 찾습니다.
        /// </summary>
        /// <param name="items">검색을 시작할 메뉴 아이템 데이터 리스트입니다.</param>
        /// <param name="itemId">찾고자 하는 메뉴 아이템의 고유 ID입니다.</param>
        /// <returns>ID와 일치하는 <see cref="MenuItemData"/>를 찾으면 해당 객체를 반환하고, 찾지 못하면 null을 반환합니다.</returns>
        protected MenuItemData FindMenuItemRecursive(List<MenuItemData> items, string itemId)
        {
            if (items == null || string.IsNullOrEmpty(itemId)) return null;

            foreach (var item in items)
            {
                if (item.ItemId == itemId)
                {
                    return item; // 현재 아이템이 찾던 아이템이면 반환
                }

                // 현재 아이템에 하위 메뉴가 있다면, 하위 메뉴에 대해 재귀적으로 검색 수행
                if (item.SubMenuItems != null && item.SubMenuItems.Count > 0)
                {
                    MenuItemData foundInSubMenu = FindMenuItemRecursive(item.SubMenuItems, itemId);
                    if (foundInSubMenu != null)
                    {
                        return foundInSubMenu; // 하위 메뉴에서 찾았으면 반환
                    }
                }
            }
            return null; // 현재 리스트 및 그 하위 리스트에서 아이템을 찾지 못함
        }
    }
}
