using System;
using AssetBank.Editor.SchemaConverter.Data.Output;
using AssetBank.Editor.Tools;
using AssetBank.Settings;
using System.Diagnostics;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace AssetBank.Editor.SchemaConverter.Tests
{
    [TestFixture]
    public class SchemaConverterTests
    {
        private const string SampleScenePath = "Assets/Scenes/SampleScene.unity";
        private string _tempDirectory;
        private string _rawJsonPath;
        private string _trimmedJsonPath;
        private string _hierarchyJsonPath;
        private string _hierarchyAndPrefabsJsonPath;
        private string _fullGraphJsonPath;

        [SetUp]
        public void SetUp()
        {
            _tempDirectory = Path.Combine("Library", "SchemaConverterTestTemp");
            Directory.CreateDirectory(_tempDirectory);
            _rawJsonPath = Path.Combine(_tempDirectory, "raw_scene.json");
            _trimmedJsonPath = Path.Combine(_tempDirectory, "trimmed_scene.json");
            _hierarchyJsonPath = Path.Combine(_tempDirectory, "hierarchy_only.json");
            _hierarchyAndPrefabsJsonPath = Path.Combine(_tempDirectory, "hierarchy_and_prefabs.json");
            _fullGraphJsonPath = Path.Combine(_tempDirectory, "full_graph.json");
        }

        [TearDown]
        public void TearDown()
        {
            if (Directory.Exists(_tempDirectory))
            {
                Directory.Delete(_tempDirectory, true);
            }
        }

        [Test, Order(1)]
        public void CanConvertSceneToRawJson()
        {
            var pythonExe = FindPythonExecutable();
            var conversionScript = GetConversionScriptPath();
            
            Assert.IsNotNull(pythonExe, "Python executable not found.");
            Assert.IsNotNull(conversionScript, "Conversion script 'convert_scene.py' not found.");

            var success = ExecutePythonConversion(SampleScenePath, _rawJsonPath, pythonExe, conversionScript);
            
            Assert.IsTrue(success, "Python conversion script failed to execute.");
            Assert.IsTrue(File.Exists(_rawJsonPath), "Raw JSON file was not created.");
            Assert.Greater(new FileInfo(_rawJsonPath).Length, 0, "Raw JSON file is empty.");
            
            Debug.Log($"Successfully created raw JSON at: {_rawJsonPath}");
        }

        [Test, Order(2)]
        public void CanTrimRawJson()
        {
            // Ensure the first step has run and created the file.
            if (!File.Exists(_rawJsonPath))
            {
                CanConvertSceneToRawJson();
            }
            Assert.IsTrue(File.Exists(_rawJsonPath), "Prerequisite step 'CanConvertSceneToRawJson' failed to produce an output file.");

            var rawJson = File.ReadAllText(_rawJsonPath);
            var settings = ProjectExporterSettings.GetOrCreateSettings();
            
            var trimmedJson = JsonDataTrimmer.Process(rawJson, settings);
            
            File.WriteAllText(_trimmedJsonPath, trimmedJson);
            
            Assert.IsTrue(File.Exists(_trimmedJsonPath), "Trimmed JSON file was not created.");
            Assert.Greater(new FileInfo(_trimmedJsonPath).Length, 0, "Trimmed JSON file is empty.");
            
            Debug.Log($"Successfully created trimmed JSON at: {_trimmedJsonPath}");
        }

        [Test, Order(3)]
        public void CanProcessHierarchyOnly()
        {
            // Ensure the prerequisite step has run.
            if (!File.Exists(_trimmedJsonPath))
            {
                CanTrimRawJson();
            }
            Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file.");
            
            var trimmedJson = File.ReadAllText(_trimmedJsonPath);
            
            // Run the conversion but only use the hierarchy part of the result.
            var sceneGraph = JsonSchemaConverter.ConvertToSceneGraph(trimmedJson);
            var hierarchyJson = JsonConvert.SerializeObject(sceneGraph.hierarchy, Formatting.Indented);
            
            File.WriteAllText(_hierarchyJsonPath, hierarchyJson);
            
            Assert.IsTrue(File.Exists(_hierarchyJsonPath), "Hierarchy-only JSON file was not created.");
            Assert.Greater(new FileInfo(_hierarchyJsonPath).Length, 2, "Hierarchy-only JSON file is empty (should be at least '[]').");
            
            Debug.Log($"Successfully created hierarchy-only JSON at: {_hierarchyJsonPath}");
        }

        [Test, Order(4)]
        public void CanProcessHierarchyAndPrefabs()
        {
            // Ensure the prerequisite step has run.
            if (!File.Exists(_trimmedJsonPath))
            {
                CanTrimRawJson();
            }
            Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file.");
            
            var trimmedJson = File.ReadAllText(_trimmedJsonPath);
            
            // Run the conversion and serialize the hierarchy and prefabs sections.
            var sceneGraph = JsonSchemaConverter.ConvertToSceneGraph(trimmedJson);
            var result = new JObject
            {
                ["hierarchy"] = JToken.FromObject(sceneGraph.hierarchy),
                ["prefabs"] = JToken.FromObject(sceneGraph.prefabs)
            };
            var outputJson = result.ToString(Formatting.Indented);
            
            File.WriteAllText(_hierarchyAndPrefabsJsonPath, outputJson);
            
            Assert.IsTrue(File.Exists(_hierarchyAndPrefabsJsonPath), "Hierarchy and prefabs JSON file was not created.");
            Assert.Greater(new FileInfo(_hierarchyAndPrefabsJsonPath).Length, 2, "Hierarchy and prefabs JSON file is empty.");
            
            Debug.Log($"Successfully created hierarchy and prefabs JSON at: {_hierarchyAndPrefabsJsonPath}");
        }

        [Test, Order(5)]
        public void CanProcessFullGraphSuccessfully()
        {
            // Ensure the prerequisite step has run.
            if (!File.Exists(_trimmedJsonPath))
            {
                CanTrimRawJson();
            }
            Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file.");
            
            var trimmedJson = File.ReadAllText(_trimmedJsonPath);

            // This test now asserts that the conversion completes WITHOUT throwing an exception.
            string finalJson = null;
            Assert.DoesNotThrow(() =>
            {
                finalJson = JsonSchemaConverter.Convert(trimmedJson);
            }, "The conversion process threw an unexpected exception.");

            File.WriteAllText(_fullGraphJsonPath, finalJson);
            
            Assert.IsTrue(File.Exists(_fullGraphJsonPath), "Full graph JSON file was not created.");
            Assert.Greater(new FileInfo(_fullGraphJsonPath).Length, 2, "Full graph JSON file is empty.");
            
            // A good final check is to ensure no warnings were generated.
            var sceneGraph = JsonConvert.DeserializeObject<SceneGraph>(finalJson);
            Assert.IsNull(sceneGraph.metadata.warnings, "The conversion generated unexpected warnings.");
            
            Debug.Log($"Successfully created full graph JSON at: {_fullGraphJsonPath}");
        }
        
        #region Helper Methods

        private string FindPythonExecutable()
        {
            var projectRoot = Path.GetDirectoryName(Application.dataPath);
            var venvPath = Path.Combine(projectRoot, "venv", "bin", "python3");
            if (File.Exists(venvPath)) return venvPath;
            
            venvPath = Path.Combine(projectRoot, "venv", "bin", "python");
            if (File.Exists(venvPath)) return venvPath;

            // Fallback for system python
            return "python3";
        }

        private string GetConversionScriptPath()
        {
            var guids = AssetDatabase.FindAssets("convert_scene");
            if (guids.Length == 0) return null;
            
            var projectRoot = Path.GetDirectoryName(Application.dataPath);
            return Path.Combine(projectRoot, AssetDatabase.GUIDToAssetPath(guids[0]));
        }

        private bool ExecutePythonConversion(string assetPath, string jsonOutputPath, string pythonExe, string scriptPath)
        {
            var projectRoot = Path.GetDirectoryName(Application.dataPath);
            var absoluteAssetPath = Path.Combine(projectRoot, assetPath);

            var arguments = $"\"{scriptPath}\" \"{absoluteAssetPath}\" \"{jsonOutputPath}\"";
            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = pythonExe,
                    Arguments = arguments,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                }
            };

            process.Start();
            string error = process.StandardError.ReadToEnd();
            process.WaitForExit();

            if (process.ExitCode == 0) return true;

            Debug.LogError($"Failed to convert {assetPath}. Error: {error}");
            return false;
        }
        
        #endregion
    }
}
