// Copyright (c) 2026 EzAdmin. All rights reserved.
// Licensed under the EzAdmin Client License. See LICENSE-CLIENT.txt.

using UnityEngine;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif

namespace EzAdmin.Client
{

    [DisallowMultipleComponent]
    [RequireComponent(typeof(EzAdminLinkPanel))]
    public sealed class EzAdminKeyboardInput : MonoBehaviour
    {
        [Tooltip("Accept typing from a physical keyboard (PC, or a keyboard plugged into anything).")]
        [SerializeField] private bool _physicalKeyboard = true;

        [Tooltip("Allow Ctrl+V / Cmd+V to paste a code copied from Discord. Desktop only.")]
        [SerializeField] private bool _allowPaste = true;

        [Tooltip("Submit automatically when the system keyboard is confirmed.")]
        [SerializeField] private bool _submitFromSystemKeyboard = true;

        private EzAdminLinkPanel _panel;
        private TouchScreenKeyboard _systemKeyboard;

        public bool SystemKeyboardAvailable
        {
            get { return TouchScreenKeyboard.isSupported; }
        }

        private void Awake()
        {
            _panel = GetComponent<EzAdminLinkPanel>();
        }

#if ENABLE_INPUT_SYSTEM
        private void OnEnable()
        {
            if (Keyboard.current != null)
            {
                Keyboard.current.onTextInput += OnTextInput;
            }
        }

        private void OnDisable()
        {
            if (Keyboard.current != null)
            {
                Keyboard.current.onTextInput -= OnTextInput;
            }
            CloseSystemKeyboard();
        }

        private void OnTextInput(char c)
        {
            if (_physicalKeyboard && !ATextFieldHasFocus())
            {
                HandleCharacter(c);
            }
        }
#else
        private void OnDisable()
        {
            CloseSystemKeyboard();
        }
#endif

        public void OpenSystemKeyboard()
        {
            if (!TouchScreenKeyboard.isSupported || _systemKeyboard != null)
            {
                return;
            }

            _systemKeyboard = TouchScreenKeyboard.Open(
                _panel.CurrentCode,
                TouchScreenKeyboardType.ASCIICapable,
                false,
                false,
                false,
                false,
                "EzAdmin code",
                EzAdminCode.Length);
        }

        private void CloseSystemKeyboard()
        {
            if (_systemKeyboard != null)
            {
                _systemKeyboard.active = false;
                _systemKeyboard = null;
            }
        }

        private void Update()
        {
            PollSystemKeyboard();

            if (!_physicalKeyboard || ATextFieldHasFocus())
            {
                return;
            }

#if ENABLE_INPUT_SYSTEM
            Keyboard keyboard = Keyboard.current;
            if (keyboard == null)
            {
                return;
            }

            if (keyboard.backspaceKey.wasPressedThisFrame)
            {
                _panel.Backspace();
            }

            if (keyboard.enterKey.wasPressedThisFrame || keyboard.numpadEnterKey.wasPressedThisFrame)
            {
                _panel.Submit();
            }

            if (_allowPaste && keyboard.vKey.wasPressedThisFrame
                && (keyboard.ctrlKey.isPressed || keyboard.leftCommandKey.isPressed || keyboard.rightCommandKey.isPressed))
            {
                Paste();
            }
#elif ENABLE_LEGACY_INPUT_MANAGER

            string typed = Input.inputString;
            for (int i = 0; i < typed.Length; i++)
            {
                HandleCharacter(typed[i]);
            }

            bool modifier = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)
                            || Input.GetKey(KeyCode.LeftCommand) || Input.GetKey(KeyCode.RightCommand);
            if (_allowPaste && modifier && Input.GetKeyDown(KeyCode.V))
            {
                Paste();
            }
#endif
        }

        private void HandleCharacter(char c)
        {
            if (c == '\b')
            {
                _panel.Backspace();
                return;
            }

            if (c == '\n' || c == '\r')
            {
#if !ENABLE_INPUT_SYSTEM
                _panel.Submit();
#endif
                return;
            }

            if (char.IsControl(c))
            {
                return;
            }

            _panel.AppendCharacter(c.ToString());
        }

        private void Paste()
        {
            if (!_allowPaste)
            {
                return;
            }

            string clipboard = GUIUtility.systemCopyBuffer;
            if (!string.IsNullOrEmpty(clipboard))
            {
                _panel.SetCode(clipboard);
            }
        }

        private void PollSystemKeyboard()
        {
            if (_systemKeyboard == null)
            {
                return;
            }

            switch (_systemKeyboard.status)
            {
                case TouchScreenKeyboard.Status.Done:
                    _panel.SetCode(_systemKeyboard.text);
                    _systemKeyboard = null;
                    if (_submitFromSystemKeyboard)
                    {
                        _panel.Submit();
                    }
                    break;

                case TouchScreenKeyboard.Status.Canceled:
                case TouchScreenKeyboard.Status.LostFocus:
                    _systemKeyboard = null;
                    break;
            }
        }

        private static bool ATextFieldHasFocus()
        {
            EventSystem events = EventSystem.current;
            if (events == null)
            {
                return false;
            }

            GameObject selected = events.currentSelectedGameObject;
            if (selected == null)
            {
                return false;
            }

            UnityEngine.UI.InputField legacyField = selected.GetComponent<UnityEngine.UI.InputField>();
            if (legacyField != null && legacyField.isFocused)
            {
                return true;
            }

            return selected.GetComponent("TMP_InputField") != null;
        }
    }
}
