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

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

        [SetUp]
        public void SetUp()
        {
            _testObject = new GameObject("ObjectToDelete");
            _context = new CommandContext();
        }

        [TearDown]
        public void TearDown()
        {
            if (_testObject != null)
            {
                Object.DestroyImmediate(_testObject);
            }
        }

        [Test]
        public void Execute_WithValidTarget_DeletesGameObject()
        {
            // Arrange
            var parameters = new DeleteGameObjectParams { targetIdentifier = _testObject.GetInstanceID() };
            var command = new DeleteGameObjectCommand(parameters.ToJson());

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

            // Assert
            // The '==' operator is overloaded by Unity to check for "faked null" after destruction.
            Assert.IsTrue(_testObject == null, "The test object should be null after destruction.");
            Assert.AreEqual(CommandOutcome.Success, outcome);
            Assert.IsNull(_context.ErrorMessage);
        }

        [Test]
        public void Execute_WithInvalidTargetId_ReturnsError()
        {
            // Arrange
            var invalidId = -1;
            var parameters = new DeleteGameObjectParams { targetIdentifier = invalidId };
            var command = new DeleteGameObjectCommand(parameters.ToJson());

            LogAssert.Expect(LogType.Error, $"[DeleteGameObjectCommand] Could not find GameObject with ID: {invalidId}");

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

            // Assert
            Assert.AreEqual(CommandOutcome.Error, outcome);
            Assert.IsNotNull(_context.ErrorMessage);
        }

        [Test]
        public void Execute_WithZeroTargetId_ReturnsError()
        {
            // Arrange
            var parameters = new DeleteGameObjectParams { targetIdentifier = 0 };
            var command = new DeleteGameObjectCommand(parameters.ToJson());

            LogAssert.Expect(LogType.Error, "[DeleteGameObjectCommand] Invalid parameters: targetIdentifier is required.");

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

            // Assert
            Assert.AreEqual(CommandOutcome.Error, outcome);
            Assert.IsNotNull(_context.ErrorMessage);
        }

        [Test]
        public void Execute_OnAssetObject_FailsSafetyCheckAndReturnsError()
        {
            // Arrange
            // Use a real asset, like the settings file for the analyzer, to test the safety check.
            var guids = AssetDatabase.FindAssets("t:ScriptableObject AnalyzerSettings");
            Assume.That(guids, Is.Not.Empty, "Required 'AnalyzerSettings.asset' not found for test.");
            
            var path = AssetDatabase.GUIDToAssetPath(guids[0]);
            var assetObject = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
            var assetInstanceId = assetObject.GetInstanceID();

            var parameters = new DeleteGameObjectParams { targetIdentifier = assetInstanceId };
            var command = new DeleteGameObjectCommand(parameters.ToJson());

            LogAssert.Expect(LogType.Error, $"[DeleteGameObjectCommand] Safety check failed: Attempted to delete an asset, not a scene object. Path: {path}");

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

            // Assert
            Assert.AreEqual(CommandOutcome.Error, outcome);
            Assert.IsNotNull(_context.ErrorMessage);
        }
    }
}
