#nullable enable
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using TMPro;

//Could be a singleton
public class GameFlowManager : MonoBehaviour {
	[SerializeField] [Required]
	GameTimer gameTimer = null!;
	[SerializeField] [Required]
	MainMenuManager mainMenu = null!;
	[SerializeField] [Required]
	Canvas UICanvas = null!;

	public enum PauseLevel {
		NotPaused,
		PreventActions,
		NothingMoves,
		TimeStop,
	}

	[field: SerializeField]
	public PauseLevel pauseLevel { get; private set; } = PauseLevel.PreventActions;

	public bool CanDoAction => pauseLevel <= PauseLevel.NotPaused;

	public BaseState CurrentState { get; private set; } = null!;
	bool lastStartButton;
	bool lastAcceptButton;
	[field: SerializeField] float fadingTime = 3f;
	[field: SerializeField] float minFadingValue = 0.1f;
	float fadeStep = 0.02f;
	float fadingTimer;
	bool fadingIn = false;
	[field: SerializeField] TMP_Text startTxt;
	[field: SerializeField] TMP_Text endTxt;

	#region Unity Messages

	void Awake() {
		CurrentState = new StartMenuFlowState(this);
		if (startTxt != null) {
			startTxt.transform.parent.gameObject.SetActive(true);
		}
		if (endTxt != null) {
			endTxt.transform.parent.gameObject.SetActive(false);
		}
	}

	void Start() => CurrentState.EnterState();

	void Update() {
		if (CurrentState.UpdateState() is {} newState)
			SwitchState(newState);	
	}

	#endregion

	void SetPauseLevel(PauseLevel value) {
		pauseLevel = value;
		Time.timeScale = value >= PauseLevel.TimeStop ? 0f : 1f;
	}

	public void GameOver(){
		if (endTxt != null) {
			endTxt.transform.parent.gameObject.SetActive(true);
		}
		SwitchState(new DeadFlowState(this));
	}

	public void FadeStartTxt(){
		fadingTimer += fadeStep;
		if(fadingTimer >= fadingTime){
			if(fadingIn){
				fadingIn = false;
				fadingTimer = 0;
			}else{
				fadingIn = true;
				fadingTimer = 0;
			}
		}

		if(!fadingIn){
			startTxt.color = new Color(startTxt.color.r, startTxt.color.g, startTxt.color.b, (1+minFadingValue) - (fadingTimer/fadingTime));
		}else{
			startTxt.color = new Color(startTxt.color.r, startTxt.color.g, startTxt.color.b, (fadingTimer/fadingTime) + minFadingValue);
		}
	}

	/// <summary>
	/// For calling from main menu button
	/// </summary>
	public void ToStartFlowState() => SwitchState(new StartFlowState(this));

	#region Inputs

	public void OnStart(InputAction.CallbackContext ctx) {
		if (!ctx.WasPressedThisFrame(ref lastStartButton))
			return;

		switch (CurrentState) {
			case StartFlowState _:
				SwitchState(new GameplayFlowState(this));
				break;
			case GameplayFlowState _:
				SwitchState(new PauseMenuFlowState(this));
				break;
			case PauseMenuFlowState _:
				SwitchState(new GameplayFlowState(this));
				break;
		}
	}

	public void OnAccept(InputAction.CallbackContext ctx) {
		if (!ctx.WasPressedThisFrame(ref lastAcceptButton))
			return;

		if (CurrentState is DeadFlowState deadState)
			deadState.ReloadGame();
	}

	#endregion

	#region States

	void SwitchState(BaseState newState) {
		CurrentState.LeaveState();
		CurrentState = newState;
		newState.EnterState();
	}

	abstract class GameFlowState : BaseState {
		readonly protected GameFlowManager gameFlowManager;

		protected GameFlowState(GameFlowManager gameFlowManager) {
			this.gameFlowManager = gameFlowManager;
		}
	}

	class StartMenuFlowState : GameFlowState {
		public StartMenuFlowState(GameFlowManager gameFlowManager) : base(gameFlowManager) {}

		public override void EnterState() {
			base.EnterState();
			
			gameFlowManager.mainMenu.gameObject.SetActive(true);
			gameFlowManager.mainMenu.SetMenuMode(MainMenuManager.MenuMode.MainMenu);
			gameFlowManager.UICanvas.gameObject.SetActive(false);
		}

		public override void LeaveState() {
			base.LeaveState();
			
			gameFlowManager.mainMenu.ResetMenuState();
			gameFlowManager.mainMenu.gameObject.SetActive(false);
			gameFlowManager.UICanvas.gameObject.SetActive(true);
		}
	}
	
	class StartFlowState : GameFlowState {
		public StartFlowState(GameFlowManager gameFlowManager) : base(gameFlowManager) {}

		public override void EnterState() {
			base.EnterState();

			gameFlowManager.startTxt.transform.parent.gameObject.SetActive(true);
			gameFlowManager.SetPauseLevel(PauseLevel.PreventActions);
		}

		public override BaseState? UpdateState(){
			gameFlowManager.FadeStartTxt();
			return null;
		}

		public override void LeaveState() => gameFlowManager.startTxt.transform.parent.gameObject.SetActive(false);
	}

	class GameplayFlowState : GameFlowState {
		public GameplayFlowState(GameFlowManager gameFlowManager) : base(gameFlowManager) {}

		public override void EnterState() {
			base.EnterState();

			gameFlowManager.gameTimer.stopped = false;
			gameFlowManager.SetPauseLevel(PauseLevel.NotPaused);
		}

		public override void LeaveState() {
			base.LeaveState();

			gameFlowManager.gameTimer.stopped = true;
		}
	}

	class PauseMenuFlowState : GameFlowState {
		public PauseMenuFlowState(GameFlowManager gameFlowManager) : base(gameFlowManager) {}

		public override void EnterState() {
			base.EnterState();
			
			gameFlowManager.mainMenu.gameObject.SetActive(true);
			gameFlowManager.mainMenu.SetMenuMode(MainMenuManager.MenuMode.PauseMenu);
			gameFlowManager.SetPauseLevel(PauseLevel.TimeStop);
		}

		public override void LeaveState() {
			base.EnterState();
			
			gameFlowManager.mainMenu.ResetMenuState();
			gameFlowManager.mainMenu.gameObject.SetActive(false);
			gameFlowManager.SetPauseLevel(PauseLevel.NotPaused);
		}
	}

	class DeadFlowState : GameFlowState {
		public DeadFlowState(GameFlowManager gameFlowManager) : base(gameFlowManager) {}

		public override void EnterState() {
			base.EnterState();

			Debug.Log("You died!\nPress Accept to restart!");
			gameFlowManager.SetPauseLevel(PauseLevel.PreventActions);
		}

		public void ReloadGame() {
			Debug.Log("Reloading scene...");
			SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
		}
	}

	#endregion
}