using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Linq;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace IntelligentProjectAnalyzer.Editor.DependencyViewer
{
    /// <summary>
    /// An editor window that displays the dependencies and references for a selected asset.
    /// It uses UI Toolkit for its interface and a DependencyGraph for data processing.
    /// </summary>
    public class DependencyViewerWindow : EditorWindow
    {
        private const string UxmlPath = "DependencyViewerWindow.uxml";
        private const string USSPath = "DependencyViewerWindow.uss";

        private ObjectField _assetSelector;
        private ListView _dependenciesList;
        private ListView _referencesList;
        private Button _refreshButton;

        private Object _selectedAsset;
        private DependencyGraph _graph;

        [MenuItem("Tools/Analyzer/Dependency Viewer")]
        public static void ShowWindow()
        {
            var wnd = GetWindow<DependencyViewerWindow>();
            wnd.titleContent = new GUIContent("Dependency Viewer");
        }

        public void CreateGUI()
        {
            var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(FindAssetPath(UxmlPath));
            if (visualTree == null)
            {
                rootVisualElement.Add(new Label($"Could not find UXML file at path '{UxmlPath}'. Make sure it's in an 'Editor' folder."));
                return;
            }
            visualTree.CloneTree(rootVisualElement);

            var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(FindAssetPath(USSPath));
            if (styleSheet != null)
            {
                rootVisualElement.styleSheets.Add(styleSheet);
            }

            _graph = new DependencyGraph();

            _assetSelector = rootVisualElement.Q<ObjectField>("script-selector");
            _dependenciesList = rootVisualElement.Q<ListView>("dependencies-list");
            _referencesList = rootVisualElement.Q<ListView>("references-list");
            _refreshButton = rootVisualElement.Q<Button>("refresh-button");
            
            _assetSelector.objectType = typeof(Object);
            _assetSelector.RegisterValueChangedCallback(OnAssetSelected);
            _refreshButton.clicked += OnRefreshClicked;

            ConfigureListView(_dependenciesList);
            ConfigureListView(_referencesList);
        }

        private static void ConfigureListView(ListView listView)
        {
            listView.makeItem = () =>
            {
                var root = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center }};
                var objField = new ObjectField { objectType = typeof(Object), style = { flexGrow = 1 } };
                objField.SetEnabled(false); 
                var pingButton = new Button { text = "Ping", style = { flexShrink = 0, marginLeft = 5 }};
                
                pingButton.clicked += () =>
                {
                    if (pingButton.userData is Object objToPing)
                    {
                        EditorGUIUtility.PingObject(objToPing);
                    }
                };
                
                root.Add(objField);
                root.Add(pingButton);
                return root;
            };

            listView.bindItem = (element, index) =>
            {
                var objField = element.Q<ObjectField>();
                var pingButton = element.Q<Button>();
                var guid = listView.itemsSource[index] as string;
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var asset = AssetDatabase.LoadAssetAtPath<Object>(assetPath);

                if (objField != null) objField.value = asset;
                if (pingButton != null) pingButton.userData = asset;
            };
        }
        
        private void OnAssetSelected(ChangeEvent<Object> evt)
        {
            var newAsset = evt.newValue;

            if (newAsset == null)
            {
                _selectedAsset = null;
                PopulateLists();
                return;
            }

            // --- Validation Step 1: Check if it's a project asset ---
            var assetPath = AssetDatabase.GetAssetPath(newAsset);
            if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/"))
            {
                EditorUtility.DisplayDialog("Invalid Asset Location", "Please select an asset from the project's 'Assets' folder. Assets from packages or built-in resources are not supported.", "OK");
                _assetSelector.SetValueWithoutNotify(null); // Clear the selection
                _selectedAsset = null;
                PopulateLists();
                return;
            }
            
            // If all checks pass, update the selection and populate the lists.
            _selectedAsset = newAsset;
            PopulateLists();
        }

        private void OnRefreshClicked()
        {
            _graph.BuildGraph();
            PopulateLists();
            Debug.Log("Dependency data refreshed.");
        }

        private void PopulateLists()
        {
            string guid = null;
            if (_selectedAsset != null)
            {
                var assetPath = AssetDatabase.GetAssetPath(_selectedAsset);
                guid = AssetDatabase.AssetPathToGUID(assetPath);
            }

            if (string.IsNullOrEmpty(guid))
            {
                _dependenciesList.itemsSource = new List<string>();
                _referencesList.itemsSource = new List<string>();
            }
            else
            {
                _dependenciesList.itemsSource = _graph.GetDependencies(guid);
                _referencesList.itemsSource = _graph.GetReferences(guid);
            }
            
            _dependenciesList.Rebuild();
            _referencesList.Rebuild();
        }

        private static string FindAssetPath(string assetName)
        {
            var guids = AssetDatabase.FindAssets(Path.GetFileNameWithoutExtension(assetName));
            return guids.Select(AssetDatabase.GUIDToAssetPath).FirstOrDefault(path => Path.GetFileName(path).Equals(assetName, StringComparison.OrdinalIgnoreCase));
        }
    }
}