// Copyright (c) 2025 TerraByte Inc.
//
// This script creates a custom Unity Editor window called "Arbitrator" to compare 
// local Git changes with the tracked remote branch using the LibGit2Sharp library.
//
// HOW TO USE:
// 1. Ensure you have manually installed the LibGit2Sharp v0.27.0 package.
// 2. Create an "Editor" folder in your Assets directory if you don't have one.
// 3. Save this script as "Arbitrator.cs" inside the "Editor" folder.
// 4. In Unity, open the window from the top menu: Terra > Arbitrator.
// 5. Click the "Compare with Cloud" button. Results will appear in the console.

using System.Linq;
using UnityEngine;
using UnityEditor;
using LibGit2Sharp;
using Terra.Arbitrator.Services;
using System.Collections.Generic;

namespace Terra.Arbitrator.GUI
{
    public class ArbitratorWindow : EditorWindow
    {
        private List<GitChange> _changes;
        private string _commitMessage = "";
        private Vector2 _scrollPosition;
        private string _infoMessage;
        private string _errorMessage;
        private bool _isLoading;
        private string _loadingMessage = "";
        private GUIStyle _evenRowStyle;
        private bool _stylesInitialized;

        [MenuItem("Terra/Changes")]
        public static void ShowWindow()
        {
            var window = GetWindow<ArbitratorWindow>();
            window.titleContent = new GUIContent("Changes", EditorGUIUtility.IconContent("d_UnityEditor.VersionControl").image);
        }
        
        /// <summary>
        /// Called when the window is enabled. This triggers an automatic refresh
        /// when the window is opened or after scripts are recompiled.
        /// </summary>
        private void OnEnable()
        {
            HandleCompare();
        }
        
        /// <summary>
        /// Initializes custom GUIStyles. We do this here to avoid creating new
        /// styles and textures on every OnGUI call, which is inefficient.
        /// </summary>
        private void InitializeStyles()
        {
            if (_stylesInitialized) return;

            _evenRowStyle = new GUIStyle();
            
            // Create a 1x1 texture with a subtle gray color
            var texture = new Texture2D(1, 1);
            // Use a slightly different color depending on the editor skin (light/dark)
            var color = EditorGUIUtility.isProSkin 
                ? new Color(0.3f, 0.3f, 0.3f, 0.3f) 
                : new Color(0.8f, 0.8f, 0.8f, 0.5f);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            
            _evenRowStyle.normal.background = texture;
            
            _stylesInitialized = true;
        }

        private void OnGUI()
        {
            InitializeStyles();
            
            // --- Top Toolbar ---
            DrawToolbar();
            
            EditorGUILayout.Space();

            // --- Message Display Area ---
            if (!string.IsNullOrEmpty(_errorMessage))
            {
                EditorGUILayout.HelpBox(_errorMessage, MessageType.Error);
            }
            else if (!string.IsNullOrEmpty(_infoMessage) && !_isLoading)
            {
                // Only show an info message if not loading, to prevent flicker
                EditorGUILayout.HelpBox(_infoMessage, MessageType.Info);
            }

            // --- Main Content ---
            else if (!_isLoading && _changes is { Count: > 0 })
            {
                DrawChangesList();
                DrawCommitSection();
            }
        }
        
        private void ClearMessages()
        {
            _errorMessage = null;
            _infoMessage = null;
        }
        
