using System.Collections.Generic;
using UnityEngine.InputSystem;
using UnityEngine;

[RequireComponent(typeof(CharacterVoiceObject))]
public class DialogPlayer : MonoBehaviour
{
    [SerializeField] private List<DialogGroup> _dialogGroups;
    private Dictionary<string, DialogGroup> _dialogGroupMap;
    private Animator _animator;
    private int _initialGestureHash;
    private int _initialExpressionHash;
    private bool _hasInitialExpression;
    private readonly Dictionary<Transform, Quaternion> _originalRotations = new();
    public bool IsPlaying { get; private set; }

    private void Awake()
    {
        _dialogGroupMap = new Dictionary<string, DialogGroup>();
        foreach (var g in _dialogGroups)
            _dialogGroupMap[g.DialogGroupName] = g;

        _animator = GetComponentInChildren<Animator>();

        if (_animator != null)
        {
            _initialGestureHash = _animator.GetCurrentAnimatorStateInfo(0).fullPathHash;
            if (_animator.layerCount > 1)
            {
                _initialExpressionHash = _animator.GetCurrentAnimatorStateInfo(1).fullPathHash;
                _hasInitialExpression = true;
            }
        }
    }

    public void Play()
    {
        if(_dialogGroups.Count > 0)
            _ = Play(_dialogGroups[0].DialogGroupName);

    }

    public async Awaitable Play(string groupName)
    {
        if (IsPlaying) return;
        if (!_dialogGroupMap.TryGetValue(groupName, out var group))
        {
            Debug.LogWarning($"[DialogPlayer] 그룹 없음: {groupName}");
            return;
        }

        IsPlaying = true;
        try
        {
            var node = group.StartNode;
            while (node != null)
            {
                await PlayNode(node);

                if (node.Choices != null && node.Choices.Count > 0)
                {
                    int picked = await WaitForChoice(node);
                    node = node.Choices[picked].DestinationNode;
                }
                else
                {
                    node = node.Next;
                }
            }
        }
        finally
        {
            IsPlaying = false;
            RestoreDefaultAnimations();
            RestoreRotations();
        }

        Debug.Log("[DialogPlayer] 대화 종료");
    }

    private void RestoreDefaultAnimations()
    {
        if (_animator == null) return;
        _animator.CrossFade(_initialGestureHash, 0.3f, 0, normalizedTimeOffset: 0f);
        if (_hasInitialExpression)
            _animator.CrossFade(_initialExpressionHash, 0.3f, 1, normalizedTimeOffset: 0f);
    }

    private async Awaitable RotateTowardPlayer(Transform target)
    {
        if (Camera.main == null) return;
        var playerCam = Camera.main.transform;

        float duration = 0.5f;
        float elapsed = 0f;

        while (elapsed < duration)
        {
            Vector3 dir = playerCam.position - target.position;
            dir.y = 0f;
            if (dir.sqrMagnitude > 0.0001f)
            {
                var targetRot = Quaternion.LookRotation(dir);
                target.rotation = Quaternion.Slerp(target.rotation, targetRot, 10f * Time.deltaTime);
            }
            elapsed += Time.deltaTime;
            await Awaitable.NextFrameAsync();
        }
    }

    private void RestoreRotations()
    {
        foreach (var kvp in _originalRotations)
        {
            if (kvp.Key != null)
                _ = RotateToRotation(kvp.Key, kvp.Value);
        }
        _originalRotations.Clear();
    }

    private async Awaitable RotateToRotation(Transform target, Quaternion targetRotation)
    {
        float duration = 0.5f;
        float elapsed = 0f;
        while (elapsed < duration)
        {
            if (target == null) return;
            target.rotation = Quaternion.Slerp(target.rotation, targetRotation, 10f * Time.deltaTime);
            elapsed += Time.deltaTime;
            await Awaitable.NextFrameAsync();
        }
    }

    private async Awaitable PlayNode(DialogNode node)
    {
        var speakerName = node.Speaker != null ? node.Speaker.Name : "(누구?)";
        Debug.Log($"[{speakerName}] {node.TalkText}");

        // 보이스 재생
        if (node.Voice != null && node.Speaker != null)
        {
            var voiceObj = CharacterVoiceObject.Find(node.Speaker);
            if (voiceObj != null && node.Voice.Clip != null)
                voiceObj.Play(node.Voice.Clip);
        }

        // 플레이어 향해 회전
        if (node.LookAtPlayer && node.Speaker != null)
        {
            var voiceObj = CharacterVoiceObject.Find(node.Speaker);
            if (voiceObj != null)
            {
                _originalRotations.TryAdd(voiceObj.transform, voiceObj.transform.rotation);
                _ = RotateTowardPlayer(voiceObj.transform);
            }
        }

        if (node.Gesture != null)
            _animator.CrossFade(node.Gesture.StateName, node.Gesture.CrossFadeDuration, node.Gesture.AnimationLayer);
        if (node.Expression != null)
            _animator.CrossFade(node.Expression.StateName, node.Expression.CrossFadeDuration, node.Expression.AnimationLayer);

        // 대기 시간 결정
        float wait = 0f;
        if (node.Voice != null && node.Voice.Clip != null)
            wait = node.Voice.Clip.length;
        else
            wait = node.LineDuration;

        if (wait > 0f)
            await Awaitable.WaitForSecondsAsync(wait);
        else
            await WaitForAdvanceInput();  // 수동 진행
    }

    private async Awaitable<int> WaitForChoice(DialogNode node)
    {
        //선택을 기다리는 함수 수정해서 사용할것
        
        if (ChoiceHud.Instance == null)
        {
            Debug.LogWarning("[DialogPlayer] ChoiceHud 없음 — 0번 자동 선택");
            return 0;
        }
        return await ChoiceHud.Instance.Show(node.Speaker, node.ChoiceQuestion ,node.Choices);
    
    }

    private async Awaitable WaitForAdvanceInput()
    {
        // TODO: VR 컨트롤러 버튼 입력 대기. 일단은 1초 대기
        await Awaitable.WaitForSecondsAsync(1f);
    }

    //테스트용
    private void Update()
    {
        if (Mouse.current == null) return;
        if (!Mouse.current.leftButton.wasPressedThisFrame) return;
        if (Camera.main == null) return;

        var ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
        if (Physics.Raycast(ray, out var hit) && hit.transform.IsChildOf(transform))
        {
            Debug.Log("캐릭터 클릭");
            Play();
        }
    }
}
