// Copyright (c) 2025 TerraByte Inc.
//
// This script acts as the Controller for the ArbitratorWindow. It manages all
// state and business logic, separating it from the UI rendering code in the window.

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

namespace Terra.Arbitrator.GUI
{
    public class ArbitratorController
    {
        // --- Private State ---
        private List<GitChange> _changes = new();
        private string _infoMessage;
        private string _errorMessage;
        private bool _isLoading;
        private string _loadingMessage = "";
        
        // --- Public Properties for the View ---
        public IReadOnlyList<GitChange> Changes => _changes;
        public string InfoMessage => _infoMessage;
        public string ErrorMessage => _errorMessage;
        public bool IsLoading => _isLoading;
        public string LoadingMessage => _loadingMessage;

        // --- Communication with the View ---
        private readonly Action _requestRepaint;
        private readonly Func<string, string, bool> _displayDialog;

        /// <summary>
        /// Initializes the controller.
        /// </summary>
        /// <param name="requestRepaint">A callback to the window's Repaint() method.</param>
        /// <param name="displayDialog">A callback to EditorUtility.DisplayDialog for user confirmations.</param>
        public ArbitratorController(Action requestRepaint, Func<string, string, bool> displayDialog)
        {
            _requestRepaint = requestRepaint;
            _displayDialog = displayDialog;
        }

        // --- Public Methods (called by the View) ---

        public void OnEnable()
        {
            // This logic is moved from ArbitratorWindow.OnEnable
            if (SessionState.GetBool(BetterGitStatePersistence.ResetQueueKey, false))
            {
                SessionState.EraseString(BetterGitStatePersistence.ResetQueueKey);
                _infoMessage = "Multi-file reset complete. Pulling again to confirm...";
                Pull();
            }
            else
            {
                Refresh();
            }
        }

        public void Refresh()
        {
            StartOperation("Refreshing status...");
            
            UnstageStep()
                .Then(CompareStep)
                .Then(FinalizeRefresh)
                .Catch(HandleOperationError)
                .Finally(FinishOperation);
        }
        
        public void Pull()
        {
            if (_isLoading) return;
            StartOperation("Analyzing for conflicts...");

            GitService.AnalyzePullConflicts()
                .Then(analysisResult =>
                {
                    if (analysisResult.HasConflicts)
                    {
                        // This part is synchronous UI, so it stays.
                        FinishOperation(); // Stop loading while modal is open
                        ConflictResolutionWindow.ShowWindow(analysisResult.ConflictingFiles, filesToActOn =>
                        {
                            if (filesToActOn == null) return; 
                            if (filesToActOn.Any()) ResetMultipleFiles(filesToActOn);
                            else ForcePull(); 
                        });
                    }
                    else
                    {
                        StartOperation("No conflicts found. Pulling changes...");
                        GitService.PerformSafePull()
                            .Then(successMessage =>
                            {
                                _infoMessage = successMessage;
                                // Refresh will handle its own start/finish states
                                Refresh();
                            })
                            .Catch(ex => {
                                // If the pull fails, we need to handle the error and finish
                                HandleOperationError(ex);
                                FinishOperation();
                            });
                    }
                })
                .Catch(ex => {
                    HandleOperationError(ex);
                    FinishOperation();
                });
        }

        public void CommitAndPush(string commitMessage)
        {   
            var selectedFiles = _changes.Where(c => c.IsSelectedForCommit).ToList();
            
            var username = BetterGitSettings.Username;
            var email = BetterGitSettings.Email;
            
            StartOperation("Staging, committing, and pushing files...");

            GitService.CommitAndPush(selectedFiles, commitMessage, username, email)
                .Then(successMessage => {
                    _infoMessage = successMessage;
                    Refresh();
                })
                .Catch(ex => {
                    HandleOperationError(ex);
                    FinishOperation();
                });
        }