        /// <summary>
        /// Draws the top menu bar for actions like refreshing.
        /// </summary>
        private void DrawToolbar()
        {
            EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
            EditorGUI.BeginDisabledGroup(_isLoading);
            // The refresh button is now on the toolbar
            if (GUILayout.Button(new GUIContent("Refresh", EditorGUIUtility.IconContent("Refresh").image, "Fetches the latest status from the remote."), EditorStyles.toolbarButton, GUILayout.Width(80)))
            {
                HandleCompare();
            }
            
            if (_changes != null && _changes.Count > 0)
            {
                if (GUILayout.Button("Select All", EditorStyles.toolbarButton, GUILayout.Width(80)))
                {
                    SetAllSelection(true);
                }
                if (GUILayout.Button("Deselect All", EditorStyles.toolbarButton, GUILayout.Width(80)))
                {
                    SetAllSelection(false);
                }
            }
            
            EditorGUI.EndDisabledGroup();
            
            // This pushes everything that comes after it to the right.
            GUILayout.FlexibleSpace();

            // The loading message will appear on the right side of the toolbar.
            if (_isLoading)
            {
                GUILayout.Label(_loadingMessage);
            }
            
            // Future: Add a dropdown menu for filters or settings here.
            // If (GUILayout.Button("Filters", EditorStyles.toolbarDropDown)) { ... }

            EditorGUILayout.EndHorizontal();
        }
        
        private void SetAllSelection(bool selected)
        {
            if (_changes == null) return;
            foreach (var change in _changes)
            {
                change.IsSelectedForCommit = selected;
            }
        }

        private void HandleCompare()
        {
            _isLoading = true;
            _loadingMessage = "Comparing with remote repository...";
            ClearMessages();
            _changes = null;

            GitService.CompareLocalToRemote()
                .Then(result => {
                    _changes = result;
                    if (_changes.Count == 0)
                    {
                        _infoMessage = "You are up-to-date! No local changes detected.";
                    }
                })
                .Catch(ex => {
                    _errorMessage = $"Comparison Failed: {ex.Message}";
                })
                .Finally(() => {
                    _isLoading = false;
                    Repaint(); // Redraw the window with the new state
                });
        }

        private void HandleCommitAndPush()
        {
            _isLoading = true;
            _loadingMessage = "Staging, committing, and pushing files...";
            ClearMessages();

            var selectedFiles = _changes.Where(c => c.IsSelectedForCommit).ToList();

            GitService.CommitAndPush(selectedFiles, _commitMessage)
                .Then(successMessage => {
                    _infoMessage = successMessage;
                    _commitMessage = ""; // Clear message on success
                    _changes = null; // Clear the list, forcing a refresh
                    HandleCompare(); // Automatically refresh to confirm
                })
                .Catch(ex => {
                    _errorMessage = $"Push Failed: {ex.Message}";
                })
                .Finally(() => {
                    _isLoading = false;
                    // Repaint is handled by the chained HandleCompare call on success
                    if (!string.IsNullOrEmpty(_errorMessage)) Repaint();
                });
        }
        
        private void HandleResetFile(GitChange change)
        {
            if (_isLoading) return;
            _isLoading = true;
            _loadingMessage = $"Generating diff for {change.FilePath}...";
            ClearMessages();
            Repaint();

            // Step 1: Get the diff content for the file.
            GitService.GetFileDiff(change)
                .Then(diffContent =>
                {
                    // This callback runs on the main thread when the diff is ready.
                    // Step 2: Show the modal diff window.
                    DiffWindow.ShowWindow(change.FilePath, diffContent, wasConfirmed =>
                    {
                        // This callback runs after the diff window is closed.
                        if (!wasConfirmed)
                        {
                            _isLoading = false; // User cancelled.
                            Repaint();
                            return;
                        }

                        // Step 3: User confirmed. Proceed to reset the file.
                        _loadingMessage = $"Resetting {change.FilePath}...";
                        Repaint();

                        GitService.ResetFileChanges(change)
                            .Then(successMessage => {
                                _infoMessage = successMessage;
                                HandleCompare(); // Refresh the main list.
                            })
                            .Catch(ex => {
                                _errorMessage = $"Reset Failed: {ex.Message}";
                                _isLoading = false;
                                Repaint();
                            });
                    });
                })
                .Catch(ex =>
                {
                    // This runs if getting the diff itself failed.
                    _errorMessage = $"Could not generate diff: {ex.Message}";
                    _isLoading = false;
                    Repaint();
                })
                .Finally(HandleCompare);
        }

