using UnityEngine;

public class NPC : MonoBehaviour
{
    public enum State { Idle, Move, Talk, Despair, Approach }

    [SerializeField] private NPCData _data;

    private State _state, _prevState;
    private float _timer;
    private AnimationController _animController;
    private NPCMovement _movement;
    private NPCTalkSystem _talk;

    public NPCData Data => _data;
    public State CurrentState => _state;

    public void Initialize(NPCData data, SpeechBubbleManager bubbleManager, Pathfinder pathfinder)
    {
        _data = data;
        _animController = GetComponent<AnimationController>();
        _movement = GetComponent<NPCMovement>();
        _talk = GetComponent<NPCTalkSystem>();

        _movement.Initialize(pathfinder);
        _movement.SetSpeed(data.MoveSpeed);
        _talk.Initialize(data, bubbleManager);

        _timer = Random.Range(1f, 3f);
    }

    public void SetState(State state) => _state = state;

    void Update()
    {
        if (_data == null || !_movement.IsPositionValid()) return;

        _prevState = _state;
        _timer -= Time.deltaTime;
        _talk.Tick();

        switch (_state)
        {
            case State.Idle:
                UpdateIdle();
                break;
            case State.Move:
                UpdateMove();
                break;
            case State.Approach:
                UpdateApproach();
                break;
            case State.Talk:
                UpdateTalkState();
                break;
            case State.Despair:
                UpdateDespair();
                break;
        }

        // On state changed: update animation + speech bubble
        if (_state != _prevState)
            OnStateChanged();
    }

    void UpdateIdle()
    {
        if (_timer <= 0)
        {
            if (_movement.FindNewPath())
                _state = State.Move;
            else
                _timer = Random.Range(1f, 2f);
        }
    }

    void UpdateMove()
    {
        _movement.FollowPath();

        if (_movement.IsArrived)
        {
            _state = State.Idle;
            return;
        }

        if (_movement.IsStuck)
        {
            _movement.ResetStuck();
            if (!_movement.FindNewPath())
                _state = State.Despair;
            return;
        }

        // Detect nearby NPC → try to talk
        var nearby = _talk.CheckNearby(this);
        if (nearby != null)
        {
            StartApproach(nearby);
            nearby.StartApproach(this);
        }
    }

    void UpdateApproach()
    {
        var target = _talk.TalkTarget;

        if (target == null || !target.GetComponent<NPCMovement>().IsPositionValid() ||
            (target.CurrentState != State.Approach && target.CurrentState != State.Talk))
        {
            _talk.ClearTarget();
            _state = State.Idle;
            return;
        }

        // Reached talk distance
        if (_talk.IsInTalkRange(target))
        {
            _talk.StartTalk(target, true);
            _state = State.Talk;
            _movement.Stop();
            _movement.FaceTarget(target.transform);
            PlayAnim(PlayerState.IDLE, _data.GetAnimationIndex(State.Talk));

            if (target.CurrentState == State.Approach)
            {
                var targetTalk = target.GetComponent<NPCTalkSystem>();
                targetTalk.StartTalk(this, false);
                target.SetState(State.Talk);
                target.GetComponent<NPCMovement>().Stop();
                target.GetComponent<NPCMovement>().FaceTarget(transform);
                target.GetComponent<AnimationController>().Play(PlayerState.IDLE,
                    target.Data.GetAnimationIndex(State.Talk));
            }
            return;
        }

        // Follow path to target
        if (_movement.IsArrived)
        {
            if (!_movement.FindPathTo(target.transform))
            {
                _talk.ClearTarget();
                _state = State.Idle;
                return;
            }
        }

        _movement.FollowPath();

        if (_movement.IsStuck)
        {
            _movement.ResetStuck();
            _talk.ClearTarget();
            _state = State.Idle;
        }
    }

    void UpdateTalkState()
    {
        bool ended = _talk.UpdateTalk();
        if (ended)
            _state = State.Idle;
    }

    void UpdateDespair()
    {
        if (_timer <= 0)
        {
            if (_movement.FindNewPath())
                _state = State.Move;
            else
                _state = State.Idle;
        }
    }

    public void StartApproach(NPC other)
    {
        if (_state == State.Talk || _state == State.Approach) return;
        if (!_talk.CanApproach(other)) return;

        _talk.SetTarget(other);
        _state = State.Approach;
        _movement.Stop();
    }

    void OnStateChanged()
    {
        int animIdx = _data.GetAnimationIndex(_state);

        switch (_state)
        {
            case State.Idle:
                _timer = Random.Range(3f, 6f);
                _movement.Stop();
                PlayAnim(PlayerState.IDLE, animIdx);
                break;
            case State.Move:
                PlayAnim(PlayerState.MOVE, animIdx);
                break;
            case State.Approach:
                PlayAnim(PlayerState.MOVE, animIdx);
                break;
            case State.Talk:
                _movement.Stop();
                PlayAnim(PlayerState.IDLE, animIdx);
                break;
            case State.Despair:
                _movement.Stop();
                _timer = 1f;
                PlayAnim(PlayerState.OTHER, animIdx);
                break;
        }

        if (_state != State.Talk)
            _talk.ShowBubble(_state);
    }

    void PlayAnim(PlayerState state, int index = 0)
    {
        _animController.Play(state, index);
    }
}
