using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEditor;
using System.Collections;
using System.IO;
using MCPForUnity.Editor.Tools;
using Newtonsoft.Json.Linq;

namespace MCPForUnityTests.Editor.Tools
{
    /// <summary>
    /// Tests for domain reload resilience - ensuring MCP requests succeed even during Unity domain reloads.
    ///
    /// These tests trigger script compilation which can cause test timing issues when Unity is
    /// backgrounded, but the MCP workflow itself is unaffected - socket messages provide external
    /// stimulus that keeps Unity responsive.
    ///
    /// Note: Focus nudge improvements (P2-9) should help with background test reliability.
    /// </summary>
    [Category("domain_reload")]
    [Explicit("Domain reload stress tests; run manually when needed.")]
    public class DomainReloadResilienceTests
    {
        private const string TempDir = "Assets/Temp/DomainReloadTests";

        [SetUp]
        public void Setup()
        {
            // Ensure temp directory exists
            if (!AssetDatabase.IsValidFolder(TempDir))
            {
                Directory.CreateDirectory(TempDir);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            }
        }

        [TearDown]
        public void TearDown()
        {
            // Clean up temp directory - this deletes any scripts we created
            if (AssetDatabase.IsValidFolder(TempDir))
            {
                AssetDatabase.DeleteAsset(TempDir);
            }

            // Remove parent temp folder if nothing else is inside
            if (AssetDatabase.IsValidFolder("Assets/Temp"))
            {
                var remainingDirs = Directory.GetDirectories("Assets/Temp");
                var remainingFiles = Directory.GetFiles("Assets/Temp");
                if (remainingDirs.Length == 0 && remainingFiles.Length == 0)
                {
                    AssetDatabase.DeleteAsset("Assets/Temp");
                }
            }

            // CRITICAL: Force a synchronous refresh and wait for any pending compilation to finish.
            // This prevents leaving compilation running that could stall subsequent tests.
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
        }

        /// <summary>
        /// This test simulates the stress test scenario:
        /// 1. Create a script (triggers domain reload)
        /// 2. Make multiple rapid read_console calls
        /// 3. Verify all calls succeed (no "No Unity plugins are currently connected" errors)
        /// 
        /// Note: This test uses UnityTest coroutine to handle the async nature of domain reloads.
        /// </summary>
        [UnityTest]
        public IEnumerator StressTest_CreateScriptAndReadConsoleMultipleTimes()
        {
            // Step 1: Create a script to trigger domain reload
            var scriptPath = Path.Combine(TempDir, "StressTestScript.cs").Replace("\\", "/");
            var scriptContent = @"using UnityEngine;

public class StressTestScript : MonoBehaviour
{
    void Start() { }
}";
            
            // Write script file
            File.WriteAllText(scriptPath, scriptContent);
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            
            Debug.Log("[DomainReloadTest] Script created, domain reload triggered");
            
            // Wait a frame for the domain reload to start
            yield return null;
            
            // Step 2: Make multiple rapid read_console calls
            // These should succeed even during the reload window
            int successCount = 0;
            int totalCalls = 5;
            
            for (int i = 0; i < totalCalls; i++)
            {
                var request = new JObject
                {
                    ["action"] = "get",
                    ["types"] = new JArray { "all" },
                    ["count"] = 50,
                    ["format"] = "plain",
                    ["includeStacktrace"] = false
                };
                
                var result = ExecuteReadConsole(request);
                
                // Check if the call succeeded
                if (result != null && result["success"]?.Value<bool>() == true)
                {
                    successCount++;
                    Debug.Log($"[DomainReloadTest] read_console call {i+1}/{totalCalls} succeeded");
                }
                else
                {
                    var error = result?["error"]?.ToString() ?? "Unknown error";
                    Debug.LogError($"[DomainReloadTest] read_console call {i+1}/{totalCalls} failed: {error}");
                }
                
                // Small delay between calls to simulate rapid-fire scenario
                yield return WaitFrames(6);
            }
            
            // Step 3: Verify all calls succeeded
            Debug.Log($"[DomainReloadTest] {successCount}/{totalCalls} read_console calls succeeded");
            Assert.AreEqual(totalCalls, successCount, 
                $"Expected all {totalCalls} read_console calls to succeed during domain reload, but only {successCount} succeeded");
        }