        public void ResetFile(GitChange change)
        {
            if (_isLoading) return;
            var userConfirmed = _displayDialog("Confirm Reset", $"Are you sure you want to revert all local changes to '{change.FilePath}'? This action cannot be undone.");
            if (!userConfirmed) return;
            
            StartOperation($"Resetting {change.FilePath}...");
            
            GitService.ResetFileChanges(change)
                .Then(successMessage => {
                    _infoMessage = successMessage;
                    Refresh();
                })
                .Catch(ex => {
                    HandleOperationError(ex);
                    FinishOperation();
                });
        }
        
        public void DiffFile(GitChange change)
        {
            if (_isLoading) return;
            StartOperation($"Launching diff for {change.FilePath}...");
            
            GitService.LaunchExternalDiff(change)
                .Catch(HandleOperationError)
                .Finally(Refresh);
        }
        
        public void ResolveConflict(GitChange change)
        {
            if (_isLoading) return;
            StartOperation($"Opening merge tool for {change.FilePath}...");
            
            GitService.LaunchMergeTool(change)
                .Then(successMessage => { _infoMessage = successMessage; })
                .Catch(HandleOperationError)
                .Finally(Refresh);
        }

        public void ResetAll()
        {
            var userConfirmed = _displayDialog(
                "Confirm Reset All",
                "Are you sure you want to discard ALL local changes (modified, added, and untracked files)?\n\nThis action cannot be undone.");

            if (!userConfirmed) return;

            StartOperation("Discarding all local changes...");

            GitService.ResetAllChanges()
                .Then(successMessage =>
                {
                    _infoMessage = successMessage;
                })
                .Catch(HandleOperationError)
                .Finally(Refresh);
        }

        public void SetAllSelection(bool selected)
        {
            if (_changes == null) return;
            foreach (var change in _changes)
            {
                if (change.Status != LibGit2Sharp.ChangeKind.Conflicted)
                {
                   change.IsSelectedForCommit = selected;
                }
            }
        }

        // --- Private Methods ---
        
        private static IPromise<bool> UnstageStep()
        {
            return GitService.UnstageAllFilesIfSafe();
        }

        private IPromise<List<GitChange>> CompareStep(bool wasUnstaged)
        {
            if (wasUnstaged)
            {
                _infoMessage = "Found and unstaged files for review.";
            }
            return GitService.CompareLocalToRemote();
        }

        private void FinalizeRefresh(List<GitChange> changes)
        {
            _changes = changes;
            if (string.IsNullOrEmpty(_infoMessage) && (_changes == null || _changes.Count == 0))
            {
                _infoMessage = "You are up-to-date! No local changes detected.";
            }
        }
        
        // --- Shared Helper Methods ---

        private void StartOperation(string loadingMessage)
        {
            _isLoading = true;
            _loadingMessage = loadingMessage;
            ClearMessages();
            _changes = null;
            _requestRepaint?.Invoke();
        }

        private void HandleOperationError(Exception ex)
        {
            _errorMessage = $"Operation Failed: {ex.Message}";
        }
        
        private void FinishOperation()
        {
            _isLoading = false;
            _requestRepaint?.Invoke();
        }
        
        private void ClearMessages()
        {
            _errorMessage = null;
            _infoMessage = null;
        }

        private void ResetMultipleFiles(List<string> filePaths)
        {
            _infoMessage = $"Starting reset for {filePaths.Count} file(s)... This may trigger script compilation.";
            _requestRepaint?.Invoke();
            SessionState.SetString("BetterGit.ResetQueue", string.Join(";", filePaths));
            EditorApplication.delayCall += BetterGitStatePersistence.ContinueInterruptedReset;
        }
        
        private void ForcePull()
        {
            StartOperation("Attempting to pull and create conflicts...");
            
            EditorApplication.LockReloadAssemblies();
            GitService.ForcePull()
                .Then(_ =>
                {
                    _infoMessage = "Pull resulted in conflicts. Please resolve them below.";
                })
                .Catch(HandleOperationError)
                .Finally(() =>
                {
                    EditorApplication.UnlockReloadAssemblies();
                    AssetDatabase.Refresh();
                    Refresh();
                });
        }
    }
}