using NUnit.Framework;
using UnityEditor;
using LLM.Editor.Commands;
using LLM.Editor.Data;
using LLM.Editor.Helper;
using System.Collections;
using UnityEngine.TestTools;
using UnityEngine;

namespace LLM.Editor.Tests.Unit
{
    [TestFixture]
    public class SetEditorStateCommandTests
    {
        private CommandContext _context;

        [SetUp]
        public void SetUp()
        {
            _context = new CommandContext();
            // Ensure we are in Edit Mode before each test
            EditorApplication.ExitPlaymode();
        }

        [UnityTest]
        public IEnumerator Execute_WithPlayState_EntersPlayMode()
        {
            // Arrange
            var parameters = new SetEditorStateParams { state = EditorTargetState.Play };
            var command = new SetEditorStateCommand(parameters.ToJson());

            // Act
            var outcome = command.Execute(_context);
            
            // Assert
            Assert.AreEqual(CommandOutcome.Success, outcome);
            
            // Wait until the editor has entered play mode
            float timeout = 0f;
            while (!EditorApplication.isPlaying && timeout < 5f)
            {
                yield return null;
                timeout += Time.deltaTime;
            }
            
            Assert.IsTrue(EditorApplication.isPlaying, "Editor did not enter play mode within the timeout.");
        }

        [UnityTest]
        public IEnumerator Execute_WithStopState_ExitsPlayMode()
        {
            // Arrange
            // First, enter play mode to test exiting it
            EditorApplication.EnterPlaymode();
            float enterTimeout = 0f;
            while (!EditorApplication.isPlaying && enterTimeout < 5f)
            {
                yield return null;
                enterTimeout += Time.deltaTime;
            }
            Assert.IsTrue(EditorApplication.isPlaying, "Pre-condition failed: Editor did not enter play mode.");

            var parameters = new SetEditorStateParams { state = EditorTargetState.Stop };
            var command = new SetEditorStateCommand(parameters.ToJson());

            // Act
            var outcome = command.Execute(_context);

            // Assert
            Assert.AreEqual(CommandOutcome.Success, outcome);

            // Wait until the editor has exited play mode
            float exitTimeout = 0f;
            while (EditorApplication.isPlaying && exitTimeout < 5f)
            {
                yield return null;
                exitTimeout += Time.deltaTime;
            }

            Assert.IsFalse(EditorApplication.isPlaying, "Editor did not exit play mode within the timeout.");
        }
        
        [UnityTest]
        public IEnumerator Execute_WithPauseState_PausesPlayMode()
        {
            // Arrange
            EditorApplication.EnterPlaymode();
            float enterTimeout = 0f;
            while (!EditorApplication.isPlaying && enterTimeout < 5f)
            {
                yield return null;
                enterTimeout += Time.deltaTime;
            }
            Assert.IsTrue(EditorApplication.isPlaying, "Pre-condition failed: Editor did not enter play mode.");
            
            var parameters = new SetEditorStateParams { state = EditorTargetState.Pause };
            var command = new SetEditorStateCommand(parameters.ToJson());

            // Act
            var outcome = command.Execute(_context);
            
            // Assert
            Assert.AreEqual(CommandOutcome.Success, outcome);
            
            // The isPaused state should be set immediately, but we yield once to be safe
            yield return null; 
            
            Assert.IsTrue(EditorApplication.isPaused);
        }
    }
}