        /// <summary>
        /// Draws the multi-column list of changed files.
        /// </summary>
        private void DrawChangesList()
        {
            // --- Draw Header ---
            EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
            EditorGUILayout.LabelField("Commit", GUILayout.Width(45));
            EditorGUILayout.LabelField("Status", GUILayout.Width(50));
            EditorGUILayout.LabelField("File Path");
            EditorGUILayout.LabelField("Actions", GUILayout.Width(55));
            EditorGUILayout.EndHorizontal();

            // --- Draw Scrollable List ---
            _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.ExpandHeight(true));
            for (var i = 0; i < _changes.Count; i++)
            {
                var change = _changes[i];
                // Use the evenRowStyle for every second row (i % 2 == 0), otherwise use no style.
                var rowStyle = i % 2 == 0 ? _evenRowStyle : GUIStyle.none;
                
                EditorGUILayout.BeginHorizontal(rowStyle);

                change.IsSelectedForCommit = EditorGUILayout.Toggle(change.IsSelectedForCommit, GUILayout.Width(45));
                
                string status;
                Color statusColor;
                string filePathDisplay;

                switch (change.Status)
                {
                    case ChangeKind.Added: 
                        status = "[+]"; statusColor = Color.green; filePathDisplay = change.FilePath; break;
                    case ChangeKind.Deleted: 
                        status = "[-]"; statusColor = Color.red; filePathDisplay = change.FilePath; break;
                    case ChangeKind.Modified: 
                        status = "[M]"; statusColor = new Color(1.0f, 0.6f, 0.0f); filePathDisplay = change.FilePath; break;
                    case ChangeKind.Renamed: 
                        status = "[R]"; statusColor = new Color(0.6f, 0.6f, 1.0f);
                        filePathDisplay = $"{change.OldFilePath} -> {change.FilePath}"; break;
                    case ChangeKind.Unmodified:
                    case ChangeKind.Copied:
                    case ChangeKind.Ignored:
                    case ChangeKind.Untracked:
                    case ChangeKind.TypeChanged:
                    case ChangeKind.Unreadable:
                    case ChangeKind.Conflicted:
                    default: 
                        status = "[?]"; statusColor = Color.white; filePathDisplay = change.FilePath; break;
                }
                
                var originalColor = UnityEngine.GUI.color;
                UnityEngine.GUI.color = statusColor;
                EditorGUILayout.LabelField(new GUIContent(status, change.Status.ToString()), GUILayout.Width(50));
                UnityEngine.GUI.color = originalColor;

                EditorGUILayout.LabelField(new GUIContent(filePathDisplay, filePathDisplay));

                EditorGUI.BeginDisabledGroup(_isLoading);
                if (GUILayout.Button(new GUIContent("Reset", "Revert changes for this file"), GUILayout.Width(55)))
                {
                    EditorApplication.delayCall += () => HandleResetFile(change);
                }
                EditorGUI.EndDisabledGroup();
                
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.EndScrollView();
        }
        
        private void DrawCommitSection()
        {
            EditorGUILayout.Space(10);
            EditorGUILayout.LabelField("Commit & Push", EditorStyles.boldLabel);
            _commitMessage = EditorGUILayout.TextArea(_commitMessage, GUILayout.Height(60), GUILayout.ExpandWidth(true));

            var isPushDisabled = string.IsNullOrWhiteSpace(_commitMessage) || !_changes.Any(c => c.IsSelectedForCommit);

            EditorGUI.BeginDisabledGroup(isPushDisabled);
            if (GUILayout.Button("Commit & Push Selected Files", GUILayout.Height(40)))
            {
                Debug.Log("Commit & Push Selected Files");
                HandleCommitAndPush();
            }
            EditorGUI.EndDisabledGroup();
            
            if (isPushDisabled)
            {
                EditorGUILayout.HelpBox("Please enter a commit message and select at least one file.", MessageType.Warning);
            }
        }
    }
}