        /// <summary>
        /// Test that read_console works reliably after a domain reload completes.
        /// </summary>
        [Test]
        public void ReadConsole_AfterDomainReload_Succeeds()
        {
            // This test assumes domain reload has already completed
            // (Unity tests run after domain reload)
            
            var request = new JObject
            {
                ["action"] = "get",
                ["types"] = new JArray { "error", "warning", "log" },
                ["count"] = 10,
                ["format"] = "plain"
            };
            
            var result = ExecuteReadConsole(request);
            
            Assert.IsNotNull(result, "read_console should return a result");
            Assert.IsTrue(result["success"]?.Value<bool>() ?? false, 
                $"read_console should succeed: {result["error"]}");
        }

        /// <summary>
        /// Test creating a script and immediately querying console logs.
        /// This simulates a common AI workflow pattern.
        /// </summary>
        [UnityTest]
        public IEnumerator CreateScript_ThenQueryConsole_Succeeds()
        {
            // Create a simple script
            var scriptPath = Path.Combine(TempDir, "TestScript1.cs").Replace("\\", "/");
            var scriptContent = @"using UnityEngine;

public class TestScript1 : MonoBehaviour
{
    void Start()
    {
        Debug.Log(""TestScript1 initialized"");
    }
}";
            
            File.WriteAllText(scriptPath, scriptContent);
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            
            Debug.Log("[DomainReloadTest] Script created");
            
            // Wait a frame
            yield return null;
            
            // Immediately try to read console
            var request = new JObject
            {
                ["action"] = "get",
                ["types"] = new JArray { "all" },
                ["count"] = 50,
                ["format"] = "plain"
            };
            
            var result = ExecuteReadConsole(request);
            
            // Should succeed even if domain reload is happening
            Assert.IsNotNull(result, "read_console should return a result");
            Assert.IsTrue(result["success"]?.Value<bool>() ?? false, 
                $"read_console should succeed even during/after script creation: {result["error"]}");
        }

        /// <summary>
        /// Test rapid script creation followed by console reads.
        /// This is an even more aggressive stress test.
        /// </summary>
        [UnityTest]
        public IEnumerator RapidScriptCreation_WithConsoleReads_AllSucceed()
        {
            int scriptCount = 3;
            int consoleReadsPerScript = 2;
            int successCount = 0;
            int totalExpectedReads = scriptCount * consoleReadsPerScript;
            
            for (int i = 0; i < scriptCount; i++)
            {
                // Create script
                var scriptPath = Path.Combine(TempDir, $"RapidScript{i}.cs").Replace("\\", "/");
                var scriptContent = $@"using UnityEngine;

public class RapidScript{i} : MonoBehaviour
{{
    void Start() {{ Debug.Log(""RapidScript{i}""); }}
}}";
                
                File.WriteAllText(scriptPath, scriptContent);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                
                Debug.Log($"[DomainReloadTest] Created script {i+1}/{scriptCount}");
                
                // Immediately try console reads
                for (int j = 0; j < consoleReadsPerScript; j++)
                {
                    var request = new JObject
                    {
                        ["action"] = "get",
                        ["types"] = new JArray { "all" },
                        ["count"] = 20,
                        ["format"] = "plain"
                    };
                    
                    var result = ExecuteReadConsole(request);
                    
                    if (result != null && result["success"]?.Value<bool>() == true)
                    {
                        successCount++;
                    }
                    else
                    {
                        var error = result?["error"]?.ToString() ?? "Unknown error";
                        Debug.LogError($"[DomainReloadTest] Console read failed: {error}");
                    }
                    
                    yield return WaitFrames(3);
                }
                
                // Brief wait between script creations
                yield return WaitFrames(12);
            }
            
            Debug.Log($"[DomainReloadTest] {successCount}/{totalExpectedReads} console reads succeeded");
            
            // We expect at least 80% success rate (some may fail due to timing, but resilience should help most)
            int minExpectedSuccess = (int)(totalExpectedReads * 0.8f);
            Assert.GreaterOrEqual(successCount, minExpectedSuccess, 
                $"Expected at least {minExpectedSuccess} console reads to succeed, but only {successCount} succeeded");
        }

        private static JObject ExecuteReadConsole(JObject request)
        {
            var raw = ReadConsole.HandleCommand(request);
            if (raw == null)
            {
                return new JObject
                {
                    ["success"] = false,
                    ["error"] = "ReadConsole returned null"
                };
            }

            return JObject.FromObject(raw);
        }

        private static IEnumerator WaitFrames(int frameCount)
        {
            for (int i = 0; i < frameCount; i++)
            {
                yield return null;
            }
        }
    }
}
