using AssetBank.Settings;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Newtonsoft.Json.Linq;

namespace AssetBank.Editor.Tools
{
    /// <summary>
    /// Handles the core logic for scanning the project and managing the component stats.
    /// </summary>
    public class ComponentStatsController
    {
        private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath);
        
        public enum ScanSource
        {
            Scenes,
            Prefabs,
            Both
        }

        /// <summary>
        /// Scans the project for components and gathers statistics about them.
        /// </summary>
        /// <param name="scanSource">The type of assets to scan.</param>
        /// <returns>A dictionary mapping component names to their statistics.</returns>
        public Dictionary<string, ComponentStats> ScanProject(ScanSource scanSource)
        {
            var settings = ProjectExporterSettings.GetOrCreateSettings();
            var ignoredFolders = settings.FoldersToIgnore;
            var assetsToScan = new List<string>();

            if (scanSource == ScanSource.Scenes || scanSource == ScanSource.Both)
            {
                assetsToScan.AddRange(AssetDatabase.FindAssets("t:Scene", new[] { "Assets" }));
            }
            if (scanSource == ScanSource.Prefabs || scanSource == ScanSource.Both)
            {
                assetsToScan.AddRange(AssetDatabase.FindAssets("t:Prefab", new[] { "Assets" }));
            }

            var assetPaths = assetsToScan.Distinct().Select(AssetDatabase.GUIDToAssetPath).ToList();
            var filteredPaths = assetPaths.Where(path => !ignoredFolders.Any(folder => path.StartsWith(folder, StringComparison.OrdinalIgnoreCase))).ToList();

            var tempDir = Path.Combine("Library", "ComponentScanTemp");
            if (Directory.Exists(tempDir))
            {
                Directory.Delete(tempDir, true);
            }
            Directory.CreateDirectory(tempDir);

            var componentStats = new Dictionary<string, ComponentStats>();

            try
            {
                EditorUtility.DisplayProgressBar("Scanning Components", "Preparing to scan...", 0f);
                for (int i = 0; i < filteredPaths.Count; i++)
                {
                    var assetPath = filteredPaths[i];
                    var progress = (float)i / filteredPaths.Count;
                    EditorUtility.DisplayProgressBar("Scanning Components", $"Processing: {Path.GetFileName(assetPath)}", progress);

                    var jsonPath = Path.Combine(tempDir, $"{Path.GetFileNameWithoutExtension(assetPath)}_{i}.json");
                    if (ExecutePythonConversion(assetPath, jsonPath))
                    {
                        ParseComponentsFromJson(jsonPath, componentStats);
                    }
                }
            }
            finally
            {
                if (Directory.Exists(tempDir))
                {
                    Directory.Delete(tempDir, true);
                }
                EditorUtility.ClearProgressBar();
            }

            return componentStats;
        }
        
        private string FindPythonExecutable()
        {
            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;
            }
            return "python3";
        }

        private bool ExecutePythonConversion(string assetPath, string jsonOutputPath)
        {
            var pythonExecutable = FindPythonExecutable();
            var scriptGuid = AssetDatabase.FindAssets("convert_scene").FirstOrDefault();
            if (string.IsNullOrEmpty(scriptGuid))
            {
                UnityEngine.Debug.LogError("Conversion script 'convert_scene.py' not found.");
                return false;
            }
            var scriptPath = Path.Combine(ProjectRoot, AssetDatabase.GUIDToAssetPath(scriptGuid));
            var absoluteAssetPath = Path.Combine(ProjectRoot, assetPath);

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

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

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

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

        /// <summary>
        /// Parses a JSON file and updates the statistics for each component found.
        /// </summary>
        private void ParseComponentsFromJson(string jsonPath, Dictionary<string, ComponentStats> stats)
        {
            try
            {
                var jsonContent = File.ReadAllText(jsonPath);
                var jsonArray = JArray.Parse(jsonContent);

                foreach (var item in jsonArray)
                {
                    if (item["data"] is not JObject dataObject) continue;
                    
                    var componentProperty = dataObject.Properties().FirstOrDefault();
                    if (componentProperty == null) continue;

                    var componentName = componentProperty.Name;
                    if (!stats.ContainsKey(componentName))
                    {
                        stats[componentName] = new ComponentStats { Name = componentName };
                    }

                    var currentStats = stats[componentName];
                    currentStats.TotalOccurrences++;

                    // Pass the *value* of the component property to the counter
                    var propertyCount = CountProperties(componentProperty.Value);
                    
                    currentStats.TotalProperties += propertyCount;
                    if (propertyCount > currentStats.MaxProperties)
                    {
                        currentStats.MaxProperties = propertyCount;
                    }
                }
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError($"Failed to parse JSON file {jsonPath}. Error: {e.Message}");
            }
        }

        /// <summary>
        /// Recursively counts the number of properties in a JToken.
        /// </summary>
        private int CountProperties(JToken token)
        {
            int count = 0;
            if (token is JObject obj)
            {
                foreach (var prop in obj.Properties())
                {
                    count++; // Count the key itself
                    count += CountProperties(prop.Value); // Recursively count properties in the value
                }
            }
            else if (token is JArray arr)
            {
                foreach (var item in arr)
                {
                    count += CountProperties(item);
                }
            }
            return count;
        }

        public void UpdateAndSaveScanResults(Dictionary<string, ComponentStats> stats)
        {
            var settings = ComponentStatsSettings.GetOrCreateSettings();
            settings.ComponentStats.Clear();
            settings.ComponentStats.AddRange(stats.Values.OrderBy(s => s.Name));
            settings.LastScanTime = DateTime.Now.ToString("g"); // "g" for general short date/time

            EditorUtility.SetDirty(settings);
            AssetDatabase.SaveAssets();
        }

        public void SaveStats(Dictionary<string, ComponentStats> stats)
        {
            var settings = ComponentStatsSettings.GetOrCreateSettings();
            settings.ComponentStats.Clear();
            settings.ComponentStats.AddRange(stats.Values.OrderBy(s => s.Name));
            
            EditorUtility.SetDirty(settings);
            AssetDatabase.SaveAssets();

            EditorUtility.DisplayDialog("Success", $"Saved {settings.ComponentStats.Count} component stats.", "OK");
        }
    }
}
