using System;
using Customer;
using UnityEngine;
using UnityEngine.AI;
using static Customer.MoodState;
using Random = UnityEngine.Random;

public class CustomerController : MonoBehaviour, IObjectParentHolder {
    private NavMeshAgent _navMeshAgent;
    private Vector3 _target;

    public SellCounter TargetCounter { get; private set; }

    [Header("WaitTime")] [SerializeField] private float _minWaitTime = 5.0f;
    [SerializeField] private float _maxWaitTime = 10.0f;

    [Header("BuyChace")] [SerializeField] private float _buyChance = 0.5f;
    [SerializeField] private float _buyChanceIncrease = 0.1f;
    [SerializeField] private float _buyChanceDecrease = 0.1f;


    [SerializeField] private Transform _objectHolder;

    private Object _currentObject;

    private float _waitTime;

    private CustomerVisibleController _visibleController;

    public MoodState State => _visibleController.GetState();

    private Vector3 _exitPosition;
    public event Action<SellCounter, CustomerController> OnCustomerLeft;


    private CustomerState _state = CustomerState.GoingToCounter;

    //enum
    public enum CustomerState {
        GoingToCounter,
        WaitingToBuy,
        Buying,
        WalkingToExit
    }

    private void Awake() {
        _navMeshAgent = GetComponent<NavMeshAgent>();
        _visibleController = GetComponent<CustomerVisibleController>();

        _visibleController.SetState(Normal);
    }

    private void Update() {
        switch (_state) {
            case CustomerState.GoingToCounter:
                if (_navMeshAgent.remainingDistance <= _navMeshAgent.stoppingDistance) {
                    OnDestinationReached();
                }

                break;
            case CustomerState.WaitingToBuy:
                WaitToBuy();
                break;
            case CustomerState.Buying:
                BuyItem();
                break;
            case CustomerState.WalkingToExit:
                CheckIfReachedExit();
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    private void CheckIfReachedExit() {
        if (_navMeshAgent.remainingDistance <= _navMeshAgent.stoppingDistance) {
            Debug.Log("Bye :p");
            CustomerManager.Instance.CustomerDies(this);
            Destroy(this.gameObject);
        }
    }

    private bool ShouldBuyItem() {
        //This should be a function that checks if a customer should buy an item
        //For example, if the item is worth 10 bucks, and the player sets the price to 20, the customer should not buy it since its too expensive

        //if an item is prices higher then X% of the base price, the customer should not buy it
        Object obj = TargetCounter.GetObject();
        if (obj == null) {
            Debug.LogError("Counter has no object");
            return false;
        }

        float price = obj.ObjectSo.basePrice;
        float sellPrice = TargetCounter.GetSellingPrice();
        bool isBroken = obj.IsBroken;

        float difference = Mathf.Abs(price - sellPrice);
        float percentage = difference / price;

        //Log this for now
        Debug.Log("Difference: " + difference + " Percentage: " + percentage);
        //Random differnece between 0.1 and 0.2;
        float diff = Random.Range(_buyChanceDecrease, _buyChanceIncrease);

        var calculatedBuyChance = _buyChance;

        if (isBroken) {
            //Customer should be less likely to buy broken items
            calculatedBuyChance -= 0.1f;
        }

        if (percentage > calculatedBuyChance + diff) {
            return false;
        }

        return true;
    }

    public void SetDestination(Vector3 position) {
        _target = position;
        _navMeshAgent.SetDestination(_target);
    }

    public void SetTargetCounter(SellCounter counter) {
        TargetCounter = counter;

        float angle = Random.Range(0, 360);
        Vector3 offset = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle));
        this.SetDestination(counter.transform.position + offset);
    }

    private void BuyItem() {
        if (TargetCounter == null) {
            Debug.LogError("Customer has no target counter");
            return;
        }

        if (!ShouldBuyItem()) {
            Debug.Log("Customer should not buy item");
            this.SetDestination(_exitPosition);
            _state = CustomerState.WalkingToExit;
            _visibleController.SetState(Angry);
            SoundFXController.Instance.PlayVillagerAngryFX();
            return;
        }

        Debug.Log("Customer bought item: " + TargetCounter.GetObject().ObjectSo.objectName + " for: " +
                  TargetCounter.GetSellingPrice());
        _visibleController.SetState(Normal);
        TargetCounter.SellItem(this);
        SoundFXController.Instance.PlayItemBoughtFX();
        SoundFXController.Instance.PlayVillagerHappyFX();
        this.SetDestination(_exitPosition);
        _state = CustomerState.WalkingToExit;
        OnCustomerLeft?.Invoke(TargetCounter, this); //Notify the customer manager that the customer left
    }

    private void OnDestinationReached() {
        //should tell the counter there is a customer waiting;
        Debug.Log("Customer reached destination for Counter: " + TargetCounter.name);
        _state = CustomerState.WaitingToBuy;
        _waitTime = Random.Range(_minWaitTime, _maxWaitTime);
    }

    void OnDrawGizmosSelected() {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(_target, 0.5f);
    }

    void WaitToBuy() {
        _waitTime -= Time.deltaTime;
        if (_waitTime <= 0) {
            _state = CustomerState.Buying;
        }
    }

    public Transform GetHolderTransform() {
        return _objectHolder;
    }

    public void SetObject(Object obj) {
        _currentObject = obj;
    }

    public Object GetObject() {
        return _currentObject;
    }

    public void ClearObject() {
        _currentObject = null;
    }

    public bool HasObject() {
        return _currentObject != null;
    }

    public void SetExitPosition(Vector3 position) {
        _exitPosition = position;
    }


    public void SetState(CustomerState state) {
        _state = state;
    }
}