using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using NaughtyAttributes;
using UnityEngine;

namespace EscapeRoomEngine.Engine.Runtime.Modules
{
    /// <summary>
    /// The <see cref="PuzzleState"/> of a puzzle that requires a specific state to be solved.
    /// </summary>
    public class StatePuzzle : PuzzleState
    {
        [Tooltip("The state this puzzle starts at.")]
        [BoxGroup("State Puzzle")]
        [SerializeField]
        protected List<int> states;
        [Tooltip("The state that needs to be reached for this puzzle to be solved.")]
        [BoxGroup("State Puzzle")]
        [SerializeField]
        protected List<int> solution;
        [Tooltip("Set this to the total number of states this puzzle has.")]
        [BoxGroup("State Puzzle")]
        [SerializeField]
        [ValidateInput("CorrectStateCount", "States count must be equal to the number of states and the length of the solution.")]
        [Min(0)]
        protected int stateCount;
        [BoxGroup("State Puzzle")]
        [ProgressBar("Correct States", "stateCount", EColor.Orange)]
        public int correctStates;

        private List<int> _initialStates;

        protected virtual void Awake()
        {
            _initialStates = new List<int>(states);
        }

        protected override void Start()
        {
            PuzzleEvent += (_, type) =>
            {
                switch (type)
                {
                    case PuzzleEventType.Restarted:
                        for (var i = 0; i < stateCount; i++)
                        {
                            SetState(i, _initialStates[i], false);
                        }
                        break;
                    case PuzzleEventType.Solved:
                        for (var i = 0; i < stateCount; i++)
                        {
                            SetState(i, solution[i], false);
                        }
                        break;
                    case PuzzleEventType.WrongInput:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(type), type, null);
                }
            };
            
            base.Start();
        }

        protected virtual void SetState(int index, int value, bool checkSolution)
        {
            if (index >= 0 && index < stateCount)
            {
                states[index] = value;
                correctStates = 0;
                for (var i = 0; i < stateCount; i++)
                {
                    if (states[i] == solution[i])
                    {
                        correctStates++;
                    }
                }

                if (checkSolution && correctStates == stateCount && correctStates > 0)
                {
                    Solve();
                }
            }
        }

        [UsedImplicitly] // used for field validation
        private bool CorrectStateCount(int count) =>
            states != null && count == states.Count &&
            solution != null && count == solution.Count;
    }
}